From 9add2afec11a57b47a9623a908430476af61cb28 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 12 Nov 2016 17:31:29 +0100 Subject: [PATCH 01/12] Deselect keyframe when pressing "V" aka. sync timelines (fixes #27) --- src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index c1a4e69b..3f1bb6b1 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -452,6 +452,8 @@ public class GuiPathing { int cursorPassed = (int) (timePassed / speed); // Move cursor to new position timeline.setCursorPosition(keyframeCursor + cursorPassed); + // Deselect keyframe to allow the user to add a new one right away + mod.setSelectedKeyframe(null); }); }); From 6efbf919dbcca342daf26e19556641343b71b5f9 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 12 Nov 2016 18:51:41 +0100 Subject: [PATCH 02/12] Handle F1 properly while mouse is visible in the replay overlay (fixes #30) --- .../replay/gui/overlay/GuiReplayOverlay.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java index 97849c6a..63c4326d 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java @@ -19,6 +19,7 @@ import net.minecraft.client.settings.GameSettings; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.InputEvent; +import org.lwjgl.input.Keyboard; import org.lwjgl.util.ReadableDimension; import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.WritablePoint; @@ -153,6 +154,23 @@ public class GuiReplayOverlay extends AbstractGuiOverlay { setMouseVisible(true); } } + if (Keyboard.getEventKeyState()) { + // Handle the F1 key binding while the overlay is opened as a gui screen + if (isMouseVisible() && Keyboard.getEventKey() == Keyboard.KEY_F1) { + gameSettings.hideGUI = !gameSettings.hideGUI; + } + } + } + + @Override + public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { + // Do not render overlay when user pressed F1 and we are not currently in some popup + if (getMinecraft().gameSettings.hideGUI && isAllowUserInput()) { + // Note that this only applies to when the mouse is visible, otherwise + // the draw method isn't called in the first place + return; + } + super.draw(renderer, size, renderInfo); } @Override From c934cb9694e84b97acd2fdd40261b78a1149210d Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 13 Nov 2016 17:07:34 +0100 Subject: [PATCH 03/12] Fix spawn player packet being sent twice The vanilla server sends the player list packet with ADD action twice to the joining player, however we must only send the spawn player packet once. This also fixes semi-dead player entities in replays that just existed at spawn but never did anything. --- .../mixin/MixinNetHandlerPlayClient.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java index baf316ed..bd5d4a99 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java @@ -3,6 +3,7 @@ package com.replaymod.recording.mixin; import com.replaymod.recording.handler.RecordingEventHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.network.play.server.S07PacketRespawn; import net.minecraft.network.play.server.S38PacketPlayerListItem; import org.spongepowered.asm.mixin.Mixin; @@ -12,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.List; +import java.util.Map; +import java.util.UUID; @Mixin(NetHandlerPlayClient.class) public abstract class MixinNetHandlerPlayClient { @@ -19,6 +22,9 @@ public abstract class MixinNetHandlerPlayClient { @Shadow private Minecraft gameController; + @Shadow + private Map playerInfoMap; + public RecordingEventHandler getRecordingEventHandler() { return ((RecordingEventHandler.RecordingEventSender) gameController.renderGlobal).getRecordingEventHandler(); } @@ -30,14 +36,19 @@ public abstract class MixinNetHandlerPlayClient { * @param packet The packet * @param ci Callback info */ - @Inject(method = "handlePlayerListItem", at=@At("RETURN")) + @Inject(method = "handlePlayerListItem", at=@At("HEAD")) public void recordOwnJoin(S38PacketPlayerListItem packet, CallbackInfo ci) { + if (gameController.thePlayer == null) return; + RecordingEventHandler handler = getRecordingEventHandler(); if (handler != null && packet.func_179768_b() == S38PacketPlayerListItem.Action.ADD_PLAYER) { @SuppressWarnings("unchecked") List dataList = packet.func_179767_a(); for (S38PacketPlayerListItem.AddPlayerData data : dataList) { - if (data.func_179962_a().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId())) { + if (data.func_179962_a() == null || data.func_179962_a().getId() == null) continue; + // Only add spawn packet for our own player and only if he isn't known yet + if (data.func_179962_a().getId().equals(Minecraft.getMinecraft().thePlayer.getGameProfile().getId()) + && !playerInfoMap.containsKey(data.func_179962_a().getId())) { handler.onPlayerJoin(); } } From ad2893bf4ee41a9364e4a87bfad7e7921cda92cb Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 13 Nov 2016 18:54:52 +0100 Subject: [PATCH 04/12] Fix vanilla bug caused by unremovable entities in the entityList of ClientWorld (fixes #29) --- .../replay/mixin/MixinWorldClient.java | 31 +++++++++++++++++++ .../resources/mixins.replay.replaymod.json | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java diff --git a/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java b/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java new file mode 100644 index 00000000..643ac038 --- /dev/null +++ b/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java @@ -0,0 +1,31 @@ +package com.replaymod.replay.mixin; + +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.entity.Entity; +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(WorldClient.class) +public abstract class MixinWorldClient { + + /** + * Fixes a bug in vanilla Minecraft that leaves entities remaining in the entityList even after respawn. + * The entityList in WorldClient is a Set that assumes that the entity ID of its entities does not change. + * However in {@link WorldClient#addEntityToWorld(int, Entity)}, the entity passed in is first added to the + * set and subsequently its id is changed, leaving it stuck in he Set. That is the buggy method. + * This wouldn't be too much of a problem if the entity had the correct id to begin with, however the handler for + * the spawn player packet creates an EntityOtherPlayerMP which takes its id from a counter and then passes it to + * this method with the wrong id. + * This mixin fixes the id of the entity before it is added to the set instead of right after. + * The original id change right after is not changed, however it should not have any effect. + * @param entityId The id to be set for the entity + * @param entity The entity to be added + * @param ci Callback info + */ + @Inject(method = "addEntityToWorld", at=@At("HEAD")) + public void replayModReplay_fix_addEntityToWorld(int entityId, Entity entity, CallbackInfo ci) { + entity.setEntityId(entityId); + } +} diff --git a/src/main/resources/mixins.replay.replaymod.json b/src/main/resources/mixins.replay.replaymod.json index b18e7b9f..2c1453f8 100644 --- a/src/main/resources/mixins.replay.replaymod.json +++ b/src/main/resources/mixins.replay.replaymod.json @@ -10,7 +10,8 @@ "MixinRenderItem", "MixinRenderManager", "MixinTileEntityEndPortalRenderer", - "MixinViewFrustum" + "MixinViewFrustum", + "MixinWorldClient" ], "server": [], "client": [], From 1f2c05e696219ab718cfd9f34aa6c6e5eacfbbc6 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 13 Nov 2016 17:09:37 +0100 Subject: [PATCH 05/12] Fix spectating player entities past death and respawn (fixes #36) While spectating, a special camera controller should be active. However after a world change (which happens when the player dies), the camera is recreated with the default controller, same when the controller setting is changed. Also fixes the spectated entity if the player respawns in the same dimension --- .../replaymod/replay/camera/CameraEntity.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index dc20471b..01bb4a99 100755 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -69,7 +69,11 @@ public class CameraEntity extends EntityPlayerSP { super(mcIn, worldIn, netHandlerPlayClient, statFileWriter); FMLCommonHandler.instance().bus().register(eventHandler); MinecraftForge.EVENT_BUS.register(eventHandler); - cameraController = ReplayModReplay.instance.createCameraController(this); + if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) { + cameraController = ReplayModReplay.instance.createCameraController(this); + } else { + cameraController = new SpectatorCameraController(this); + } } /** @@ -151,7 +155,9 @@ public class CameraEntity extends EntityPlayerSP { // This is important if the spectated player respawns as their // entity is recreated and we have to spectate a new entity UUID spectating = ReplayModReplay.instance.getReplayHandler().getSpectatedUUID(); - if (spectating != null && (view.getUniqueID() != spectating || view.worldObj != worldObj)) { + if (spectating != null && (view.getUniqueID() != spectating + || view.worldObj != worldObj) + || worldObj.getEntityByID(view.getEntityId()) != view) { view = worldObj.getPlayerEntityByUUID(spectating); if (view != null) { mc.setRenderViewEntity(view); @@ -354,7 +360,11 @@ public class CameraEntity extends EntityPlayerSP { @SubscribeEvent public void onSettingsChanged(SettingsChangedEvent event) { if (event.getKey() == Setting.CAMERA) { - cameraController = ReplayModReplay.instance.createCameraController(CameraEntity.this); + if (ReplayModReplay.instance.getReplayHandler().getSpectatedUUID() == null) { + cameraController = ReplayModReplay.instance.createCameraController(CameraEntity.this); + } else { + cameraController = new SpectatorCameraController(CameraEntity.this); + } } } From d64ef8b2e16f1b92599f56f81d193ccdf5ebc774 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 13 Nov 2016 21:42:33 +0100 Subject: [PATCH 06/12] Hide NPCs in the Player Overview GUI (fixes #29) NPCs are now by default removed from the Player Overview GUI unless CTRL is held while the GUI is opened. NPCs are identified my their v2 UUID. --- .../extras/playeroverview/PlayerOverview.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java index fd1f1507..89c96a9b 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java @@ -3,6 +3,7 @@ package com.replaymod.extras.playeroverview; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.replaymod.core.ReplayMod; +import com.replaymod.core.utils.Utils; import com.replaymod.extras.Extra; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.camera.CameraEntity; @@ -39,6 +40,16 @@ public class PlayerOverview implements Extra { return !(input instanceof CameraEntity); // Exclude the camera entity } }); + if (!Utils.isCtrlDown()) { + // Hide all players that have an UUID v2 (commonly used for NPCs) + Iterator iter = players.iterator(); + while (iter.hasNext()) { + UUID uuid = iter.next().getGameProfile().getId(); + if (uuid != null && uuid.version() == 2) { + iter.remove(); + } + } + } new PlayerOverviewGui(PlayerOverview.this, players).display(); } } From a817c7367a0cf4ae2a4d6a24309be7b1f4d40965 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Wed, 16 Nov 2016 18:18:55 +0100 Subject: [PATCH 07/12] Prevent GUIs of other mods from opening during replay --- src/main/java/com/replaymod/replay/camera/CameraEntity.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 01bb4a99..d7351cc2 100755 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -291,6 +291,12 @@ public class CameraEntity extends EntityPlayerSP { return pos; } + @Override + public void openGui(Object mod, int modGuiId, World world, int x, int y, int z) { + // Do not open any block GUIs for the camera entities + // Note: Vanilla GUIs are filtered out on a packet level, this only applies to mod GUIs + } + @Override public void setDead() { super.setDead(); From 50b53fc6a3bd96b94ccc1c96a56810962b7fcd11 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Thu, 24 Nov 2016 21:31:21 +0100 Subject: [PATCH 08/12] Add a way to register key bindings triggered every render tick (fixes #31) --- .../replaymod/core/KeyBindingRegistry.java | 57 +++++++++++++------ .../simplepathing/gui/GuiPathing.java | 2 +- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/replaymod/core/KeyBindingRegistry.java b/src/main/java/com/replaymod/core/KeyBindingRegistry.java index 65acb418..8231700c 100644 --- a/src/main/java/com/replaymod/core/KeyBindingRegistry.java +++ b/src/main/java/com/replaymod/core/KeyBindingRegistry.java @@ -9,6 +9,7 @@ import net.minecraft.util.ReportedException; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.InputEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; import org.lwjgl.input.Keyboard; import java.util.Collection; @@ -20,16 +21,25 @@ import java.util.concurrent.Callable; public class KeyBindingRegistry { private Map keyBindings = new HashMap(); private Multimap keyBindingHandlers = ArrayListMultimap.create(); + private Multimap repeatedKeyBindingHandlers = ArrayListMultimap.create(); private Multimap rawHandlers = ArrayListMultimap.create(); public void registerKeyBinding(String name, int keyCode, Runnable whenPressed) { + keyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed); + } + + public void registerRepeatedKeyBinding(String name, int keyCode, Runnable whenPressed) { + repeatedKeyBindingHandlers.put(registerKeyBinding(name, keyCode), whenPressed); + } + + private KeyBinding registerKeyBinding(String name, int keyCode) { KeyBinding keyBinding = keyBindings.get(name); if (keyBinding == null) { keyBinding = new KeyBinding(name, keyCode, "replaymod.title"); keyBindings.put(name, keyBinding); ClientRegistry.registerKeyBinding(keyBinding); } - keyBindingHandlers.put(keyBinding, whenPressed); + return keyBinding; } public void registerRaw(int keyCode, Runnable whenPressed) { @@ -46,25 +56,38 @@ public class KeyBindingRegistry { handleRaw(); } + @SubscribeEvent + public void onTick(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + handleRepeatedKeyBindings(); + } + + public void handleRepeatedKeyBindings() { + for (Map.Entry> entry : repeatedKeyBindingHandlers.asMap().entrySet()) { + if (entry.getKey().isKeyDown()) { + invokeKeyBindingHandlers(entry.getKey(), entry.getValue()); + } + } + } + public void handleKeyBindings() { for (Map.Entry> entry : keyBindingHandlers.asMap().entrySet()) { while (entry.getKey().isPressed()) { - for (final Runnable runnable : entry.getValue()) { - try { - runnable.run(); - } catch (Throwable cause) { - CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding"); - CrashReportCategory category = crashReport.makeCategory("Key Binding"); - category.addCrashSection("Key Binding", entry.getKey()); - category.addCrashSectionCallable("Handler", new Callable() { - @Override - public Object call() throws Exception { - return runnable; - } - }); - throw new ReportedException(crashReport); - } - } + invokeKeyBindingHandlers(entry.getKey(), entry.getValue()); + } + } + } + + private void invokeKeyBindingHandlers(KeyBinding keyBinding, Collection handlers) { + for (final Runnable runnable : handlers) { + try { + runnable.run(); + } catch (Throwable cause) { + CrashReport crashReport = CrashReport.makeCrashReport(cause, "Handling Key Binding"); + CrashReportCategory category = crashReport.makeCategory("Key Binding"); + category.addCrashSection("Key Binding", keyBinding); + category.addCrashSectionCallable("Handler", runnable::toString); + throw new ReportedException(crashReport); } } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index 3f1bb6b1..91815070 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -431,7 +431,7 @@ public class GuiPathing { }); }); - core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> { + core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> { // Current replay time int time = replayHandler.getReplaySender().currentTimeStamp(); // Position of the cursor From e6a789d4f885dc087a8a117bd96bf1809bb30dc8 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 26 Nov 2016 12:56:23 +0100 Subject: [PATCH 09/12] Fix camera rotation when jumping in time (fixes #39) --- src/main/java/com/replaymod/replay/ReplayHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index 7646205d..6d867cf4 100755 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -280,7 +280,7 @@ public class ReplayHandler { CameraEntity cam = getCameraEntity(); if (cam != null) { targetCameraPosition = new Location(cam.posX, cam.posY, cam.posZ, - cam.rotationPitch, cam.rotationYaw); + cam.rotationYaw, cam.rotationPitch); } else { targetCameraPosition = null; } From 00bcc9e65ef55e7f229ef576b938f23061f60a20 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 26 Nov 2016 13:26:53 +0100 Subject: [PATCH 10/12] Fix paths of files that are supposed to be in the mc data dir (fixes #40) --- src/main/java/com/replaymod/core/ReplayMod.java | 3 ++- .../com/replaymod/online/ReplayModOnline.java | 7 +++++-- .../com/replaymod/render/ReplayModRender.java | 17 +++++++++++++++++ .../java/com/replaymod/render/VideoWriter.java | 3 ++- .../replaymod/render/gui/GuiRenderSettings.java | 3 +-- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 4d2230bd..97a1f656 100755 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -85,7 +85,8 @@ public class ReplayMod { } public File getReplayFolder() throws IOException { - File folder = new File(getSettingsRegistry().get(Setting.RECORDING_PATH)); + String path = getSettingsRegistry().get(Setting.RECORDING_PATH); + File folder = new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path); FileUtils.forceMkdir(folder); return folder; } diff --git a/src/main/java/com/replaymod/online/ReplayModOnline.java b/src/main/java/com/replaymod/online/ReplayModOnline.java index 365aae19..d4f750f2 100644 --- a/src/main/java/com/replaymod/online/ReplayModOnline.java +++ b/src/main/java/com/replaymod/online/ReplayModOnline.java @@ -8,10 +8,10 @@ import com.replaymod.online.gui.GuiSaveModifiedReplay; import com.replaymod.online.handler.GuiHandler; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.events.ReplayCloseEvent; -import de.johni0702.minecraft.gui.container.GuiScreen; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.studio.ReplayStudio; +import de.johni0702.minecraft.gui.container.GuiScreen; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.Mod; @@ -24,6 +24,8 @@ import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; +import static net.minecraft.client.Minecraft.getMinecraft; + @Mod(modid = ReplayModOnline.MOD_ID, useMetadata = true) public class ReplayModOnline { public static final String MOD_ID = "replaymod-online"; @@ -101,7 +103,8 @@ public class ReplayModOnline { } public File getDownloadsFolder() { - return new File(core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH)); + String path = core.getSettingsRegistry().get(Setting.DOWNLOAD_PATH); + return new File(path.startsWith("./") ? getMinecraft().mcDataDir : null, path); } public File getDownloadedFile(int id) { diff --git a/src/main/java/com/replaymod/render/ReplayModRender.java b/src/main/java/com/replaymod/render/ReplayModRender.java index ade88210..6ddb36ae 100644 --- a/src/main/java/com/replaymod/render/ReplayModRender.java +++ b/src/main/java/com/replaymod/render/ReplayModRender.java @@ -1,11 +1,17 @@ package com.replaymod.render; import com.replaymod.core.ReplayMod; +import net.minecraft.crash.CrashReport; +import net.minecraft.util.ReportedException; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.Logger; +import java.io.File; +import java.io.IOException; + @Mod(modid = ReplayModRender.MOD_ID, useMetadata = true) public class ReplayModRender { public static final String MOD_ID = "replaymod-render"; @@ -32,6 +38,17 @@ public class ReplayModRender { core.getSettingsRegistry().register(Setting.class); } + public File getVideoFolder() { + String path = core.getSettingsRegistry().get(Setting.RENDER_PATH); + File folder = new File(path.startsWith("./") ? core.getMinecraft().mcDataDir : null, path); + try { + FileUtils.forceMkdir(folder); + } catch (IOException e) { + throw new ReportedException(CrashReport.makeCrashReport(e, "Cannot create video folder.")); + } + return folder; + } + public Configuration getConfiguration() { return configuration; } diff --git a/src/main/java/com/replaymod/render/VideoWriter.java b/src/main/java/com/replaymod/render/VideoWriter.java index 66665768..1e98ab37 100755 --- a/src/main/java/com/replaymod/render/VideoWriter.java +++ b/src/main/java/com/replaymod/render/VideoWriter.java @@ -49,7 +49,8 @@ public class VideoWriter implements FrameConsumer { System.out.println("Starting " + settings.getExportCommand() + " with args: " + commandArgs); String[] cmdline = new CommandLine(executable).addArguments(commandArgs).toStrings(); process = new ProcessBuilder(cmdline).directory(outputFolder).start(); - OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream("export.log"), ffmpegLog); + File exportLogFile = new File(Minecraft.getMinecraft().mcDataDir, "export.log"); + OutputStream exportLogOut = new TeeOutputStream(new FileOutputStream(exportLogFile), ffmpegLog); new StreamPipe(process.getInputStream(), exportLogOut).start(); new StreamPipe(process.getErrorStream(), exportLogOut).start(); outputStream = process.getOutputStream(); diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java index a9be9391..247db259 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java @@ -7,7 +7,6 @@ import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; import com.replaymod.render.RenderSettings; import com.replaymod.render.ReplayModRender; -import com.replaymod.render.Setting; import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.replay.ReplayHandler; import com.replaymod.replaystudio.pathing.path.Timeline; @@ -410,7 +409,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable { private File generateOutputFile(RenderSettings.EncodingPreset encodingPreset) { String fileName = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date()); - File folder = new File(ReplayModRender.instance.getCore().getSettingsRegistry().get(Setting.RENDER_PATH)); + File folder = ReplayModRender.instance.getVideoFolder(); return new File(folder, fileName + "." + encodingPreset.getFileExtension()); } From 61d6fd41621a3cc401e76d4b1e75087e5187868d Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 26 Nov 2016 13:43:38 +0100 Subject: [PATCH 11/12] Close replay file zip after recording (fixes #32) --- src/main/java/com/replaymod/recording/packet/PacketListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index 89c75c90..cd07c6e0 100755 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -129,6 +129,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { synchronized (replayFile) { try { replayFile.save(); + replayFile.close(); } catch (IOException e) { logger.error("Saving replay file:", e); } From 5b3284f8f782775104ac7fc5b3d56b4f039c8d0d Mon Sep 17 00:00:00 2001 From: johni0702 Date: Tue, 29 Nov 2016 22:24:54 +0100 Subject: [PATCH 12/12] Update jGui and ReplayStudio (fixes #33) --- ReplayStudio | 2 +- jGui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ReplayStudio b/ReplayStudio index 9591df75..3f396e9e 160000 --- a/ReplayStudio +++ b/ReplayStudio @@ -1 +1 @@ -Subproject commit 9591df75a1a5916f495a3c85757346fd8276ad8d +Subproject commit 3f396e9e6f1c43c24be7f9c3364a05f3b53d6b6d diff --git a/jGui b/jGui index 163cfe2f..4e3caf85 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit 163cfe2f8a9caa6792dc262c139afc066d371cfb +Subproject commit 4e3caf85569c322951b7aa53cdbc9a2d548768e9