Use ReplayInputStream to support loading older replays on newer MC

This commit is contained in:
Jonas Herzig
2017-08-23 20:23:04 +02:00
parent 7a4440c4a8
commit 6a7147b401
2 changed files with 43 additions and 20 deletions

View File

@@ -5,8 +5,12 @@ import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask; import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@@ -103,10 +107,10 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
protected ChannelHandlerContext ctx; protected ChannelHandlerContext ctx;
/** /**
* The data input stream from which new packets are read. * The replay input stream from which new packets are read.
* When accessing this stream make sure to synchronize on {@code this} as it's used from multiple threads. * When accessing this stream make sure to synchronize on {@code this} as it's used from multiple threads.
*/ */
protected DataInputStream dis; protected ReplayInputStream replayIn;
/** /**
* The next packet that should be sent. * The next packet that should be sent.
@@ -546,8 +550,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
REPLAY_LOOP: REPLAY_LOOP:
while (!terminate) { while (!terminate) {
synchronized (ReplaySender.this) { synchronized (ReplaySender.this) {
if (dis == null) { if (replayIn == null) {
dis = new DataInputStream(replayFile.getPacketData()); replayIn = replayFile.getPacketData();
} }
// Packet loop // Packet loop
while (true) { while (true) {
@@ -574,7 +578,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
// Read the next packet if we don't already have one // Read the next packet if we don't already have one
if (nextPacket == null) { if (nextPacket == null) {
nextPacket = new PacketData(dis); nextPacket = new PacketData(replayIn);
} }
int nextTimeStamp = nextPacket.timestamp; int nextTimeStamp = nextPacket.timestamp;
@@ -631,9 +635,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
nextPacket = null; nextPacket = null;
lastPacketSent = System.currentTimeMillis(); lastPacketSent = System.currentTimeMillis();
replayHandler.restartedReplay(); replayHandler.restartedReplay();
if (dis != null) { if (replayIn != null) {
dis.close(); replayIn.close();
dis = null; replayIn = null;
} }
} }
} }
@@ -722,17 +726,17 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
if (timestamp < lastTimeStamp) { // Restart the replay if we need to go backwards in time if (timestamp < lastTimeStamp) { // Restart the replay if we need to go backwards in time
hasWorldLoaded = false; hasWorldLoaded = false;
lastTimeStamp = 0; lastTimeStamp = 0;
if (dis != null) { if (replayIn != null) {
dis.close(); replayIn.close();
dis = null; replayIn = null;
} }
startFromBeginning = false; startFromBeginning = false;
nextPacket = null; nextPacket = null;
replayHandler.restartedReplay(); replayHandler.restartedReplay();
} }
if (dis == null) { if (replayIn == null) {
dis = new DataInputStream(replayFile.getPacketData()); replayIn = replayFile.getPacketData();
} }
while (true) { // Send packets while (true) { // Send packets
@@ -744,7 +748,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
nextPacket = null; nextPacket = null;
} else { } else {
// Otherwise read one from the input stream // Otherwise read one from the input stream
pd = new PacketData(dis); pd = new PacketData(replayIn);
} }
int nextTimeStamp = pd.timestamp; int nextTimeStamp = pd.timestamp;
@@ -759,7 +763,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} catch (EOFException eof) { } catch (EOFException eof) {
// Shit! We hit the end before finishing our job! What shall we do now? // Shit! We hit the end before finishing our job! What shall we do now?
// well, let's just pretend we're done... // well, let's just pretend we're done...
dis = null; replayIn = null;
break; break;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -842,13 +846,32 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
} }
private static final class PacketData { private static final class PacketData {
private static final ByteBuf byteBuf = Unpooled.buffer();
private static final ByteBufOutputStream byteBufOut = new ByteBufOutputStream(byteBuf);
private static final ReplayOutputStream encoder = new ReplayOutputStream(new ReplayStudio(), byteBufOut);
private final int timestamp; private final int timestamp;
private final byte[] bytes; private final byte[] bytes;
public PacketData(DataInputStream in) throws IOException { public PacketData(ReplayInputStream in) throws IOException {
timestamp = in.readInt(); com.replaymod.replaystudio.PacketData data = in.readPacket();
bytes = new byte[in.readInt()]; timestamp = (int) data.getTime();
in.readFully(bytes); // We need to re-encode MCProtocolLib packets, so we can later decode them as NMS packets
// The main reason we aren't reading them as NMS packets is that we want ReplayStudio to be able
// to apply ViaVersion (and potentially other magic) to it.
synchronized (encoder) {
byteBuf.markReaderIndex(); // Mark the current reader and writer index (should be at start)
byteBuf.markWriterIndex();
encoder.write(data); // Re-encode packet, data will end up in byteBuf
encoder.flush();
byteBuf.skipBytes(8); // Skip packet length & timestamp
bytes = new byte[byteBuf.readableBytes()]; // Create bytes array of sufficient size
byteBuf.readBytes(bytes); // Read all data into bytes
byteBuf.resetReaderIndex(); // Reset reader & writer index for next use
byteBuf.resetWriterIndex();
}
} }
} }
} }