From d1e55266e2c36de23557354e2f19745de3e3d641 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Tue, 18 Aug 2015 10:39:42 +0200 Subject: [PATCH] The fake player now has the same entity id as the recording player as assigned by the server This fixes arrows bumping into recording player in replay ReplaySender converts old replays on the fly Do not insert PlayerListEntry as it's already contained in the replay --- .../events/handlers/RecordingHandler.java | 67 ++++--------- .../mixin/MixinNetHandlerPlayClient.java | 28 ++++++ .../replaymod/replay/ReplaySender.java | 95 ++++++++++++++----- src/main/resources/META-INF/replaymod_at.cfg | 11 ++- src/main/resources/mixins.replaymod.json | 1 + 5 files changed, 129 insertions(+), 73 deletions(-) create mode 100644 src/main/java/eu/crushedpixel/replaymod/mixin/MixinNetHandlerPlayClient.java diff --git a/src/main/java/eu/crushedpixel/replaymod/events/handlers/RecordingHandler.java b/src/main/java/eu/crushedpixel/replaymod/events/handlers/RecordingHandler.java index e33eb55c..9ca66d0b 100755 --- a/src/main/java/eu/crushedpixel/replaymod/events/handlers/RecordingHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/events/handlers/RecordingHandler.java @@ -12,12 +12,8 @@ import net.minecraft.network.Packet; import net.minecraft.network.PacketBuffer; import net.minecraft.network.play.server.*; import net.minecraft.network.play.server.S14PacketEntity.S17PacketEntityLookMove; -import net.minecraft.network.play.server.S38PacketPlayerListItem.Action; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.util.MathHelper; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.living.LivingDeathEvent; -import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.minecart.MinecartInteractEvent; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import net.minecraftforge.event.entity.player.PlayerUseItemEvent; @@ -29,8 +25,6 @@ import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent; public class RecordingHandler { - public static final int entityID = Integer.MIN_VALUE + 9001; - private final Minecraft mc = Minecraft.getMinecraft(); private Double lastX = null, lastY = null, lastZ = null; private ItemStack[] playerItems = new ItemStack[5]; @@ -39,33 +33,10 @@ public class RecordingHandler { private int lastRiding = -1; private Integer rotationYawHeadBefore = null; - @SubscribeEvent - public void onPlayerJoin(EntityJoinWorldEvent e) { + public void onPlayerJoin() { try { - if(e.entity != mc.thePlayer) return; if(!ConnectionEventHandler.isRecording()) return; - EntityPlayer player = (EntityPlayer) e.entity; - - S38PacketPlayerListItem ppli = new S38PacketPlayerListItem(); - ByteBuf buf = Unpooled.buffer(); - PacketBuffer pbuf = new PacketBuffer(buf); - - pbuf.writeEnumValue(Action.ADD_PLAYER); - pbuf.writeVarIntToBuffer(1); - pbuf.writeUuid(e.entity.getUniqueID()); - - pbuf.writeString(player.getName()); - pbuf.writeVarIntToBuffer(0); - pbuf.writeVarIntToBuffer(mc.playerController.getCurrentGameType().getID()); - pbuf.writeVarIntToBuffer(0); - - pbuf.writeBoolean(true); - pbuf.writeChatComponent(player.getDisplayName()); - - ppli.readPacketData(pbuf); - ConnectionEventHandler.insertPacket(ppli); - ConnectionEventHandler.insertPacket(spawnPlayer(mc.thePlayer)); } catch(Exception e1) { e1.printStackTrace(); @@ -79,7 +50,7 @@ public class RecordingHandler { ByteBuf bb = Unpooled.buffer(); PacketBuffer pb = new PacketBuffer(bb); - pb.writeVarIntToBuffer(entityID); + pb.writeVarIntToBuffer(player.getEntityId()); pb.writeUuid(EntityPlayer.getUUID(player.getGameProfile())); pb.writeInt(MathHelper.floor_double(player.posX * 32.0D)); @@ -144,12 +115,12 @@ public class RecordingHandler { int z = MathHelper.floor_double(e.player.posZ * 32.0D); byte yaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F)); byte pitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F)); - packet = new S18PacketEntityTeleport(entityID, x, y, z, yaw, pitch, e.player.onGround); + packet = new S18PacketEntityTeleport(e.player.getEntityId(), x, y, z, yaw, pitch, e.player.onGround); } else { byte newYaw = (byte) ((int) (e.player.rotationYaw * 256.0F / 360.0F)); byte newPitch = (byte) ((int) (e.player.rotationPitch * 256.0F / 360.0F)); - packet = new S17PacketEntityLookMove(entityID, + packet = new S17PacketEntityLookMove(e.player.getEntityId(), (byte) Math.round(dx * 32), (byte) Math.round(dy * 32), (byte) Math.round(dz * 32), newYaw, newPitch, e.player.onGround); } @@ -164,7 +135,7 @@ public class RecordingHandler { ByteBuf bb1 = Unpooled.buffer(); PacketBuffer pb1 = new PacketBuffer(bb1); - pb1.writeVarIntToBuffer(entityID); + pb1.writeVarIntToBuffer(e.player.getEntityId()); pb1.writeByte(rotationYawHead); head.readPacketData(pb1); @@ -174,7 +145,7 @@ public class RecordingHandler { rotationYawHeadBefore = rotationYawHead; } - S12PacketEntityVelocity vel = new S12PacketEntityVelocity(entityID, e.player.motionX, e.player.motionY, e.player.motionZ); + S12PacketEntityVelocity vel = new S12PacketEntityVelocity(e.player.getEntityId(), e.player.motionX, e.player.motionY, e.player.motionZ); ConnectionEventHandler.insertPacket(vel); //Animation Packets @@ -185,7 +156,7 @@ public class RecordingHandler { ByteBuf bb = Unpooled.buffer(); PacketBuffer pb = new PacketBuffer(bb); - pb.writeVarIntToBuffer(entityID); + pb.writeVarIntToBuffer(e.player.getEntityId()); pb.writeByte(0); pac.readPacketData(pb); @@ -216,31 +187,31 @@ public class RecordingHandler { //Inventory Handling if(playerItems[0] != mc.thePlayer.getHeldItem()) { playerItems[0] = mc.thePlayer.getHeldItem(); - S04PacketEntityEquipment pee = new S04PacketEntityEquipment(entityID, 0, playerItems[0]); + S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 0, playerItems[0]); ConnectionEventHandler.insertPacket(pee); } if(playerItems[1] != mc.thePlayer.inventory.armorInventory[0]) { playerItems[1] = mc.thePlayer.inventory.armorInventory[0]; - S04PacketEntityEquipment pee = new S04PacketEntityEquipment(entityID, 1, playerItems[1]); + S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 1, playerItems[1]); ConnectionEventHandler.insertPacket(pee); } if(playerItems[2] != mc.thePlayer.inventory.armorInventory[1]) { playerItems[2] = mc.thePlayer.inventory.armorInventory[1]; - S04PacketEntityEquipment pee = new S04PacketEntityEquipment(entityID, 2, playerItems[2]); + S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 2, playerItems[2]); ConnectionEventHandler.insertPacket(pee); } if(playerItems[3] != mc.thePlayer.inventory.armorInventory[2]) { playerItems[3] = mc.thePlayer.inventory.armorInventory[2]; - S04PacketEntityEquipment pee = new S04PacketEntityEquipment(entityID, 3, playerItems[3]); + S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 3, playerItems[3]); ConnectionEventHandler.insertPacket(pee); } if(playerItems[4] != mc.thePlayer.inventory.armorInventory[3]) { playerItems[4] = mc.thePlayer.inventory.armorInventory[3]; - S04PacketEntityEquipment pee = new S04PacketEntityEquipment(entityID, 4, playerItems[4]); + S04PacketEntityEquipment pee = new S04PacketEntityEquipment(e.player.getEntityId(), 4, playerItems[4]); ConnectionEventHandler.insertPacket(pee); } @@ -259,7 +230,7 @@ public class RecordingHandler { ByteBuf buf = Unpooled.buffer(); PacketBuffer pbuf = new PacketBuffer(buf); - pbuf.writeInt(entityID); + pbuf.writeInt(e.player.getEntityId()); pbuf.writeInt(lastRiding); pbuf.writeBoolean(false); @@ -275,7 +246,7 @@ public class RecordingHandler { ByteBuf bb = Unpooled.buffer(); PacketBuffer pb = new PacketBuffer(bb); - pb.writeVarIntToBuffer(entityID); + pb.writeVarIntToBuffer(e.player.getEntityId()); pb.writeByte(2); pac.readPacketData(pb); @@ -294,7 +265,7 @@ public class RecordingHandler { public void onPickupItem(ItemPickupEvent event) { if(!ConnectionEventHandler.isRecording()) return; try { - ConnectionEventHandler.insertPacket(new S0DPacketCollectItem(event.pickedUp.getEntityId(), entityID)); + ConnectionEventHandler.insertPacket(new S0DPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId())); } catch(Exception e) { e.printStackTrace(); } @@ -305,7 +276,7 @@ public class RecordingHandler { if(!ConnectionEventHandler.isRecording()) return; try { //destroy entity, then respawn - ConnectionEventHandler.insertPacket(new S13PacketDestroyEntities(entityID)); + ConnectionEventHandler.insertPacket(new S13PacketDestroyEntities(mc.thePlayer.getEntityId())); ConnectionEventHandler.insertPacket(spawnPlayer(mc.thePlayer)); } catch(Exception e) { e.printStackTrace(); @@ -322,7 +293,7 @@ public class RecordingHandler { ByteBuf bb = Unpooled.buffer(); PacketBuffer pb = new PacketBuffer(bb); - pb.writeVarIntToBuffer(entityID); + pb.writeVarIntToBuffer(event.entityPlayer.getEntityId()); pb.writeByte(3); packet.readPacketData(pb); @@ -347,7 +318,7 @@ public class RecordingHandler { ByteBuf buf = Unpooled.buffer(); PacketBuffer pbuf = new PacketBuffer(buf); - pbuf.writeVarIntToBuffer(entityID); + pbuf.writeVarIntToBuffer(event.entityPlayer.getEntityId()); pbuf.writeBlockPos(event.pos); pub.readPacketData(pbuf); @@ -374,7 +345,7 @@ public class RecordingHandler { ByteBuf buf = Unpooled.buffer(); PacketBuffer pbuf = new PacketBuffer(buf); - pbuf.writeInt(entityID); + pbuf.writeInt(event.player.getEntityId()); pbuf.writeInt(event.minecart.getEntityId()); pbuf.writeBoolean(false); diff --git a/src/main/java/eu/crushedpixel/replaymod/mixin/MixinNetHandlerPlayClient.java b/src/main/java/eu/crushedpixel/replaymod/mixin/MixinNetHandlerPlayClient.java new file mode 100644 index 00000000..d9db66b1 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/mixin/MixinNetHandlerPlayClient.java @@ -0,0 +1,28 @@ +package eu.crushedpixel.replaymod.mixin; + +import eu.crushedpixel.replaymod.ReplayMod; +import eu.crushedpixel.replaymod.recording.ConnectionEventHandler; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.network.play.server.S01PacketJoinGame; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(NetHandlerPlayClient.class) +public abstract class MixinNetHandlerPlayClient { + + /** + * Record the own player entity joining the world. + * We cannot use the {@link net.minecraftforge.event.entity.EntityJoinWorldEvent} because the entity id + * of the player is set afterwards. + * @param packet The packet + * @param ci Callback info + */ + @Inject(method = "handleJoinGame", at=@At("RETURN")) + public void recordJoinGame(S01PacketJoinGame packet, CallbackInfo ci) { + if (ConnectionEventHandler.isRecording()) { + ReplayMod.recordingHandler.onPlayerJoin(); + } + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java index 173f9971..a42f8aab 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplaySender.java @@ -5,7 +5,6 @@ import com.google.common.io.Files; import com.google.common.util.concurrent.ListenableFutureTask; import eu.crushedpixel.replaymod.ReplayMod; import eu.crushedpixel.replaymod.entities.CameraEntity; -import eu.crushedpixel.replaymod.events.handlers.RecordingHandler; import eu.crushedpixel.replaymod.holders.PacketData; import eu.crushedpixel.replaymod.utils.ReplayFile; import eu.crushedpixel.replaymod.utils.ReplayFileIO; @@ -40,6 +39,14 @@ import java.util.concurrent.Callable; @Sharable public class ReplaySender extends ChannelInboundHandlerAdapter { + /** + * Previously packets for the client player were inserted using one fixed entity id (this one). + * This is no longer the case however to provide backwards compatibility, we have to convert + * these old packets to use the normal entity id. + * Need to punch someone? -> CrushedPixel + */ + public static final int LEGACY_ENTITY_ID = Integer.MIN_VALUE + 9001; + /** * These packets are ignored completely during replay. */ @@ -291,6 +298,8 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } } + convertLegacyEntityIds(p); + if(p instanceof S48PacketResourcePackSend) { S48PacketResourcePackSend packet = (S48PacketResourcePackSend) p; String url = packet.func_179783_a(); @@ -311,33 +320,12 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } } - if(p instanceof S1CPacketEntityMetadata) { - S1CPacketEntityMetadata packet = (S1CPacketEntityMetadata) p; - if(packet.field_149379_a == actualID) { - packet.field_149379_a = RecordingHandler.entityID; - } - } - - if(p instanceof S1BPacketEntityAttach) { - S1BPacketEntityAttach packet = (S1BPacketEntityAttach) p; - if(packet.func_149403_d() == actualID) { - return null; - } - } - - if(p instanceof S19PacketEntityStatus) { - S19PacketEntityStatus packet = (S19PacketEntityStatus)p; - if(packet.field_149164_a == actualID) { - packet.field_149164_a = RecordingHandler.entityID; - } - } - if(p instanceof S01PacketJoinGame) { S01PacketJoinGame packet = (S01PacketJoinGame) p; allowMovement = true; int entId = packet.getEntityId(); actualID = entId; - entId = Integer.MIN_VALUE + 9002; + entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers int dimension = packet.getDimension(); EnumDifficulty difficulty = packet.getDifficulty(); int maxPlayers = packet.getMaxPlayers(); @@ -726,4 +714,65 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { return p; // During synchronous playback everything is sent normally } + /** + * This is necessary to convert packets from old replays to new replays. + * @param packet The packet to be transformed. + * @see #LEGACY_ENTITY_ID + */ + private void convertLegacyEntityIds(Packet packet) { + if (packet instanceof S0CPacketSpawnPlayer) { + S0CPacketSpawnPlayer p = (S0CPacketSpawnPlayer) packet; + if (p.field_148957_a == LEGACY_ENTITY_ID) { + p.field_148957_a = actualID; + } + } else if (packet instanceof S18PacketEntityTeleport) { + S18PacketEntityTeleport p = (S18PacketEntityTeleport) packet; + if (p.field_149458_a == LEGACY_ENTITY_ID) { + p.field_149458_a = actualID; + } + } else if (packet instanceof S14PacketEntity.S17PacketEntityLookMove) { + S14PacketEntity.S17PacketEntityLookMove p = (S14PacketEntity.S17PacketEntityLookMove) packet; + if (p.field_149074_a == LEGACY_ENTITY_ID) { + p.field_149074_a = actualID; + } + } else if (packet instanceof S19PacketEntityHeadLook) { + S19PacketEntityHeadLook p = (S19PacketEntityHeadLook) packet; + if (p.field_149384_a == LEGACY_ENTITY_ID) { + p.field_149384_a = actualID; + } + } else if (packet instanceof S12PacketEntityVelocity) { + S12PacketEntityVelocity p = (S12PacketEntityVelocity) packet; + if (p.field_149417_a == LEGACY_ENTITY_ID) { + p.field_149417_a = actualID; + } + } else if (packet instanceof S0BPacketAnimation) { + S0BPacketAnimation p = (S0BPacketAnimation) packet; + if (p.entityId == LEGACY_ENTITY_ID) { + p.entityId = actualID; + } + } else if (packet instanceof S04PacketEntityEquipment) { + S04PacketEntityEquipment p = (S04PacketEntityEquipment) packet; + if (p.field_149394_a == LEGACY_ENTITY_ID) { + p.field_149394_a = actualID; + } + } else if (packet instanceof S1BPacketEntityAttach) { + S1BPacketEntityAttach p = (S1BPacketEntityAttach) packet; + if (p.field_149408_a == LEGACY_ENTITY_ID) { + p.field_149408_a = actualID; + } + if (p.field_149406_b == LEGACY_ENTITY_ID) { + p.field_149406_b = actualID; + } + } else if (packet instanceof S0DPacketCollectItem) { + S0DPacketCollectItem p = (S0DPacketCollectItem) packet; + if (p.field_149356_b == LEGACY_ENTITY_ID) { + p.field_149356_b = actualID; + } + } else if (packet instanceof S13PacketDestroyEntities) { + S13PacketDestroyEntities p = (S13PacketDestroyEntities) packet; + if (p.field_149100_a.length == 1 && p.field_149100_a[0] == LEGACY_ENTITY_ID) { + p.field_149100_a[0] = actualID; + } + } + } } diff --git a/src/main/resources/META-INF/replaymod_at.cfg b/src/main/resources/META-INF/replaymod_at.cfg index 1f2cf852..a5d3c5f0 100644 --- a/src/main/resources/META-INF/replaymod_at.cfg +++ b/src/main/resources/META-INF/replaymod_at.cfg @@ -22,8 +22,6 @@ public net.minecraft.client.Minecraft func_147115_a(Z)V # sendClickBlockToContro # Packets public net.minecraft.network.play.server.S0FPacketSpawnMob field_149043_l # dataWatcher -public net.minecraft.network.play.server.S0CPacketSpawnPlayer field_148960_i # dataWatcher -public net.minecraft.network.play.server.S0CPacketSpawnPlayer field_179820_b # uuid public net.minecraft.network.play.server.S38PacketPlayerListItem field_179964_d # profile public net.minecraft.network.play.server.S1DPacketEntityEffect field_149434_a # entityId public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149379_a # entityId @@ -31,6 +29,15 @@ public net.minecraft.network.play.server.S1CPacketEntityMetadata field_149378_b public net.minecraft.network.play.server.S0BPacketAnimation field_148981_a # entityId public net.minecraft.network.play.server.S02PacketChat field_179842_b # position public net.minecraft.network.play.server.S19PacketEntityStatus field_149164_a # entityId +public net.minecraft.network.play.server.S0CPacketSpawnPlayer * +public net.minecraft.network.play.server.S18PacketEntityTeleport * +public net.minecraft.network.play.server.S14PacketEntity * +public net.minecraft.network.play.server.S19PacketEntityHeadLook * +public net.minecraft.network.play.server.S12PacketEntityVelocity * +public net.minecraft.network.play.server.S04PacketEntityEquipment * +public net.minecraft.network.play.server.S1BPacketEntityAttach * +public net.minecraft.network.play.server.S0DPacketCollectItem * +public net.minecraft.network.play.server.S13PacketDestroyEntities * # GuiTextField public net.minecraft.client.gui.GuiTextField field_146216_j # text diff --git a/src/main/resources/mixins.replaymod.json b/src/main/resources/mixins.replaymod.json index ccd467be..72ae3dcc 100644 --- a/src/main/resources/mixins.replaymod.json +++ b/src/main/resources/mixins.replaymod.json @@ -5,6 +5,7 @@ "MixinEffectRenderer", "MixinEntityRenderer", "MixinMinecraft", + "MixinNetHandlerPlayClient", "MixinPlayerControllerMP", "MixinRender", "MixinRenderArrow",