diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 4a4c71ce..86cf4877 100755 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -26,6 +26,9 @@ import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.EventBus; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; import org.apache.commons.io.FileUtils; import java.io.File; @@ -306,11 +309,38 @@ public class ReplayMod { }); } + /** + * Set when the currently running code has been scheduled by runLater. + * If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been + * processed, otherwise a livelock may occur. + */ + private boolean inRunLater = false; + public void runLater(Runnable runnable) { + if (mc.isCallingFromMinecraftThread() && inRunLater) { + EventBus bus = FMLCommonHandler.instance().bus(); + bus.register(new Object() { + @SubscribeEvent + public void onRenderTick(TickEvent.RenderTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + runLater(runnable); + bus.unregister(this); + } + } + }); + return; + } @SuppressWarnings("unchecked") Queue tasks = mc.scheduledTasks; synchronized (mc.scheduledTasks) { - tasks.add(ListenableFutureTask.create(runnable, null)); + tasks.add(ListenableFutureTask.create(() -> { + inRunLater = true; + try { + runnable.run(); + } finally { + inRunLater = false; + } + }, null)); } } diff --git a/src/main/java/com/replaymod/replay/ReplaySender.java b/src/main/java/com/replaymod/replay/ReplaySender.java index fc41cf77..09cfa9d3 100755 --- a/src/main/java/com/replaymod/replay/ReplaySender.java +++ b/src/main/java/com/replaymod/replay/ReplaySender.java @@ -2,7 +2,7 @@ package com.replaymod.replay; import com.google.common.base.Preconditions; import com.google.common.io.Files; -import com.google.common.util.concurrent.ListenableFutureTask; +import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.Restrictions; import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replaystudio.replay.ReplayFile; @@ -30,7 +30,6 @@ import java.io.*; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; /** * Sends replay packets to netty channels. @@ -440,22 +439,19 @@ public class ReplaySender extends ChannelInboundHandlerAdapter { } } - new Callable() { + new Runnable() { @Override @SuppressWarnings("unchecked") - public Void call() { + public void run() { if (mc.theWorld == null || !mc.isCallingFromMinecraftThread()) { - synchronized(mc.scheduledTasks) { - mc.scheduledTasks.add(ListenableFutureTask.create(this)); - } - return null; + ReplayMod.instance.runLater(this); + return; } CameraEntity cent = replayHandler.getCameraEntity(); cent.setCameraPosition(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e()); - return null; } - }.call(); + }.run(); } if(p instanceof S2BPacketChangeGameState) {