Use ReplayOutputStream for packet writing

We didn't used to do that because it would have required us to serialize and
then de-serialize as MCProtocolLib only to re-serialize each packet.
With ReplayStudio v2, we only have to convert to from MC's ByteBuf types to
ReplayStudio's ByteBuf (fairly trivial).

This also allows us to automatically make use of ReplayStudio's backwards
compatibility features: We didn't used to record the login phase pre-1.14 and we
still don't, so we now would have to inject a fake login packet (because we're
no longer generating legacy-version replay files) but ReplayOutputStream can
do that for us.
Without this commit, any pre-1.14 replays generated are unplayable due to the
missing LoginSuccess packet.
This commit is contained in:
Jonas Herzig
2020-04-21 22:58:55 +02:00
parent c8b7e5b213
commit 05f617a885

View File

@@ -12,7 +12,9 @@ import com.replaymod.recording.Setting;
import com.replaymod.recording.handler.ConnectionEventHandler; import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.recording.mixin.SPacketSpawnMobAccessor; import com.replaymod.recording.mixin.SPacketSpawnMobAccessor;
import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor; import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ReplayMetaData;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen; import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
@@ -85,7 +87,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private final ResourcePackRecorder resourcePackRecorder; private final ResourcePackRecorder resourcePackRecorder;
private final ExecutorService saveService = Executors.newSingleThreadExecutor(); private final ExecutorService saveService = Executors.newSingleThreadExecutor();
private final DataOutputStream packetOutputStream; private final ReplayOutputStream packetOutputStream;
private ReplayMetaData metaData; private ReplayMetaData metaData;
@@ -97,8 +99,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private volatile boolean serverWasPaused; private volatile boolean serverWasPaused;
//#if MC>=11400 //#if MC>=11400
private NetworkState connectionState = NetworkState.LOGIN; private NetworkState connectionState = NetworkState.LOGIN;
private boolean loginPhase = true;
//#else //#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY; //$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//$$ private boolean loginPhase = false;
//#endif //#endif
/** /**
@@ -113,8 +117,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
this.replayFile = replayFile; this.replayFile = replayFile;
this.metaData = metaData; this.metaData = metaData;
this.resourcePackRecorder = new ResourcePackRecorder(replayFile); this.resourcePackRecorder = new ResourcePackRecorder(replayFile);
// Note: doesn't actually always include the login phase, see `connectionState` field instead. this.packetOutputStream = replayFile.writePacketData();
this.packetOutputStream = new DataOutputStream(replayFile.writePacketData());
this.startTime = metaData.getDate(); this.startTime = metaData.getDate();
saveMetaData(); saveMetaData();
@@ -181,19 +184,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif //#endif
//#endif //#endif
byte[] bytes = getPacketData(packet);
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (serverWasPaused) {
timePassedWhilePaused = now - startTime - lastSentPacket;
serverWasPaused = false;
}
int timestamp = (int) (now - startTime - timePassedWhilePaused);
lastSentPacket = timestamp;
PacketData packetData = getPacketData(timestamp, packet);
saveService.submit(() -> { saveService.submit(() -> {
if (serverWasPaused) {
timePassedWhilePaused = now - startTime - lastSentPacket;
serverWasPaused = false;
}
int timestamp = (int) (now - startTime - timePassedWhilePaused);
lastSentPacket = timestamp;
try { try {
packetOutputStream.writeInt(timestamp); packetOutputStream.write(packetData);
packetOutputStream.writeInt(bytes.length);
packetOutputStream.write(bytes);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -202,6 +203,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#if MC>=11400 //#if MC>=11400
if (packet instanceof LoginSuccessS2CPacket) { if (packet instanceof LoginSuccessS2CPacket) {
connectionState = NetworkState.PLAY; connectionState = NetworkState.PLAY;
loginPhase = false;
} }
//#endif //#endif
} catch(Exception e) { } catch(Exception e) {
@@ -347,7 +349,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif //#endif
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private byte[] getPacketData(Packet packet) throws Exception { private PacketData getPacketData(int timestamp, Packet packet) throws Exception {
//#if MC<11500 //#if MC<11500
//$$ if (packet instanceof MobSpawnS2CPacket) { //$$ if (packet instanceof MobSpawnS2CPacket) {
//$$ MobSpawnS2CPacket p = (MobSpawnS2CPacket) packet; //$$ MobSpawnS2CPacket p = (MobSpawnS2CPacket) packet;
@@ -403,22 +405,26 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
throw new IOException("Unknown packet type:" + packet.getClass()); throw new IOException("Unknown packet type:" + packet.getClass());
} }
ByteBuf byteBuf = Unpooled.buffer(); ByteBuf byteBuf = Unpooled.buffer();
PacketByteBuf packetBuffer = new PacketByteBuf(byteBuf); try {
packetBuffer.writeVarInt(packetId); packet.write(new PacketByteBuf(byteBuf));
packet.write(packetBuffer); return new PacketData(timestamp, new com.replaymod.replaystudio.protocol.Packet(
MCVer.getPacketTypeRegistry(loginPhase),
packetId,
com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(
byteBuf.array(),
byteBuf.arrayOffset(),
byteBuf.readableBytes()
)
));
} finally {
byteBuf.release();
byteBuf.readerIndex(0); //#if MC>=10800
byte[] array = new byte[byteBuf.readableBytes()]; if (packet instanceof CustomPayloadS2CPacket) {
byteBuf.readBytes(array); ((CustomPayloadS2CPacket) packet).getData().release();
}
byteBuf.release(); //#endif
//#if MC>=10800
if (packet instanceof CustomPayloadS2CPacket) {
((CustomPayloadS2CPacket) packet).getData().release();
} }
//#endif
return array;
} }
public void addMarker(String name) { public void addMarker(String name) {