From b27af2781b651b2aff8f1219a4ef3086836cdaf4 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 28 Aug 2017 10:56:37 +0200 Subject: [PATCH] Fix player entities being half-despawned after jumping in time (fixes #93) --- .../com/replaymod/replay/ReplaySender.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/main/java/com/replaymod/replay/ReplaySender.java b/src/main/java/com/replaymod/replay/ReplaySender.java index 8a42426b..9e000d3a 100755 --- a/src/main/java/com/replaymod/replay/ReplaySender.java +++ b/src/main/java/com/replaymod/replay/ReplaySender.java @@ -19,16 +19,22 @@ import net.minecraft.client.resources.I18n; import net.minecraft.entity.Entity; import net.minecraft.network.*; import net.minecraft.network.play.server.*; +import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.World; import net.minecraft.world.WorldSettings.GameType; import net.minecraft.world.WorldType; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import java.io.*; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -799,6 +805,49 @@ public class ReplaySender extends ChannelDuplexHandler { } protected Packet processPacketSync(Packet p) { + if (p instanceof S21PacketChunkData) { + S21PacketChunkData packet = (S21PacketChunkData) p; + if (packet.func_149276_g() == 0) { + // If the chunk is getting unloaded, we will have to forcefully update the position of all entities + // within. Otherwise, if there wasn't a game tick recently, there may be entities that have moved + // out of the chunk by now but are still registered in it. If we do not update those, they will get + // unloaded even though they shouldn't. + // To make things worse, it seems like players were never supposed to be unloaded this way because + // they will remain glitched in the World#playerEntities list. + World world = mc.theWorld; + IChunkProvider chunkProvider = world.getChunkProvider(); + // Get the chunk that will be unloaded + Chunk chunk = chunkProvider.provideChunk(packet.func_149273_e(), packet.func_149271_f()); + if (!chunk.isEmpty()) { + List entitiesInChunk = new ArrayList<>(); + // Gather all entities in that chunk + for (ClassInheritanceMultiMap entityList : chunk.getEntityLists()) { + @SuppressWarnings("unchecked") + Collection typedEntityList = entityList; + entitiesInChunk.addAll(typedEntityList); + } + for (Entity entity : entitiesInChunk) { + // Skip interpolation of position updates coming from server + // (See: newX in EntityLivingBase or otherPlayerMPX in EntityOtherPlayerMP) + entity.onUpdate(); + + // Check whether the entity has left the chunk + int chunkX = MathHelper.floor_double(entity.posX / 16); + int chunkZ = MathHelper.floor_double(entity.posZ / 16); + if (entity.chunkCoordX != chunkX || entity.chunkCoordZ != chunkZ) { + // Entity has left the chunk + chunk.removeEntityAtIndex(entity, entity.chunkCoordY); + if (chunkProvider.chunkExists(chunkX, chunkZ)) { + chunkProvider.provideChunk(chunkX, chunkZ).addEntity(entity); + } else { + // Entity has left all loaded chunks + entity.addedToChunk = false; + } + } + } + } + } + } return p; // During synchronous playback everything is sent normally }