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:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user