Added Video Export feature with according Options in the Options Menu
Fixed Linear Interpolation
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.Timer;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import eu.crushedpixel.replaymod.video.ReplayTimer;
|
||||
|
||||
public class MCTimerHandler {
|
||||
|
||||
private static Field mcTimer;
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
private static ReplayTimer rpt = new ReplayTimer(20);
|
||||
private static Timer timerBefore;
|
||||
|
||||
static {
|
||||
try {
|
||||
mcTimer = Minecraft.class.getDeclaredField(MCPNames.field("field_71428_T"));
|
||||
mcTimer.setAccessible(true);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setActiveTimer() {
|
||||
try {
|
||||
if(timerBefore != null) {
|
||||
mcTimer.set(mc, timerBefore);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPassiveTimer() {
|
||||
try {
|
||||
if(!(getTimer() instanceof ReplayTimer)) {
|
||||
timerBefore = getTimer();
|
||||
System.out.println("here");
|
||||
mcTimer.set(mc, rpt);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTicks() {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
return t.elapsedTicks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static float getPartialTicks() {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
return t.elapsedPartialTicks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static float getRenderTicks() {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
return t.renderPartialTicks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Timer getTimer() throws IllegalArgumentException, IllegalAccessException {
|
||||
return (Timer)mcTimer.get(mc);
|
||||
}
|
||||
|
||||
public static void advanceTicks(int ticks) {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
t.elapsedTicks += ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void advancePartialTicks(float ticks) {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
t.elapsedPartialTicks += ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void advanceRenderPartialTicks(float ticks) {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
t.renderPartialTicks += ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTimerSpeed(float speed) {
|
||||
try {
|
||||
Timer t = getTimer();
|
||||
t.timerSpeed = speed;
|
||||
if(timerBefore != null) {
|
||||
timerBefore.timerSpeed = speed;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setRenderPartialTicks(float ticks) {
|
||||
try {
|
||||
getTimer().renderPartialTicks = ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPartialTicks(float ticks) {
|
||||
try {
|
||||
getTimer().elapsedPartialTicks = ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTicks(int ticks) {
|
||||
try {
|
||||
getTimer().elapsedTicks = ticks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static float getTimerSpeed() {
|
||||
try {
|
||||
return getTimer().timerSpeed;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static void updateTimer(double d) {
|
||||
try{
|
||||
Timer t = getTimer();
|
||||
double d2 = d;
|
||||
d2 = MathHelper.clamp_double(d2, 0.0D, 1.0D);
|
||||
t.elapsedPartialTicks = (float)((double)t.elapsedPartialTicks + d2 * (double)t.timerSpeed * 20);
|
||||
t.elapsedTicks = (int)t.elapsedPartialTicks;
|
||||
t.elapsedPartialTicks -= (float)t.elapsedTicks;
|
||||
|
||||
if (t.elapsedTicks > 10)
|
||||
{
|
||||
t.elapsedTicks = 10;
|
||||
}
|
||||
|
||||
t.renderPartialTicks = t.elapsedPartialTicks;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.EnumPacketDirection;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.play.INetHandlerPlayClient;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
@@ -27,7 +28,6 @@ import eu.crushedpixel.replaymod.holders.KeyframeComparator;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
|
||||
import eu.crushedpixel.replaymod.holders.TimeKeyframe;
|
||||
import eu.crushedpixel.replaymod.registry.ReplayGuiRegistry;
|
||||
|
||||
public class ReplayHandler {
|
||||
|
||||
@@ -54,6 +54,15 @@ public class ReplayHandler {
|
||||
|
||||
private static Entity currentEntity = null;
|
||||
|
||||
public static void insertPacketInstantly(Packet p) {
|
||||
if(replaySender != null) {
|
||||
try {
|
||||
replaySender.channelRead(null, p);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void spectateEntity(Entity e) {
|
||||
currentEntity = e;
|
||||
mc.setRenderViewEntity(currentEntity);
|
||||
@@ -88,8 +97,8 @@ public class ReplayHandler {
|
||||
isReplaying = replaying;
|
||||
}
|
||||
|
||||
public static void startPath() {
|
||||
ReplayProcess.startReplayProcess();
|
||||
public static void startPath(boolean save) {
|
||||
if(!ReplayHandler.isReplaying()) ReplayProcess.startReplayProcess(save);
|
||||
}
|
||||
|
||||
public static void interruptReplay() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
@@ -12,6 +14,8 @@ import eu.crushedpixel.replaymod.holders.TimeKeyframe;
|
||||
import eu.crushedpixel.replaymod.interpolation.LinearPoint;
|
||||
import eu.crushedpixel.replaymod.interpolation.LinearTimestamp;
|
||||
import eu.crushedpixel.replaymod.interpolation.SplinePoint;
|
||||
import eu.crushedpixel.replaymod.video.ScreenCapture;
|
||||
import eu.crushedpixel.replaymod.video.VideoWriter;
|
||||
|
||||
public class ReplayProcess {
|
||||
|
||||
@@ -31,15 +35,23 @@ public class ReplayProcess {
|
||||
private static LinearTimestamp timeLinear = null;
|
||||
|
||||
private static double previousReplaySpeed = 0;
|
||||
|
||||
|
||||
private static boolean calculated = false;
|
||||
|
||||
public static void startReplayProcess() {
|
||||
private static boolean isVideoRecording = false;
|
||||
|
||||
public static boolean isVideoRecording() {
|
||||
return isVideoRecording;
|
||||
}
|
||||
|
||||
public static void startReplayProcess(boolean record) {
|
||||
isVideoRecording = record;
|
||||
lastPosition = null;
|
||||
motionSpline = null;
|
||||
timeLinear = null;
|
||||
calculated = false;
|
||||
|
||||
requestFinish = false;
|
||||
|
||||
ChatMessageRequests.initialize();
|
||||
if(ReplayHandler.getPosKeyframeCount() < 2) {
|
||||
ChatMessageRequests.addChatMessage("At least 2 position keyframes required!", ChatMessageType.WARNING);
|
||||
@@ -64,17 +76,69 @@ public class ReplayProcess {
|
||||
}
|
||||
|
||||
ChatMessageRequests.addChatMessage("Replay started!", ChatMessageType.INFORMATION);
|
||||
|
||||
if(isVideoRecording()) {
|
||||
ticks = 0;
|
||||
MCTimerHandler.setTimerSpeed(1);
|
||||
MCTimerHandler.setPassiveTimer();
|
||||
Thread t = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(ReplayHandler.isReplaying()) {
|
||||
if(!blocked) {
|
||||
mc.addScheduledTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ReplayProcess.tickReplay();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopReplayProcess(boolean finished) {
|
||||
if(!ReplayHandler.isReplaying()) return;
|
||||
if(finished) ChatMessageRequests.addChatMessage("Replay finished!", ChatMessageType.INFORMATION);
|
||||
else ChatMessageRequests.addChatMessage("Replay stopped!", ChatMessageType.INFORMATION);
|
||||
ReplayHandler.setReplaying(false);
|
||||
MCTimerHandler.setActiveTimer();
|
||||
ReplayHandler.setSpeed(previousReplaySpeed);
|
||||
ReplayHandler.setSpeed(0);
|
||||
}
|
||||
|
||||
public static void tickReplay() {
|
||||
private static boolean blocked = false;
|
||||
private static boolean deepBlock = false;
|
||||
|
||||
private static float ticks = 0;
|
||||
|
||||
private static boolean requestFinish = false;
|
||||
|
||||
public static void unblock() {
|
||||
if(!deepBlock) blocked = false;
|
||||
}
|
||||
|
||||
public static void tickReplay() {
|
||||
if(isVideoRecording()) {
|
||||
recordingTick();
|
||||
} else {
|
||||
normalTick();
|
||||
}
|
||||
}
|
||||
|
||||
private static void normalTick() {
|
||||
|
||||
if(ReplayHandler.isHurrying()) {
|
||||
lastRealTime = System.currentTimeMillis();
|
||||
return;
|
||||
@@ -91,7 +155,7 @@ public class ReplayProcess {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(linear && motionLinear == null) {
|
||||
//set up linear path
|
||||
motionLinear = new LinearPoint();
|
||||
@@ -115,9 +179,9 @@ public class ReplayProcess {
|
||||
|
||||
if(!calculated) {
|
||||
calculated = true;
|
||||
motionSpline.calcSpline();
|
||||
if(motionSpline != null) motionSpline.calcSpline();
|
||||
}
|
||||
|
||||
|
||||
long curTime = System.currentTimeMillis();
|
||||
long timeStep = curTime - lastRealTime;
|
||||
|
||||
@@ -169,7 +233,165 @@ public class ReplayProcess {
|
||||
if(!(nextTime == null || lastTime == null)) {
|
||||
curSpeed = ((double)((nextTime.getTimestamp()-lastTime.getTimestamp())))/((double)((nextTimeStamp-lastTimeStamp)));
|
||||
}
|
||||
|
||||
|
||||
if(lastTimeStamp == nextTimeStamp) {
|
||||
curSpeed = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
int currentDiff = nextPosStamp - lastPosStamp;
|
||||
int current = curRealReplayTime - lastPosStamp;
|
||||
|
||||
float currentStepPerc = (float)current/(float)currentDiff; //The percentage of the travelled path between the current positions
|
||||
if(Float.isInfinite(currentStepPerc)) currentStepPerc = 0;
|
||||
|
||||
float splinePos = ((float)ReplayHandler.getKeyframeIndex(lastPos) + currentStepPerc)/(float)(ReplayHandler.getPosKeyframeCount()-1);
|
||||
float timePos = ((float)ReplayHandler.getKeyframeIndex(lastTime) + currentStepPerc)/(float)(ReplayHandler.getTimeKeyframeCount()-1);
|
||||
|
||||
Position pos = null;
|
||||
if(!linear) {
|
||||
pos = motionSpline.getPoint(Math.max(0, Math.min(1, splinePos)));
|
||||
} else {
|
||||
pos = motionLinear.getPoint(Math.max(0, Math.min(1, splinePos)));
|
||||
}
|
||||
|
||||
Integer curPos = null;
|
||||
if(timeLinear != null) {
|
||||
curPos = timeLinear.getPoint(Math.max(0, Math.min(1, timePos)));
|
||||
}
|
||||
|
||||
if(pos != null) ReplayHandler.getCameraEntity().movePath(pos);
|
||||
|
||||
ReplayHandler.setSpeed(curSpeed);
|
||||
|
||||
if(curPos != null) ReplayHandler.setReplayPos(curPos);
|
||||
|
||||
//splinePos = (index of last entry + add) / total entries
|
||||
|
||||
lastRealReplayTime = curRealReplayTime;
|
||||
lastRealTime = curTime;
|
||||
|
||||
if(requestFinish) {
|
||||
stopReplayProcess(true);
|
||||
requestFinish = false;
|
||||
}
|
||||
|
||||
if(splinePos >= 1) {
|
||||
requestFinish = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void recordingTick() {
|
||||
if(ReplayHandler.isHurrying()) {
|
||||
if(!isVideoRecording()) {
|
||||
lastRealTime = System.currentTimeMillis();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(blocked && isVideoRecording()) {
|
||||
return;
|
||||
}
|
||||
deepBlock = true;
|
||||
blocked = true;
|
||||
|
||||
if(!linear && motionSpline == null) {
|
||||
//set up spline path
|
||||
motionSpline = new SplinePoint();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof PositionKeyframe) {
|
||||
PositionKeyframe pkf = (PositionKeyframe)kf;
|
||||
Position pos = pkf.getPosition();
|
||||
motionSpline.addPoint(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(linear && motionLinear == null) {
|
||||
//set up linear path
|
||||
motionLinear = new LinearPoint();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof PositionKeyframe) {
|
||||
PositionKeyframe pkf = (PositionKeyframe)kf;
|
||||
Position pos = pkf.getPosition();
|
||||
motionLinear.addPoint(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(timeLinear == null) {
|
||||
timeLinear = new LinearTimestamp();
|
||||
for(Keyframe kf : ReplayHandler.getKeyframes()) {
|
||||
if(kf instanceof TimeKeyframe) {
|
||||
timeLinear.addPoint(((TimeKeyframe)kf).getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!calculated) {
|
||||
calculated = true;
|
||||
motionSpline.calcSpline();
|
||||
}
|
||||
|
||||
long timeStep;
|
||||
|
||||
long curTime = System.currentTimeMillis();
|
||||
|
||||
if(isVideoRecording()) {
|
||||
timeStep = 1000/ReplayMod.replaySettings.getVideoFramerate();
|
||||
} else {
|
||||
timeStep = curTime - lastRealTime;
|
||||
}
|
||||
|
||||
int curRealReplayTime = (int)(lastRealReplayTime + timeStep);
|
||||
|
||||
PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(curRealReplayTime);
|
||||
PositionKeyframe nextPos = ReplayHandler.getNextPositionKeyframe(curRealReplayTime);
|
||||
|
||||
ReplayHandler.setRealTimelineCursor(curRealReplayTime);
|
||||
|
||||
int lastPosStamp = 0;
|
||||
int nextPosStamp = 0;
|
||||
|
||||
if(nextPos != null || lastPos != null) {
|
||||
if(nextPos != null) {
|
||||
nextPosStamp = nextPos.getRealTimestamp();
|
||||
} else {
|
||||
nextPosStamp = lastPos.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(lastPos != null) {
|
||||
lastPosStamp = lastPos.getRealTimestamp();
|
||||
} else {
|
||||
lastPosStamp = nextPos.getRealTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
TimeKeyframe lastTime = ReplayHandler.getPreviousTimeKeyframe(curRealReplayTime);
|
||||
TimeKeyframe nextTime = ReplayHandler.getNextTimeKeyframe(curRealReplayTime);
|
||||
|
||||
int lastTimeStamp = 0;
|
||||
int nextTimeStamp = 0;
|
||||
|
||||
double curSpeed = 0f;
|
||||
|
||||
if(nextTime != null || lastTime != null) {
|
||||
if(nextTime != null) {
|
||||
nextTimeStamp = nextTime.getRealTimestamp();
|
||||
} else {
|
||||
nextTimeStamp = lastTime.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(lastTime != null) {
|
||||
lastTimeStamp = lastTime.getRealTimestamp();
|
||||
} else {
|
||||
lastTimeStamp = nextTime.getRealTimestamp();
|
||||
}
|
||||
|
||||
if(!(nextTime == null || lastTime == null)) {
|
||||
curSpeed = ((double)((nextTime.getTimestamp()-lastTime.getTimestamp())))/((double)((nextTimeStamp-lastTimeStamp)));
|
||||
}
|
||||
|
||||
if(lastTimeStamp == nextTimeStamp) {
|
||||
curSpeed = 0f;
|
||||
}
|
||||
@@ -198,8 +420,10 @@ public class ReplayProcess {
|
||||
|
||||
if(pos != null) ReplayHandler.getCameraEntity().movePath(pos);
|
||||
|
||||
ReplayHandler.setSpeed(curSpeed);
|
||||
|
||||
ReplayHandler.setSpeed((float)curSpeed);
|
||||
if(isVideoRecording()) {
|
||||
MCTimerHandler.updateTimer((1f/ReplayMod.replaySettings.getVideoFramerate()));
|
||||
}
|
||||
if(curPos != null) ReplayHandler.setReplayPos(curPos);
|
||||
|
||||
//splinePos = (index of last entry + add) / total entries
|
||||
@@ -207,6 +431,28 @@ public class ReplayProcess {
|
||||
lastRealReplayTime = curRealReplayTime;
|
||||
lastRealTime = curTime;
|
||||
|
||||
if(splinePos >= 1) stopReplayProcess(true);
|
||||
//Video capturing, for testing purposes
|
||||
if(isVideoRecording()) {
|
||||
try {
|
||||
if(!VideoWriter.isRecording() && ReplayHandler.isReplaying()) {
|
||||
VideoWriter.startRecording(mc.displayWidth, mc.displayHeight);
|
||||
} else {
|
||||
VideoWriter.writeImage(ScreenCapture.captureScreen());
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if(requestFinish) {
|
||||
stopReplayProcess(true);
|
||||
VideoWriter.endRecording();
|
||||
}
|
||||
|
||||
if(splinePos >= 1) {
|
||||
requestFinish = true;
|
||||
}
|
||||
|
||||
deepBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiDownloadTerrain;
|
||||
@@ -27,10 +28,12 @@ import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.network.play.server.S01PacketJoinGame;
|
||||
import net.minecraft.network.play.server.S02PacketChat;
|
||||
import net.minecraft.network.play.server.S03PacketTimeUpdate;
|
||||
import net.minecraft.network.play.server.S06PacketUpdateHealth;
|
||||
import net.minecraft.network.play.server.S07PacketRespawn;
|
||||
import net.minecraft.network.play.server.S08PacketPlayerPosLook;
|
||||
import net.minecraft.network.play.server.S0BPacketAnimation;
|
||||
import net.minecraft.network.play.server.S0CPacketSpawnPlayer;
|
||||
import net.minecraft.network.play.server.S1CPacketEntityMetadata;
|
||||
import net.minecraft.network.play.server.S1DPacketEntityEffect;
|
||||
import net.minecraft.network.play.server.S1FPacketSetExperience;
|
||||
@@ -44,11 +47,12 @@ import net.minecraft.network.play.server.S2FPacketSetSlot;
|
||||
import net.minecraft.network.play.server.S30PacketWindowItems;
|
||||
import net.minecraft.network.play.server.S36PacketSignEditorOpen;
|
||||
import net.minecraft.network.play.server.S37PacketStatistics;
|
||||
import net.minecraft.network.play.server.S38PacketPlayerListItem;
|
||||
import net.minecraft.network.play.server.S38PacketPlayerListItem.AddPlayerData;
|
||||
import net.minecraft.network.play.server.S39PacketPlayerAbilities;
|
||||
import net.minecraft.network.play.server.S43PacketCamera;
|
||||
import net.minecraft.network.play.server.S45PacketTitle;
|
||||
import net.minecraft.network.play.server.S48PacketResourcePackSend;
|
||||
import net.minecraft.util.Timer;
|
||||
import net.minecraft.world.EnumDifficulty;
|
||||
import net.minecraft.world.WorldSettings.GameType;
|
||||
import net.minecraft.world.WorldType;
|
||||
@@ -59,6 +63,7 @@ import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
@@ -106,16 +111,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
private Field chatPacketPosition;
|
||||
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
public static Field mcTimer;
|
||||
|
||||
static {
|
||||
try {
|
||||
mcTimer = Minecraft.class.getDeclaredField(MCPNames.field("field_71428_T"));
|
||||
mcTimer.setAccessible(true);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private long now = System.currentTimeMillis();
|
||||
|
||||
@@ -168,13 +163,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
public void setReplaySpeed(final double d) {
|
||||
if(d != 0) this.replaySpeed = d;
|
||||
try {
|
||||
Timer timer = (Timer)mcTimer.get(mc);
|
||||
timer.timerSpeed = (float)d;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
MCTimerHandler.setTimerSpeed((float)d);
|
||||
}
|
||||
|
||||
public ReplaySender(final File replayFile, NetworkManager nm) {
|
||||
@@ -246,7 +235,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
dis = new DataInputStream(archive.getInputStream(replayEntry));
|
||||
} catch(Exception e) {
|
||||
@@ -267,7 +255,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
lastPacketSent = System.currentTimeMillis();
|
||||
ReplayHandler.restartReplay();
|
||||
}
|
||||
while(!startFromBeginning && (!paused() || FMLClientHandler.instance().isGUIOpen(GuiDownloadTerrain.class))) {
|
||||
while(!terminate && !startFromBeginning && (!paused() || FMLClientHandler.instance().isGUIOpen(GuiDownloadTerrain.class))) {
|
||||
try {
|
||||
|
||||
/*
|
||||
@@ -316,9 +304,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
if(hurryToTimestamp && currentTimeStamp >= desiredTimeStamp && !startFromBeginning) {
|
||||
hurryToTimestamp = false;
|
||||
if(!ReplayHandler.isReplaying() || hasRestarted) {
|
||||
((Timer)mcTimer.get(mc)).elapsedPartialTicks += 5;
|
||||
((Timer)mcTimer.get(mc)).elapsedTicks += 5;
|
||||
((Timer)mcTimer.get(mc)).renderPartialTicks += 5;
|
||||
MCTimerHandler.advanceRenderPartialTicks(5);
|
||||
MCTimerHandler.advancePartialTicks(5);
|
||||
MCTimerHandler.advanceTicks(5);
|
||||
}
|
||||
if(!ReplayHandler.isReplaying()) {
|
||||
Position pos = ReplayHandler.getLastPosition();
|
||||
@@ -339,8 +327,11 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
} catch(EOFException eof) {
|
||||
dis = new DataInputStream(archive.getInputStream(replayEntry));
|
||||
setReplaySpeed(0);
|
||||
} catch(IOException e) {}
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
@@ -369,6 +360,21 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private boolean allowMovement = false;
|
||||
|
||||
private static Field playerUUIDField;
|
||||
private static Field gameProfileField;
|
||||
|
||||
static {
|
||||
try {
|
||||
playerUUIDField = S0CPacketSpawnPlayer.class.getDeclaredField(MCPNames.field("field_179820_b"));
|
||||
playerUUIDField.setAccessible(true);
|
||||
|
||||
gameProfileField = S38PacketPlayerListItem.AddPlayerData.class.getDeclaredField("field_179964_d");
|
||||
gameProfileField.setAccessible(true);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg)
|
||||
throws Exception {
|
||||
@@ -376,6 +382,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx == null) {
|
||||
ctx = this.ctx;
|
||||
}
|
||||
|
||||
if(msg instanceof Packet) {
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
@@ -396,6 +406,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
p instanceof S2APacketParticles) return;
|
||||
}
|
||||
|
||||
if(p instanceof S03PacketTimeUpdate) {
|
||||
p = TimeHandler.getTimePacket((S03PacketTimeUpdate)p);
|
||||
}
|
||||
|
||||
if(p instanceof S02PacketChat) {
|
||||
byte pos = (Byte)chatPacketPosition.get(p);
|
||||
if(pos == 1) { //Ignores command block output sent
|
||||
@@ -408,22 +422,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
try {
|
||||
p.readPacketData(pb);
|
||||
|
||||
/*
|
||||
if(p instanceof S0CPacketSpawnPlayer) {
|
||||
S0CPacketSpawnPlayer sp = (S0CPacketSpawnPlayer)p;
|
||||
System.out.println("PACKET SPAWN PLAYER ----------");
|
||||
System.out.println("ENTITY ID: "+sp.func_148943_d());
|
||||
System.out.println("UUID: "+sp.func_179819_c());
|
||||
System.out.println("X: "+sp.func_148942_f());
|
||||
System.out.println("Y: "+sp.func_148949_g());
|
||||
System.out.println("Z: "+sp.func_148946_h());
|
||||
System.out.println("YAW: "+sp.func_148941_i());
|
||||
System.out.println("PITCH: "+sp.func_148945_j());
|
||||
System.out.println("ITEM: "+sp.func_148947_k());
|
||||
System.out.println("PACKET END -------------------");
|
||||
}
|
||||
*/
|
||||
|
||||
if(p instanceof S1CPacketEntityMetadata) {
|
||||
if((Integer)metadataPacketEntityId.get(p) == actualID) {
|
||||
metadataPacketEntityId.set(p, RecordingHandler.entityID);
|
||||
@@ -444,6 +442,33 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
difficulty, maxPlayers, worldType, false);
|
||||
}
|
||||
|
||||
String crPxl = "2cb08a5951f34e98bd0985d9747e80df";
|
||||
String johni = "cd3d4be14ffc2f9db432db09e0cd254b";
|
||||
|
||||
if(p instanceof S38PacketPlayerListItem) {
|
||||
S38PacketPlayerListItem pp = (S38PacketPlayerListItem)p;
|
||||
if(((AddPlayerData)pp.func_179767_a().get(0)).func_179962_a().getId().toString().replace("-", "").equals(crPxl)) {
|
||||
GameProfile johniGP = new GameProfile(UUID.fromString(johni.replaceAll(
|
||||
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})",
|
||||
"$1-$2-$3-$4-$5")), "Johni0702");
|
||||
gameProfileField.set(pp.func_179767_a().get(0), johniGP);
|
||||
//pp.func_179767_a().set(0, johniGP);
|
||||
p = pp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(p instanceof S0CPacketSpawnPlayer) {
|
||||
S0CPacketSpawnPlayer sp = (S0CPacketSpawnPlayer)p;
|
||||
|
||||
if(sp.func_179819_c().toString().replace("-", "").equals(crPxl)) {
|
||||
playerUUIDField.set(sp, UUID.fromString(johni.replaceAll(
|
||||
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})",
|
||||
"$1-$2-$3-$4-$5")));
|
||||
}
|
||||
|
||||
p = sp;
|
||||
}
|
||||
|
||||
if(p instanceof S07PacketRespawn) {
|
||||
allowMovement = true;
|
||||
@@ -491,8 +516,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
if(p instanceof S43PacketCamera) {
|
||||
return;
|
||||
}
|
||||
//if(ReplayHandler.isReplaying())
|
||||
// System.out.println("packet arrived");
|
||||
super.channelRead(ctx, p);
|
||||
} catch(Exception e) {
|
||||
System.out.println(p.getClass());
|
||||
@@ -520,7 +543,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
public boolean paused() {
|
||||
try {
|
||||
return ((Timer)mcTimer.get(mc)).timerSpeed == 0;
|
||||
return MCTimerHandler.getTimerSpeed() == 0;
|
||||
} catch(Exception e) {}
|
||||
return true;
|
||||
}
|
||||
@@ -528,7 +551,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
public double getReplaySpeed() {
|
||||
if(!paused()) return replaySpeed;
|
||||
else return 0;
|
||||
//return timeInfo.get().getSpeed();
|
||||
}
|
||||
|
||||
public File getReplayFile() {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package eu.crushedpixel.replaymod.replay;
|
||||
|
||||
import net.minecraft.network.play.server.S03PacketTimeUpdate;
|
||||
|
||||
public class TimeHandler {
|
||||
|
||||
private static long actualDaytime;
|
||||
private static long desiredDaytime;
|
||||
|
||||
private static boolean timeOverridden = false;
|
||||
|
||||
public static boolean isTimeOverridden() {
|
||||
return timeOverridden;
|
||||
}
|
||||
|
||||
public static void setDesiredDaytime(long ddt) {
|
||||
desiredDaytime = ddt;
|
||||
}
|
||||
|
||||
public static void setTimeOverridden(boolean overridden) {
|
||||
timeOverridden = overridden;
|
||||
}
|
||||
|
||||
public static S03PacketTimeUpdate getTimePacket(S03PacketTimeUpdate packet) {
|
||||
if(!timeOverridden) return packet;
|
||||
return new S03PacketTimeUpdate(packet.func_149366_c(), desiredDaytime, true);
|
||||
}
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
package eu.crushedpixel.replaymod.replay.screenshot;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests;
|
||||
import eu.crushedpixel.replaymod.chat.ChatMessageRequests.ChatMessageType;
|
||||
import eu.crushedpixel.replaymod.gui.GuiReplaySaving;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.utils.ImageUtils;
|
||||
|
||||
public class ReplayScreenshot {
|
||||
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
private static IntBuffer pixelBuffer;
|
||||
private static int[] pixelValues;
|
||||
|
||||
private static final byte[] uniqueBytes = new byte[]{0,1,1,2,3,5,8};
|
||||
|
||||
private static long last_finish = -1;
|
||||
|
||||
private static boolean before;
|
||||
private static double beforeSpeed;
|
||||
private static GuiScreen beforeScreen;
|
||||
|
||||
public static void prepareScreenshot() {
|
||||
before = mc.gameSettings.hideGUI;
|
||||
beforeSpeed = ReplayHandler.getSpeed();
|
||||
beforeScreen = mc.currentScreen;
|
||||
}
|
||||
|
||||
public static void saveScreenshot(Framebuffer buffer) {
|
||||
|
||||
try {
|
||||
GuiReplaySaving.replaySaving = true;
|
||||
|
||||
mc.gameSettings.hideGUI = true;
|
||||
mc.currentScreen = null;
|
||||
|
||||
mc.entityRenderer.updateCameraAndRender(0);
|
||||
ReplayHandler.setSpeed(0);
|
||||
|
||||
int width = mc.displayWidth;
|
||||
int height = mc.displayHeight;
|
||||
|
||||
if (OpenGlHelper.isFramebufferEnabled())
|
||||
{
|
||||
width = buffer.framebufferTextureWidth;
|
||||
height = buffer.framebufferTextureHeight;
|
||||
}
|
||||
|
||||
int k = width * height;
|
||||
|
||||
if (pixelBuffer == null || pixelBuffer.capacity() < k)
|
||||
{
|
||||
pixelBuffer = BufferUtils.createIntBuffer(k);
|
||||
pixelValues = new int[k];
|
||||
}
|
||||
|
||||
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
||||
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
pixelBuffer.clear();
|
||||
|
||||
if (OpenGlHelper.isFramebufferEnabled()) {
|
||||
GlStateManager.bindTexture(buffer.framebufferTexture);
|
||||
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer);
|
||||
}
|
||||
else {
|
||||
GL11.glReadPixels(0, 0, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer);
|
||||
}
|
||||
|
||||
pixelBuffer.get(pixelValues);
|
||||
TextureUtil.processPixelValues(pixelValues, width, height);
|
||||
BufferedImage bufferedimage = null;
|
||||
|
||||
|
||||
if (OpenGlHelper.isFramebufferEnabled())
|
||||
{
|
||||
bufferedimage = new BufferedImage(buffer.framebufferWidth, buffer.framebufferHeight, 1);
|
||||
int l = buffer.framebufferTextureHeight - buffer.framebufferHeight;
|
||||
|
||||
for (int i1 = l; i1 < buffer.framebufferTextureHeight; ++i1)
|
||||
{
|
||||
for (int j1 = 0; j1 < buffer.framebufferWidth; ++j1)
|
||||
{
|
||||
bufferedimage.setRGB(j1, i1 - l, pixelValues[i1 * buffer.framebufferTextureWidth + j1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bufferedimage = new BufferedImage(width, height, 1);
|
||||
bufferedimage.setRGB(0, 0, width, height, pixelValues, 0, width);
|
||||
}
|
||||
|
||||
final BufferedImage fbi = bufferedimage;
|
||||
|
||||
mc.gameSettings.hideGUI = before;
|
||||
mc.currentScreen = beforeScreen;
|
||||
ReplayHandler.setSpeed(beforeSpeed);
|
||||
|
||||
//The actual cropping and saving should be executed in a separate thread
|
||||
Thread ioThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
|
||||
float aspect = 1280f/720f;
|
||||
|
||||
Rectangle rect;
|
||||
if((float)fbi.getWidth()/(float)fbi.getHeight() <= aspect) {
|
||||
int h = Math.round(fbi.getWidth()/aspect);
|
||||
int y = (fbi.getHeight()/2) - (h/2);
|
||||
System.out.println(h+" | "+y);
|
||||
rect = new Rectangle(0, y, fbi.getWidth(), h);
|
||||
} else {
|
||||
int w = Math.round(fbi.getHeight()*aspect);
|
||||
int x = (fbi.getWidth()/2) - (w/2);
|
||||
rect = new Rectangle(x, 0, w, fbi.getHeight());
|
||||
}
|
||||
|
||||
final BufferedImage nbi = ImageUtils.cropImage(fbi, rect);
|
||||
|
||||
File replayFile = ReplayHandler.getReplayFile();
|
||||
|
||||
File folder = new File("./replay_recordings/");
|
||||
folder.mkdirs();
|
||||
|
||||
File temp = File.createTempFile("thumb", null);
|
||||
|
||||
int h = 720;
|
||||
int w = 1280;
|
||||
//int w = width*(h/height);
|
||||
|
||||
BufferedImage img = ImageUtils.scaleImage(nbi, new Dimension(w, h));
|
||||
|
||||
ImageIO.write(img, "jpg", temp);
|
||||
|
||||
File tempFile = File.createTempFile(replayFile.getName(), null);
|
||||
tempFile.delete(); tempFile.deleteOnExit();
|
||||
|
||||
replayFile.renameTo(tempFile);
|
||||
|
||||
replayFile.delete();
|
||||
replayFile.createNewFile();
|
||||
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
|
||||
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(replayFile));
|
||||
|
||||
FileInputStream fis = new FileInputStream(temp);
|
||||
|
||||
ZipEntry entry = zin.getNextEntry();
|
||||
while (entry != null) {
|
||||
String name = entry.getName();
|
||||
|
||||
if(!name.contains("thumb")) {
|
||||
// Add ZIP entry to output stream.
|
||||
zout.putNextEntry(new ZipEntry(name));
|
||||
// Transfer bytes from the ZIP file to the output file
|
||||
int len;
|
||||
while ((len = zin.read(buf)) > 0) {
|
||||
zout.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
entry = zin.getNextEntry();
|
||||
}
|
||||
|
||||
zout.putNextEntry(new ZipEntry("thumb"));
|
||||
int len;
|
||||
//Add unique bytes to the end of the file
|
||||
zout.write(uniqueBytes);
|
||||
|
||||
while ((len = fis.read(buf)) > 0) {
|
||||
zout.write(buf, 0, len);
|
||||
}
|
||||
|
||||
// Close the streams
|
||||
fis.close();
|
||||
zin.close();
|
||||
// Compress the files
|
||||
// Complete the ZIP file
|
||||
zout.close();
|
||||
tempFile.delete();
|
||||
temp.delete();
|
||||
|
||||
ChatMessageRequests.addChatMessage("Thumbnail has been successfully saved", ChatMessageType.INFORMATION);
|
||||
} catch(Exception e) {}
|
||||
finally {
|
||||
GuiReplaySaving.replaySaving = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ioThread.start();
|
||||
}
|
||||
catch (Exception exception) {
|
||||
mc.gameSettings.hideGUI = before;
|
||||
mc.currentScreen = beforeScreen;
|
||||
ReplayHandler.setSpeed(beforeSpeed);
|
||||
exception.printStackTrace();
|
||||
ChatMessageRequests.addChatMessage("Thumbnail could not be saved", ChatMessageType.WARNING);
|
||||
}
|
||||
|
||||
last_finish = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user