Record raw packets instead of re-encoding decoded packets
Until now, recording worked by injecting right before the vanilla packet handler. At that point it was receiving packet classes and had to re-encode them to store them in the replay file. However, not all decoded packets can necessarily be re-encoded without errors, requiring us to have a bunch of workarounds. This commit changes the way recording works by injecting right before the decoder. It therefore receives the raw bytebufs and no longer has to deal with the re-encode issue.
This commit is contained in:
@@ -3,7 +3,6 @@ package com.replaymod.recording.handler;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.utils.ModCompat;
|
||||
import com.replaymod.core.utils.Utils;
|
||||
import com.replaymod.core.versions.MCVer;
|
||||
import com.replaymod.editor.gui.MarkerProcessor;
|
||||
import com.replaymod.recording.ServerInfoExt;
|
||||
import com.replaymod.recording.Setting;
|
||||
@@ -43,7 +42,6 @@ import static com.replaymod.core.versions.MCVer.getMinecraft;
|
||||
*/
|
||||
public class ConnectionEventHandler {
|
||||
|
||||
private static final String packetHandlerKey = "packet_handler";
|
||||
private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss";
|
||||
private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
|
||||
private static final MinecraftClient mc = getMinecraft();
|
||||
@@ -141,7 +139,13 @@ public class ConnectionEventHandler {
|
||||
metaData.setMcVersion(ReplayMod.instance.getMinecraftVersion());
|
||||
packetListener = new PacketListener(core, outputPath, replayFile, metaData);
|
||||
Channel channel = ((NetworkManagerAccessor) networkManager).getChannel();
|
||||
channel.pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);
|
||||
if (channel.pipeline().get(PacketListener.DECODER_KEY) != null) {
|
||||
// Regular channel, we'll inject our recorder directly before the decoder
|
||||
channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, packetListener);
|
||||
} else {
|
||||
// Integrated server passes packets directly, there's no splitting, decompression or decoding
|
||||
channel.pipeline().addFirst(PacketListener.RAW_RECORDER_KEY, packetListener);
|
||||
}
|
||||
|
||||
recordingEventHandler = new RecordingEventHandler(packetListener);
|
||||
recordingEventHandler.register();
|
||||
|
||||
@@ -321,99 +321,6 @@ public class RecordingEventHandler extends EventRegistrations {
|
||||
}
|
||||
}
|
||||
|
||||
//#if FABRIC>=1
|
||||
// FIXME fabric
|
||||
//#else
|
||||
//$$ @SubscribeEvent
|
||||
//$$ public void onPickupItem(ItemPickupEvent event) {
|
||||
//$$ try {
|
||||
//#if MC>=11100
|
||||
//#if MC>=11200
|
||||
//#if MC>=11400
|
||||
//$$ ItemStack stack = event.getStack();
|
||||
//$$ packetListener.save(new SCollectItemPacket(
|
||||
//$$ event.getOriginalEntity().getEntityId(),
|
||||
//$$ event.getPlayer().getEntityId(),
|
||||
//$$ event.getStack().getCount()
|
||||
//$$ ));
|
||||
//#else
|
||||
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
|
||||
//$$ event.pickedUp.getItem().getMaxStackSize()));
|
||||
//#endif
|
||||
//#else
|
||||
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(),
|
||||
//$$ event.pickedUp.getEntityItem().getMaxStackSize()));
|
||||
//#endif
|
||||
//#else
|
||||
//$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId()));
|
||||
//#endif
|
||||
//$$ } catch(Exception e) {
|
||||
//$$ e.printStackTrace();
|
||||
//$$ }
|
||||
//$$ }
|
||||
//#endif
|
||||
|
||||
//#if MC>=11400
|
||||
// FIXME fabric
|
||||
//#else
|
||||
//$$ @SubscribeEvent
|
||||
//$$ public void onSleep(PlayerSleepInBedEvent event) {
|
||||
//$$ try {
|
||||
//#if MC>=10904
|
||||
//$$ if (event.getEntityPlayer() != mc.player) {
|
||||
//$$ return;
|
||||
//$$ }
|
||||
//$$
|
||||
//$$ packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos()));
|
||||
//#else
|
||||
//$$ if (event.entityPlayer != mc.thePlayer) {
|
||||
//$$ return;
|
||||
//$$ }
|
||||
//$$
|
||||
//$$ packetListener.save(new S0APacketUseBed(event.entityPlayer,
|
||||
//#if MC>=10800
|
||||
//$$ event.pos
|
||||
//#else
|
||||
//$$ event.x, event.y, event.z
|
||||
//#endif
|
||||
//$$ ));
|
||||
//#endif
|
||||
//$$
|
||||
//$$ wasSleeping = true;
|
||||
//$$
|
||||
//$$ } catch(Exception e) {
|
||||
//$$ e.printStackTrace();
|
||||
//$$ }
|
||||
//$$ }
|
||||
//#endif
|
||||
|
||||
/* FIXME event not (yet?) on 1.13
|
||||
@SubscribeEvent
|
||||
public void enterMinecart(MinecartInteractEvent event) {
|
||||
try {
|
||||
//#if MC>=10904
|
||||
if(event.getEntity() != mc.player) {
|
||||
return;
|
||||
}
|
||||
|
||||
packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart()));
|
||||
|
||||
lastRiding = event.getMinecart().getEntityId();
|
||||
//#else
|
||||
//$$ if(event.entity != mc.thePlayer) {
|
||||
//$$ return;
|
||||
//$$ }
|
||||
//$$
|
||||
//$$ packetListener.save(new S1BPacketEntityAttach(0, event.player, event.minecart));
|
||||
//$$
|
||||
//$$ lastRiding = event.minecart.getEntityId();
|
||||
//#endif
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//#if MC>=10800
|
||||
public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) {
|
||||
//#else
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.replaymod.recording.mixin;
|
||||
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(ClientConnection.class)
|
||||
public abstract class MixinClientConnection {
|
||||
@Shadow
|
||||
private Channel channel;
|
||||
|
||||
@Inject(method = "setCompressionThreshold", at = @At("RETURN"))
|
||||
private void ensureReplayModRecorderIsAfterDecompress(CallbackInfo ci) {
|
||||
ChannelHandler recorder = null;
|
||||
for (Map.Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
String key = entry.getKey();
|
||||
if (PacketListener.RAW_RECORDER_KEY.equals(key)) {
|
||||
recorder = entry.getValue();
|
||||
}
|
||||
if (PacketListener.DECOMPRESS_KEY.equals(key)) {
|
||||
if (recorder != null) {
|
||||
// If we've already found the recorder, then that means decompress is after recorder. That's no good
|
||||
// because it means the recorder is getting compressed packets, we need to move the recorder.
|
||||
channel.pipeline().remove(recorder);
|
||||
channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, recorder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.replaymod.recording.packet;
|
||||
|
||||
import com.github.steveice10.netty.buffer.PooledByteBufAllocator;
|
||||
import com.github.steveice10.packetlib.tcp.io.ByteBufNetOutput;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.utils.Restrictions;
|
||||
@@ -16,23 +15,24 @@ import com.replaymod.recording.handler.ConnectionEventHandler;
|
||||
import com.replaymod.replaystudio.PacketData;
|
||||
import com.replaymod.replaystudio.data.Marker;
|
||||
import com.replaymod.replaystudio.io.ReplayOutputStream;
|
||||
import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State;
|
||||
import com.replaymod.replaystudio.protocol.Packet;
|
||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||
import com.replaymod.replaystudio.replay.ReplayMetaData;
|
||||
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.DisconnectS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.ItemPickupAnimationS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.network.NetworkState;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
@@ -41,24 +41,6 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
//#if MC>=11500
|
||||
//#else
|
||||
//$$ import com.replaymod.recording.mixin.SPacketSpawnMobAccessor;
|
||||
//$$ import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor;
|
||||
//$$ import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket;
|
||||
//#endif
|
||||
|
||||
//#if MC>=11400
|
||||
import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket;
|
||||
//#else
|
||||
//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
|
||||
//#endif
|
||||
|
||||
//#if MC>=10904
|
||||
//#else
|
||||
//$$ import java.util.List;
|
||||
//#endif
|
||||
|
||||
//#if MC>=10800
|
||||
//#if MC<10904
|
||||
//$$ import net.minecraft.network.play.server.S46PacketSetCompressionLevel;
|
||||
@@ -68,7 +50,6 @@ import net.minecraft.network.packet.s2c.play.ResourcePackSendS2CPacket;
|
||||
import net.minecraft.network.NetworkSide;
|
||||
//#endif
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
@@ -86,12 +67,31 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.replaymod.core.versions.MCVer.*;
|
||||
import static com.replaymod.replaystudio.util.Utils.writeInt;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@ChannelHandler.Sharable // so we can re-order it
|
||||
public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
|
||||
public static final String RAW_RECORDER_KEY = "replay_recorder_raw";
|
||||
public static final String DECODED_RECORDER_KEY = "replay_recorder_decoded";
|
||||
|
||||
public static final String DECOMPRESS_KEY = "decompress";
|
||||
public static final String DECODER_KEY = "decoder";
|
||||
|
||||
private static final MinecraftClient mc = getMinecraft();
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
//#if MC>=11700
|
||||
//$$ private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket("", "", false, null));
|
||||
//$$ private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket(0));
|
||||
//#else
|
||||
private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket());
|
||||
private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket());
|
||||
//#endif
|
||||
//#if MC<10904
|
||||
//$$ private static final int PACKET_ID_PLAY_COMPRESSION = getPacketId(EnumConnectionState.PLAY, new S46PacketSetCompressionLevel());
|
||||
//#endif
|
||||
|
||||
private final ReplayMod core;
|
||||
private final Path outputPath;
|
||||
private final ReplayFile replayFile;
|
||||
@@ -109,13 +109,6 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
private long lastSentPacket;
|
||||
private long timePassedWhilePaused;
|
||||
private volatile boolean serverWasPaused;
|
||||
//#if MC>=11400
|
||||
private NetworkState connectionState = NetworkState.LOGIN;
|
||||
private boolean loginPhase = true;
|
||||
//#else
|
||||
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
|
||||
//$$ private boolean loginPhase = false;
|
||||
//#endif
|
||||
|
||||
/**
|
||||
* Used to keep track of the last metadata save job submitted to the save service and
|
||||
@@ -163,6 +156,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
public void save(net.minecraft.network.Packet packet) {
|
||||
Packet encoded;
|
||||
try {
|
||||
encoded = encodeMcPacket(getConnectionState(), packet);
|
||||
} catch (Exception e) {
|
||||
logger.error("Encoding packet:", e);
|
||||
return;
|
||||
}
|
||||
save(encoded);
|
||||
}
|
||||
|
||||
public void save(Packet packet) {
|
||||
// If we're not on the main thread (i.e. we're on the netty thread), then we need to schedule the saving
|
||||
// to happen on the main thread so we can guarantee correct ordering of inbound and inject packets.
|
||||
@@ -173,24 +177,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if(packet instanceof PlayerSpawnS2CPacket) {
|
||||
//#if MC>=10800
|
||||
UUID uuid = ((PlayerSpawnS2CPacket) packet).getPlayerUuid();
|
||||
//#else
|
||||
//$$ UUID uuid = ((S0CPacketSpawnPlayer) packet).func_148948_e().getId();
|
||||
//#endif
|
||||
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
|
||||
uuids.add(uuid.toString());
|
||||
metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
|
||||
saveMetaData();
|
||||
}
|
||||
|
||||
//#if MC>=10800
|
||||
if (packet instanceof LoginCompressionS2CPacket) {
|
||||
//#if MC>=11800
|
||||
if (packet.getRegistry().getState() == State.LOGIN && packet.getId() == PACKET_ID_LOGIN_COMPRESSION) {
|
||||
return; // Replay data is never compressed on the packet level
|
||||
}
|
||||
//#if MC<10904
|
||||
//$$ if (packet instanceof S46PacketSetCompressionLevel) {
|
||||
//$$ if (packet.getRegistry().getState() == State.PLAY && packet.getId() == PACKET_ID_PLAY_COMPRESSION) {
|
||||
//$$ return; // Replay data is never compressed on the packet level
|
||||
//$$ }
|
||||
//#endif
|
||||
@@ -203,7 +195,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
int timestamp = (int) (now - startTime - timePassedWhilePaused);
|
||||
lastSentPacket = timestamp;
|
||||
PacketData packetData = getPacketData(timestamp, packet);
|
||||
PacketData packetData = new PacketData(timestamp, packet);
|
||||
saveService.submit(() -> {
|
||||
try {
|
||||
if (ReplayMod.isMinimalMode()) {
|
||||
@@ -230,18 +222,27 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
//#if MC>=11400
|
||||
if (packet instanceof LoginSuccessS2CPacket) {
|
||||
connectionState = NetworkState.PLAY;
|
||||
loginPhase = false;
|
||||
}
|
||||
//#endif
|
||||
} catch(Exception e) {
|
||||
logger.error("Writing packet:", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
super.handlerAdded(ctx);
|
||||
|
||||
if (ctx.pipeline().get(DECODED_RECORDER_KEY) == null) {
|
||||
if (ctx.pipeline().get(PacketListener.DECODER_KEY) != null) {
|
||||
// Regular channel, we'll inject our decoded recorder directly after the decoder
|
||||
ctx.pipeline().addAfter(DECODER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener());
|
||||
} else {
|
||||
// Integrated server passes packets directly, there's no splitting, decompression or decoding
|
||||
// The decoded packet handler can just go directly behind this hand
|
||||
ctx.pipeline().addAfter(RAW_RECORDER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
metaData.setDuration((int) lastSentPacket);
|
||||
@@ -328,139 +329,44 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
this.context = ctx;
|
||||
|
||||
if (msg instanceof Packet) {
|
||||
try {
|
||||
Packet packet = (Packet) msg;
|
||||
NetworkState connectionState = getConnectionState();
|
||||
|
||||
//#if MC>=10904
|
||||
if(packet instanceof ItemPickupAnimationS2CPacket) {
|
||||
if(mc.player != null ||
|
||||
((ItemPickupAnimationS2CPacket) packet).getEntityId() == mc.player.getEntityId()) {
|
||||
//#else
|
||||
//$$ if(packet instanceof S0DPacketCollectItem) {
|
||||
//$$ if(mc.thePlayer != null || ((S0DPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) {
|
||||
//#endif
|
||||
super.channelRead(ctx, msg);
|
||||
return;
|
||||
Packet packet = null;
|
||||
if (msg instanceof ByteBuf) {
|
||||
// for regular connections, we're expecting to observe `ByteBuf`s here
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
if (buf.readableBytes() > 0) {
|
||||
packet = decodePacket(connectionState, buf);
|
||||
}
|
||||
} else if (msg instanceof net.minecraft.network.Packet) {
|
||||
// for integrated server connections MC is passing the packet objects directly, so we need to encode them
|
||||
// ourselves to be able to store them
|
||||
packet = encodeMcPacket(connectionState, (net.minecraft.network.Packet) msg);
|
||||
}
|
||||
|
||||
//#if MC>=10800
|
||||
if (packet instanceof ResourcePackSendS2CPacket) {
|
||||
if (packet != null) {
|
||||
if (connectionState == NetworkState.PLAY && packet.getId() == PACKET_ID_RESOURCE_PACK_SEND) {
|
||||
ClientConnection connection = ctx.pipeline().get(ClientConnection.class);
|
||||
save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) packet));
|
||||
save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) decodeMcPacket(packet)));
|
||||
return;
|
||||
}
|
||||
//#else
|
||||
//$$ if (packet instanceof S3FPacketCustomPayload) {
|
||||
//$$ S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet;
|
||||
//$$ if ("MC|RPack".equals(p.func_149169_c())) {
|
||||
//$$ save(resourcePackRecorder.handleResourcePack(p));
|
||||
//$$ return;
|
||||
//$$ }
|
||||
//$$ }
|
||||
//#endif
|
||||
|
||||
//#if MC<11400
|
||||
//$$ if (packet instanceof FMLProxyPacket) {
|
||||
//$$ // This packet requires special handling
|
||||
//#if MC>=10800
|
||||
//$$ ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save);
|
||||
//#else
|
||||
//$$ save(((FMLProxyPacket) packet).toS3FPacket());
|
||||
//#endif
|
||||
//$$ super.channelRead(ctx, msg);
|
||||
//$$ return;
|
||||
//$$ }
|
||||
//#endif
|
||||
|
||||
//#if MC>=10800
|
||||
if (packet instanceof CustomPayloadS2CPacket) {
|
||||
// Forge may read from this ByteBuf and/or release it during handling
|
||||
// We want to save the full thing however, so we create a copy and save that one instead of the
|
||||
// original one
|
||||
// Note: This isn't an issue with vanilla MC because our saving code runs on the main thread
|
||||
// shortly before the vanilla handling code does. Forge however does some stuff on the netty
|
||||
// threads which leads to this race condition
|
||||
packet = new CustomPayloadS2CPacket(
|
||||
((CustomPayloadS2CPacket) packet).getChannel(),
|
||||
new PacketByteBuf(((CustomPayloadS2CPacket) packet).getData().slice().retain())
|
||||
);
|
||||
}
|
||||
//#endif
|
||||
|
||||
save(packet);
|
||||
|
||||
if (packet instanceof CustomPayloadS2CPacket) {
|
||||
CustomPayloadS2CPacket p = (CustomPayloadS2CPacket) packet;
|
||||
if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannel())) {
|
||||
packet = new DisconnectS2CPacket(new LiteralText("Please update to view this replay."));
|
||||
save(packet);
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.error("Handling packet for recording:", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
//#if MC>=10904
|
||||
private <T> void DataManager_set(DataTracker dataManager, DataTracker.Entry<T> entry) {
|
||||
dataManager.startTracking(entry.getData(), entry.get());
|
||||
private NetworkState getConnectionState() {
|
||||
ChannelHandlerContext ctx = context;
|
||||
if (ctx == null) {
|
||||
return NetworkState.LOGIN;
|
||||
}
|
||||
AttributeKey<NetworkState> key = ClientConnection.ATTR_KEY_PROTOCOL;
|
||||
return ctx.channel().attr(key).get();
|
||||
}
|
||||
//#endif
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PacketData getPacketData(int timestamp, Packet packet) throws Exception {
|
||||
//#if MC<11500
|
||||
//$$ if (packet instanceof MobSpawnS2CPacket) {
|
||||
//$$ MobSpawnS2CPacket p = (MobSpawnS2CPacket) packet;
|
||||
//$$ SPacketSpawnMobAccessor pa = (SPacketSpawnMobAccessor) p;
|
||||
//$$ if (pa.getDataManager() == null) {
|
||||
//$$ pa.setDataManager(new DataTracker(null));
|
||||
//$$ if (p.getTrackedValues() != null) {
|
||||
//$$ Set<Integer> seen = new HashSet<>();
|
||||
//#if MC>=10904
|
||||
//$$ for (DataTracker.Entry<?> entry : Lists.reverse(p.getTrackedValues())) {
|
||||
//$$ if (!seen.add(entry.getData().getId())) continue;
|
||||
//$$ DataManager_set(pa.getDataManager(), entry);
|
||||
//$$ }
|
||||
//#else
|
||||
//$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List<DataWatcher.WatchableObject>) p.func_149027_c())) {
|
||||
//$$ if (!seen.add(wo.getDataValueId())) continue;
|
||||
//$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject());
|
||||
//$$ }
|
||||
//#endif
|
||||
//$$ }
|
||||
//$$ }
|
||||
//$$ }
|
||||
//$$
|
||||
//$$ if (packet instanceof PlayerSpawnS2CPacket) {
|
||||
//$$ PlayerSpawnS2CPacket p = (PlayerSpawnS2CPacket) packet;
|
||||
//$$ SPacketSpawnPlayerAccessor pa = (SPacketSpawnPlayerAccessor) p;
|
||||
//$$ if (pa.getDataManager() == null) {
|
||||
//$$ pa.setDataManager(new DataTracker(null));
|
||||
//$$ if (p.getTrackedValues() != null) {
|
||||
//$$ Set<Integer> seen = new HashSet<>();
|
||||
//#if MC>=10904
|
||||
//$$ for (DataTracker.Entry<?> entry : Lists.reverse(p.getTrackedValues())) {
|
||||
//$$ if (!seen.add(entry.getData().getId())) continue;
|
||||
//$$ DataManager_set(pa.getDataManager(), entry);
|
||||
//$$ }
|
||||
//#else
|
||||
//$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List<DataWatcher.WatchableObject>) p.func_148944_c())) {
|
||||
//$$ if (!seen.add(wo.getDataValueId())) continue;
|
||||
//$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject());
|
||||
//$$ }
|
||||
//#endif
|
||||
//$$ }
|
||||
//$$ }
|
||||
//$$ }
|
||||
//#endif
|
||||
|
||||
private static Packet encodeMcPacket(NetworkState connectionState, net.minecraft.network.Packet packet) throws Exception {
|
||||
//#if MC>=10800
|
||||
Integer packetId = connectionState.getPacketId(NetworkSide.CLIENTBOUND, packet);
|
||||
//#else
|
||||
@@ -472,23 +378,55 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
try {
|
||||
packet.write(new PacketByteBuf(byteBuf));
|
||||
return new PacketData(timestamp, new com.replaymod.replaystudio.protocol.Packet(
|
||||
MCVer.getPacketTypeRegistry(loginPhase),
|
||||
return new Packet(
|
||||
MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN),
|
||||
packetId,
|
||||
com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(
|
||||
byteBuf.array(),
|
||||
byteBuf.arrayOffset(),
|
||||
byteBuf.readableBytes()
|
||||
)
|
||||
));
|
||||
);
|
||||
} finally {
|
||||
byteBuf.release();
|
||||
|
||||
//#if MC>=10800
|
||||
if (packet instanceof CustomPayloadS2CPacket) {
|
||||
((CustomPayloadS2CPacket) packet).getData().release();
|
||||
}
|
||||
}
|
||||
|
||||
private static net.minecraft.network.Packet decodeMcPacket(Packet packet) throws IOException, IllegalAccessException, InstantiationException {
|
||||
NetworkState connectionState = packet.getRegistry().getState() == State.LOGIN ? NetworkState.LOGIN : NetworkState.PLAY;
|
||||
int packetId = packet.getId();
|
||||
PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.wrappedBuffer(packet.getBuf().nioBuffer()));
|
||||
|
||||
//#if MC>=11700
|
||||
//$$ return connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId, packetBuf);
|
||||
//#else
|
||||
//#if MC>=10800
|
||||
net.minecraft.network.Packet p = connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId);
|
||||
//#else
|
||||
//$$ net.minecraft.network.Packet p = net.minecraft.network.Packet.generatePacket(connectionState.func_150755_b(), packetId);
|
||||
//#endif
|
||||
p.read(packetBuf);
|
||||
return p;
|
||||
//#endif
|
||||
}
|
||||
|
||||
private static Packet decodePacket(NetworkState connectionState, ByteBuf buf) {
|
||||
PacketByteBuf packetBuf = new PacketByteBuf(buf.slice());
|
||||
int packetId = packetBuf.readVarInt();
|
||||
byte[] bytes = new byte[packetBuf.readableBytes()];
|
||||
packetBuf.readBytes(bytes);
|
||||
return new Packet(
|
||||
MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN),
|
||||
packetId,
|
||||
com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(bytes)
|
||||
);
|
||||
}
|
||||
|
||||
private static int getPacketId(NetworkState networkState, net.minecraft.network.Packet packet) {
|
||||
try {
|
||||
return requireNonNull(networkState.getPacketId(NetworkSide.CLIENTBOUND, packet));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to determine packet id for " + packet.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,4 +468,31 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
public void setServerWasPaused() {
|
||||
this.serverWasPaused = true;
|
||||
}
|
||||
|
||||
private class DecodedPacketListener extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
|
||||
if (msg instanceof CustomPayloadS2CPacket) {
|
||||
CustomPayloadS2CPacket packet = (CustomPayloadS2CPacket) msg;
|
||||
if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannel())) {
|
||||
save(new DisconnectS2CPacket(new LiteralText("Please update to view this replay.")));
|
||||
}
|
||||
}
|
||||
|
||||
if (msg instanceof PlayerSpawnS2CPacket) {
|
||||
//#if MC>=10800
|
||||
UUID uuid = ((PlayerSpawnS2CPacket) msg).getPlayerUuid();
|
||||
//#else
|
||||
//$$ UUID uuid = ((S0CPacketSpawnPlayer) msg).func_148948_e().getId();
|
||||
//#endif
|
||||
Set<String> uuids = new HashSet<>(Arrays.asList(metaData.getPlayers()));
|
||||
uuids.add(uuid.toString());
|
||||
metaData.setPlayers(uuids.toArray(new String[uuids.size()]));
|
||||
saveMetaData();
|
||||
}
|
||||
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"EntityLivingBaseAccessor",
|
||||
"IntegratedServerAccessor",
|
||||
"NetworkManagerAccessor",
|
||||
"MixinClientConnection",
|
||||
//#if MC<11500
|
||||
//$$ "SPacketSpawnMobAccessor",
|
||||
//$$ "SPacketSpawnPlayerAccessor",
|
||||
|
||||
Reference in New Issue
Block a user