diff --git a/build.gradle b/build.gradle index 6f84a650..a9783cc0 100755 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ configurations { dependencies { compile 'org.projectlombok:lombok:1.16.4' - compile 'org.spongepowered:mixin:0.6.8-SNAPSHOT' + compile 'org.spongepowered:mixin:0.7.5-SNAPSHOT' shade 'com.googlecode.mp4parser:isoparser:1.1.7' shade 'org.apache.commons:commons-exec:1.3' shade 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0' diff --git a/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java b/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java new file mode 100644 index 00000000..6347c057 --- /dev/null +++ b/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java @@ -0,0 +1,35 @@ +package com.replaymod.compat.mapwriter.mixin; + +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraftforge.fml.common.Loader; +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.CallbackInfoReturnable; + +/** + * Approximately this for <1.12: https://github.com/Vectron/mapwriter/commit/68234520c7a3a0ae8201a085d7e66369900586ac + */ +@Mixin(Minecraft.class) +public abstract class MixinMinecraft { + + @Shadow + private ServerData currentServerData; + + @Inject(method = "getCurrentServerData", cancellable = true, at = @At("HEAD")) + private void replayModCompat_fixBug96(CallbackInfoReturnable ci) { + if (currentServerData == null + && (Loader.isModLoaded("mapwriter") || Loader.isModLoaded("MapWriter")) + && ReplayModReplay.instance.getReplayHandler() != null) { + for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { + if ("mapwriter.util.Utils".equals(elem.getClassName()) && "getWorldName".equals(elem.getMethodName())) { + ci.setReturnValue(new ServerData(null, "replay")); + return; + } + } + } + } +} diff --git a/src/main/java/com/replaymod/core/LoadingPlugin.java b/src/main/java/com/replaymod/core/LoadingPlugin.java index 9a251ed9..ebc8df0b 100755 --- a/src/main/java/com/replaymod/core/LoadingPlugin.java +++ b/src/main/java/com/replaymod/core/LoadingPlugin.java @@ -19,6 +19,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin { Mixins.addConfiguration("mixins.recording.replaymod.json"); Mixins.addConfiguration("mixins.render.replaymod.json"); Mixins.addConfiguration("mixins.replay.replaymod.json"); + Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json"); Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json"); diff --git a/src/main/java/com/replaymod/replay/ReplaySender.java b/src/main/java/com/replaymod/replay/ReplaySender.java index 8a42426b..92d97bd4 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,53 @@ 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) + // Needs to be called at least 4 times thanks to + // EntityOtherPlayerMP#otherPlayerMPPosRotationIncrements (max vanilla value is 3) + for (int i = 0; i < 4; i++) { + 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 } diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 5db8ecb2..9935baf5 100755 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -160,6 +160,11 @@ public class CameraEntity extends EntityPlayerSP { if (spectating != null && (view.getUniqueID() != spectating || view.worldObj != worldObj) || worldObj.getEntityByID(view.getEntityId()) != view) { + if (spectating == null) { + // Entity (non-player) died, stop spectating + ReplayModReplay.instance.getReplayHandler().spectateEntity(this); + return; + } view = worldObj.getPlayerEntityByUUID(spectating); if (view != null) { mc.setRenderViewEntity(view); diff --git a/src/main/resources/mixins.compat.mapwriter.replaymod.json b/src/main/resources/mixins.compat.mapwriter.replaymod.json new file mode 100644 index 00000000..eb02cea2 --- /dev/null +++ b/src/main/resources/mixins.compat.mapwriter.replaymod.json @@ -0,0 +1,12 @@ +{ + "required": false, + "package": "com.replaymod.compat.mapwriter.mixin", + "mixins": [], + "server": [], + "client": [ + "MixinMinecraft" + ], + "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", + "refmap": "mixins.replaymod.refmap.json" +} \ No newline at end of file diff --git a/src/main/resources/mixins.compat.shaders.replaymod.json b/src/main/resources/mixins.compat.shaders.replaymod.json index 792af576..ed6e1a9a 100644 --- a/src/main/resources/mixins.compat.shaders.replaymod.json +++ b/src/main/resources/mixins.compat.shaders.replaymod.json @@ -10,5 +10,6 @@ "MixinShadersRender" ], "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file diff --git a/src/main/resources/mixins.extras.playeroverview.replaymod.json b/src/main/resources/mixins.extras.playeroverview.replaymod.json index 7c9a0a1f..9c9bf8e7 100644 --- a/src/main/resources/mixins.extras.playeroverview.replaymod.json +++ b/src/main/resources/mixins.extras.playeroverview.replaymod.json @@ -7,5 +7,6 @@ "MixinRender" ], "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file diff --git a/src/main/resources/mixins.recording.replaymod.json b/src/main/resources/mixins.recording.replaymod.json index 95a6738d..75e77050 100644 --- a/src/main/resources/mixins.recording.replaymod.json +++ b/src/main/resources/mixins.recording.replaymod.json @@ -10,5 +10,6 @@ "MixinRenderGlobal" ], "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file diff --git a/src/main/resources/mixins.render.replaymod.json b/src/main/resources/mixins.render.replaymod.json index a2e940c8..539d1c55 100644 --- a/src/main/resources/mixins.render.replaymod.json +++ b/src/main/resources/mixins.render.replaymod.json @@ -13,5 +13,6 @@ "server": [], "client": [], "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file diff --git a/src/main/resources/mixins.replay.replaymod.json b/src/main/resources/mixins.replay.replaymod.json index 8d02ed09..e92257fb 100644 --- a/src/main/resources/mixins.replay.replaymod.json +++ b/src/main/resources/mixins.replay.replaymod.json @@ -15,5 +15,6 @@ "server": [], "client": [], "compatibilityLevel": "JAVA_8", + "minVersion": "0.6.11", "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file