From 6d0cdfbcb478b04daab23e3d8f3c183f87e1c260 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Tue, 18 Oct 2016 11:02:46 +0200 Subject: [PATCH 01/25] Update jGui (fixes #15) --- jGui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jGui b/jGui index fc13c5f7..f3e0e3b2 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit fc13c5f73c0ad214108f9575839d20449c2ceb00 +Subproject commit f3e0e3b2712cdd1c46dda00a825610d7dfbe5cd7 From e35bfcaa5886923c9c32e5e3b9be1d34c4bc4cce Mon Sep 17 00:00:00 2001 From: johni0702 Date: Wed, 19 Oct 2016 13:43:03 +0200 Subject: [PATCH 02/25] Increase spacing between rows in the render settings gui --- src/main/java/com/replaymod/render/gui/GuiRenderSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java index 3e3ddbdb..aa822cf6 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java @@ -130,7 +130,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable { new GuiPanel().addElements(null, bitRateField, bitRateUnit).setLayout(new HorizontalLayout()), frameRateSlider).setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(3)), new GuiLabel().setI18nText("replaymod.gui.rendersettings.outputfile"), outputFileButton) - .setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(3)); + .setLayout(new GridLayout().setCellsEqualSize(false).setColumns(2).setSpacingX(5).setSpacingY(5)); public final GuiCheckbox nametagCheckbox = new GuiCheckbox() .setI18nLabel("replaymod.gui.rendersettings.nametags"); From dd0537b8fd95be69db3951d9cf0aa66f9a6dcabc Mon Sep 17 00:00:00 2001 From: johni0702 Date: Wed, 19 Oct 2016 13:45:19 +0200 Subject: [PATCH 03/25] Center star between resolution fields in render settings gui --- src/main/java/com/replaymod/render/gui/GuiRenderSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java index aa822cf6..12cee846 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java @@ -91,7 +91,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable { }).setSize(122, 20).setSteps(110); public final GuiPanel videoResolutionPanel = new GuiPanel() .setLayout(new HorizontalLayout(HorizontalLayout.Alignment.RIGHT).setSpacing(2)) - .addElements(null, videoWidth, new GuiLabel().setText("*"), videoHeight); + .addElements(new HorizontalLayout.Data(0.5), videoWidth, new GuiLabel().setText("*"), videoHeight); public final GuiNumberField bitRateField = new GuiNumberField().setValue(10).setSize(50, 20); public final GuiDropdownMenu bitRateUnit = new GuiDropdownMenu() From b5a6d8b3072fc40d1147bd6b9b47a3b1e1e15558 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 16:10:03 +0200 Subject: [PATCH 04/25] Fix camera player head being visible while in third person --- .../java/com/replaymod/replay/camera/CameraEntity.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 4674c4c9..656b3951 100755 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -237,6 +237,14 @@ public class CameraEntity extends EntityPlayerSP { return ReplayModReplay.instance.getReplayHandler().isCameraView(); // Make sure we're treated as spectator } + @Override + public boolean shouldRenderInPass(int pass) { + // Never render the camera + // This is necessary to hide the player head in third person mode and to not + // cause any unwanted shadows when rendering with shaders. + return false; + } + @Override public ResourceLocation getLocationSkin() { Entity view = mc.getRenderViewEntity(); From 21bbeb2235671697a0a3ef05d8702ca16d8c2f56 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 19:37:33 +0200 Subject: [PATCH 05/25] Fix bitrate field always being active regardless of selected preset (fixes #20) --- .../java/com/replaymod/render/gui/GuiRenderSettings.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java index 12cee846..a9be9391 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java @@ -284,6 +284,15 @@ public class GuiRenderSettings extends GuiScreen implements Closeable { videoHeight.setTextColor(Colors.RED); } + // Enable/Disable bitrate input field and dropdown + if (encodingPresetDropdown.getSelectedValue().hasBitrateSetting()) { + bitRateField.setEnabled(); + bitRateUnit.setEnabled(); + } else { + bitRateField.setDisabled(); + bitRateUnit.setDisabled(); + } + // Enable/Disable camera stabilization checkboxes switch (renderMethodDropdown.getSelectedValue()) { case CUBIC: From 4e6b387f974880d38000ad885a732d7c4adc09ff Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 21:09:52 +0200 Subject: [PATCH 06/25] Update jGui Fix popups being drawn outside of the actually visible element (fixes #21) --- jGui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jGui b/jGui index f3e0e3b2..0dfcc65d 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit f3e0e3b2712cdd1c46dda00a825610d7dfbe5cd7 +Subproject commit 0dfcc65d695d0fed7242ccea4ade580ebe69e8cb From b2c7faac6f2cf2aba59788c805cc3497d139e138 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 21:22:36 +0200 Subject: [PATCH 07/25] Fix marker not being deselected when clicking anywhere else --- .../com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java index 2ad67f89..d3219d2c 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiMarkerTimeline.java @@ -133,6 +133,8 @@ public class GuiMarkerTimeline extends AbstractGuiTimeline im replayHandler.doJump(marker.getTime(), false); } return true; + } else { + selectedMarker = null; } return super.mouseClick(position, button); } From d547098ce85c202365c454710b7cbbdc6adee5f5 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 22:13:51 +0200 Subject: [PATCH 08/25] Fix markers not being removed Markers were saved in a (Hash)Set even though they are mutable. Reducing the exposed interface to Collection allows changing the interal implementation to ArrayList which solves the problem. --- src/main/java/com/replaymod/replay/ReplayHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index b01fdc66..7646205d 100755 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -59,7 +59,7 @@ public class ReplayHandler { */ private boolean suppressCameraMovements; - private final Set markers; + private final List markers; private final GuiReplayOverlay overlay; @@ -78,7 +78,7 @@ public class ReplayHandler { FMLCommonHandler.instance().bus().post(new ReplayOpenEvent.Pre(this)); - markers = new HashSet<>(replayFile.getMarkers().or(Collections.emptySet())); + markers = new ArrayList<>(replayFile.getMarkers().or(Collections.emptySet())); replaySender = new ReplaySender(this, replayFile, asyncMode); @@ -185,9 +185,9 @@ public class ReplayHandler { /** * Returns all markers. * When changed, {@link #saveMarkers()} should be called to save the changes. - * @return Set of markers + * @return Collection of markers in no particular order */ - public Set getMarkers() { + public Collection getMarkers() { return markers; } @@ -196,7 +196,7 @@ public class ReplayHandler { */ public void saveMarkers() { try { - replayFile.writeMarkers(markers); + replayFile.writeMarkers(new HashSet<>(markers)); } catch (IOException e) { e.printStackTrace(); } From 1851e973466a3807927227c7992b6e049cc1340f Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 22:24:01 +0200 Subject: [PATCH 09/25] Allow deletion of selected keyframes by pressing the DELETE key (fixes #19) --- .../java/com/replaymod/simplepathing/gui/GuiPathing.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index fbe43ac6..ab8aae28 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -403,6 +403,15 @@ public class GuiPathing { }); }); + core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> { + if (!overlay.isVisible()) { + return; + } + if (mod.getSelectedKeyframe() != null) { + updateKeyframe(mod.getSelectedKeyframe().getValue(TimestampProperty.PROPERTY).isPresent()); + } + }); + // Start loading entity tracker entityTrackerFuture = SettableFuture.create(); new Thread(() -> { From 721fdc4d86e916f2f479c1a10755ad1c8ad3bdbe Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 22 Oct 2016 22:52:27 +0200 Subject: [PATCH 10/25] Allow moving the keyframe timeline cursor by dragging (fixes #18) --- .../replaymod/simplepathing/gui/GuiKeyframeTimeline.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java index e9017265..2ca6ba72 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java @@ -243,6 +243,15 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline Date: Sat, 22 Oct 2016 23:15:06 +0200 Subject: [PATCH 11/25] Update jGui Fix handling of ESC key in popups (fixes #17) This commit changes the behavior of the escape key from closing the current gui screen to only closing the current layer of the gui screen. E.g. pressing ESC closes the popup but not the gui screen, it used to close both --- jGui | 2 +- .../online/gui/GuiReplayCenterSearch.java | 14 +++++++++++++- .../replay/gui/overlay/GuiEditMarkerPopup.java | 14 +++++++++++++- .../simplepathing/gui/GuiEditKeyframe.java | 14 +++++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/jGui b/jGui index 0dfcc65d..4125b3ac 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit 0dfcc65d695d0fed7242ccea4ade580ebe69e8cb +Subproject commit 4125b3ac207b7afee753c0503e07f8f7899a9950 diff --git a/src/main/java/com/replaymod/online/gui/GuiReplayCenterSearch.java b/src/main/java/com/replaymod/online/gui/GuiReplayCenterSearch.java index 7ecfa148..9a39e54d 100644 --- a/src/main/java/com/replaymod/online/gui/GuiReplayCenterSearch.java +++ b/src/main/java/com/replaymod/online/gui/GuiReplayCenterSearch.java @@ -11,15 +11,18 @@ import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiTextField; import de.johni0702.minecraft.gui.element.GuiToggleButton; import de.johni0702.minecraft.gui.element.advanced.GuiDropdownMenu; +import de.johni0702.minecraft.gui.function.Typeable; import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; import de.johni0702.minecraft.gui.utils.Colors; import net.minecraft.client.resources.I18n; +import org.lwjgl.input.Keyboard; +import org.lwjgl.util.ReadablePoint; import java.util.ArrayList; import java.util.List; -public class GuiReplayCenterSearch extends AbstractGuiPopup { +public class GuiReplayCenterSearch extends AbstractGuiPopup implements Typeable { private final GuiReplayCenter replayCenter; private final ApiClient apiClient; public final GuiLabel title = new GuiLabel().setI18nText("replaymod.gui.center.search.filters").setColor(Colors.BLACK); @@ -100,4 +103,13 @@ public class GuiReplayCenterSearch extends AbstractGuiPopup { +public class GuiEditMarkerPopup extends AbstractGuiPopup implements Typeable { private final ReplayHandler replayHandler; private final Marker marker; @@ -107,4 +110,13 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup { protected GuiEditMarkerPopup getThis() { return this; } + + @Override + public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) { + if (keyCode == Keyboard.KEY_ESCAPE) { + cancelButton.onClick(); + return true; + } + return false; + } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java index 335d79cf..0bf0a7c9 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java @@ -13,16 +13,19 @@ import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.element.GuiLabel; import de.johni0702.minecraft.gui.element.GuiNumberField; import de.johni0702.minecraft.gui.element.IGuiLabel; +import de.johni0702.minecraft.gui.function.Typeable; import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; import de.johni0702.minecraft.gui.utils.Colors; import org.apache.commons.lang3.tuple.Triple; +import org.lwjgl.input.Keyboard; +import org.lwjgl.util.ReadablePoint; import static de.johni0702.minecraft.gui.utils.Utils.link; -public abstract class GuiEditKeyframe> extends AbstractGuiPopup { +public abstract class GuiEditKeyframe> extends AbstractGuiPopup implements Typeable { private static GuiNumberField newGuiNumberField() { return new GuiNumberField().setPrecision(0).setValidateOnFocusChange(true); } @@ -84,6 +87,15 @@ public abstract class GuiEditKeyframe> extends Abst }); } + @Override + public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) { + if (keyCode == Keyboard.KEY_ESCAPE) { + cancelButton.onClick(); + return true; + } + return false; + } + @Override public void open() { super.open(); From fcd4c0788efbe90965841a933087576edef48127 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 12:06:50 +0200 Subject: [PATCH 12/25] Add error popup when trying to play path with reversed time keyframes Update jGui --- jGui | 2 +- .../gui/GuiKeyframeTimeline.java | 40 ++++++++++++++----- .../simplepathing/gui/GuiPathing.java | 24 +++++++++-- .../assets/replaymod/lang/en_US.lang | 3 ++ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/jGui b/jGui index 4125b3ac..163cfe2f 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit 4125b3ac207b7afee753c0503e07f8f7899a9950 +Subproject commit 163cfe2f8a9caa6792dc262c139afc066d371cfb diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java index 2ca6ba72..72f1368c 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java @@ -121,24 +121,42 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline= endTime || endFrameTime <= startTime) { - continue; // Segment out of display range - } + drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + 1, 0xFF0088FF); + } - double relativeStart = startFrameTime - startTime; - double relativeEnd = endFrameTime - startTime; - int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1); - int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2); - if (startX < endX) { - renderer.drawRect(startX + 1, BORDER_TOP + 1, endX - startX - 2, KEYFRAME_SIZE - 2, 0xFF0088FF); + // Draw red quads on time path segments that would require time going backwards + for (PathSegment segment : mod.getCurrentTimeline().getPaths().get(GuiPathing.TIME_PATH).getSegments()) { + long startTimestamp = segment.getStartKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new); + long endTimestamp = segment.getEndKeyframe().getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new); + if (endTimestamp >= startTimestamp) { + continue; // All is fine, time is not moving backwards } + drawQuadOnSegment(renderer, visibleWidth, segment, BORDER_TOP + KEYFRAME_SIZE + 1, 0xFFFF0000); } super.drawTimelineCursor(renderer, size); } + private void drawQuadOnSegment(GuiRenderer renderer, int visibleWidth, PathSegment segment, int y, int color) { + int startTime = getOffset(); + int visibleTime = (int) (getZoom() * getLength()); + int endTime = getOffset() + visibleTime; + + long startFrameTime = segment.getStartKeyframe().getTime(); + long endFrameTime = segment.getEndKeyframe().getTime(); + if (startFrameTime >= endTime || endFrameTime <= startTime) { + return; // Segment out of display range + } + + double relativeStart = startFrameTime - startTime; + double relativeEnd = endFrameTime - startTime; + int startX = BORDER_LEFT + Math.max(0, (int) (relativeStart / visibleTime * visibleWidth) + KEYFRAME_SIZE / 2 + 1); + int endX = BORDER_LEFT + Math.min(visibleWidth, (int) (relativeEnd / visibleTime * visibleWidth) - KEYFRAME_SIZE / 2); + if (startX < endX) { + renderer.drawRect(startX + 1, y, endX - startX - 2, KEYFRAME_SIZE - 2, color); + } + } + /** * Returns the keyframe at the specified position. * @param position The raw position diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index ab8aae28..8e0236b1 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -38,6 +38,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; +import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.popup.GuiYesNoPopup; import de.johni0702.minecraft.gui.utils.Colors; import net.minecraft.entity.Entity; @@ -75,7 +76,7 @@ public class GuiPathing { public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() { @Override public void run() { - preparePathsForPlayback(); + if (!preparePathsForPlayback()) return; new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display(); } }).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0); @@ -231,7 +232,7 @@ public class GuiPathing { Timeline timeline = mod.getCurrentTimeline(); Path timePath = timeline.getPaths().get(TIME_PATH); - preparePathsForPlayback(); + if (!preparePathsForPlayback()) return; timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)); ListenableFuture future = player.start(timeline); @@ -438,10 +439,27 @@ public class GuiPathing { }).start(); } - private void preparePathsForPlayback() { + private boolean preparePathsForPlayback() { Timeline timeline = mod.getCurrentTimeline(); timeline.getPaths().get(TIME_PATH).updateAll(); timeline.getPaths().get(POSITION_PATH).updateAll(); + + // Make sure time keyframes's values are monotonically increasing + int lastTime = 0; + for (Keyframe keyframe : timeline.getPaths().get(TIME_PATH).getKeyframes()) { + int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new); + if (time < lastTime) { + // We are going backwards in time + GuiInfoPopup.open(replayHandler.getOverlay(), + "replaymod.error.negativetime1", + "replaymod.error.negativetime2", + "replaymod.error.negativetime3"); + return false; + } + lastTime = time; + } + + return true; } public void zoomTimeline(double factor) { diff --git a/src/main/resources/assets/replaymod/lang/en_US.lang b/src/main/resources/assets/replaymod/lang/en_US.lang index 1c660296..ecdf54ef 100644 --- a/src/main/resources/assets/replaymod/lang/en_US.lang +++ b/src/main/resources/assets/replaymod/lang/en_US.lang @@ -485,6 +485,9 @@ replaymod.gui.objects=Custom Objects #Errors replaymod.error.unknownrestriction1=This replay cannot be played with your current version. replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown. +replaymod.error.negativetime1=Some of your time keyframes are out of order. +replaymod.error.negativetime2=Going backwards in time is not supported. +replaymod.error.negativetime3=The invalid parts are marked in red. #Replay Mod Incompatibility Warning replaymod.gui.modwarning.title=Incompatibility detected From 18f83039af7d6acb25952238b551340ca52c0438 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 13:49:56 +0200 Subject: [PATCH 13/25] Fix crash when moving the last keyframe --- ReplayStudio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReplayStudio b/ReplayStudio index 0d8e8c3f..9591df75 160000 --- a/ReplayStudio +++ b/ReplayStudio @@ -1 +1 @@ -Subproject commit 0d8e8c3f543cee2763283425caea134878c348d8 +Subproject commit 9591df75a1a5916f495a3c85757346fd8276ad8d From 4805e5e9f50790c955db2986b681265fc6d246f9 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 14:06:17 +0200 Subject: [PATCH 14/25] Fix crash when setting two keyframes to the same time via the edit keyframe gui The save button is now disabled if the configured time is already occupied. --- .../simplepathing/gui/GuiEditKeyframe.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java index 0bf0a7c9..edb160a5 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java @@ -19,6 +19,7 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; import de.johni0702.minecraft.gui.utils.Colors; +import de.johni0702.minecraft.gui.utils.Consumer; import org.apache.commons.lang3.tuple.Triple; import org.lwjgl.input.Keyboard; import org.lwjgl.util.ReadablePoint; @@ -71,9 +72,10 @@ public abstract class GuiEditKeyframe> extends Abst this.path = path; long time = keyframe.getTime(); - timeMinField.setValue(time / 1000 / 60); - timeSecField.setValue(time / 1000 % 60); - timeMSecField.setValue(time % 1000); + Consumer updateSaveButtonState = s -> saveButton.setEnabled(canSave()); + timeMinField.setValue(time / 1000 / 60).onTextChanged(updateSaveButtonState); + timeSecField.setValue(time / 1000 % 60).onTextChanged(updateSaveButtonState); + timeMSecField.setValue(time % 1000).onTextChanged(updateSaveButtonState); title.setI18nText("replaymod.gui.editkeyframe.title." + type); saveButton.onClick(() -> { @@ -87,6 +89,14 @@ public abstract class GuiEditKeyframe> extends Abst }); } + private boolean canSave() { + long newTime = (timeMinField.getInteger() * 60 + timeSecField.getInteger()) * 1000 + timeMSecField.getInteger(); + if (newTime != keyframe.getTime() && path.getKeyframe(newTime) != null) { + return false; + } + return true; + } + @Override public boolean typeKey(ReadablePoint mousePosition, int keyCode, char keyChar, boolean ctrlDown, boolean shiftDown) { if (keyCode == Keyboard.KEY_ESCAPE) { From b240f8a27691d7ff5600bf84f8551ae115c13094 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 14:45:14 +0200 Subject: [PATCH 15/25] Fix camera path always playing from the beginning regardless of ctrl key --- .../replaymod/pathing/player/AbstractTimelinePlayer.java | 6 ++++++ .../replaymod/pathing/player/RealtimeTimelinePlayer.java | 2 +- .../java/com/replaymod/simplepathing/gui/GuiPathing.java | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java b/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java index 07530d28..04104787 100644 --- a/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java +++ b/src/main/java/com/replaymod/pathing/player/AbstractTimelinePlayer.java @@ -24,6 +24,7 @@ public abstract class AbstractTimelinePlayer { private final Minecraft mc = Minecraft.getMinecraft(); private final ReplayHandler replayHandler; private Timeline timeline; + protected long startOffset; private long lastTime; private long lastTimestamp; private ListenableFuture future; @@ -33,6 +34,11 @@ public abstract class AbstractTimelinePlayer { this.replayHandler = replayHandler; } + public ListenableFuture start(Timeline timeline, long from) { + startOffset = from; + return start(timeline); + } + public ListenableFuture start(Timeline timeline) { this.timeline = timeline; diff --git a/src/main/java/com/replaymod/pathing/player/RealtimeTimelinePlayer.java b/src/main/java/com/replaymod/pathing/player/RealtimeTimelinePlayer.java index eca409f4..4271ddbf 100644 --- a/src/main/java/com/replaymod/pathing/player/RealtimeTimelinePlayer.java +++ b/src/main/java/com/replaymod/pathing/player/RealtimeTimelinePlayer.java @@ -46,6 +46,6 @@ public class RealtimeTimelinePlayer extends AbstractTimelinePlayer { @Override public long getTimePassed() { - return firstFrame ? 0 : System.currentTimeMillis() - startTime; + return startOffset + (firstFrame ? 0 : System.currentTimeMillis() - startTime); } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index 8e0236b1..efae78eb 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -235,7 +235,9 @@ public class GuiPathing { if (!preparePathsForPlayback()) return; timePath.setActive(!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)); - ListenableFuture future = player.start(timeline); + // Start from cursor time unless the control key is pressed (then start from beginning) + int startTime = Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)? 0 : GuiPathing.this.timeline.getCursorPosition(); + ListenableFuture future = player.start(timeline, startTime); overlay.setCloseable(false); overlay.setMouseVisible(true); Futures.addCallback(future, new FutureCallback() { From c8b71dc2cce19f25e20e720e58a1a2b40b0263cb Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 15:07:24 +0200 Subject: [PATCH 16/25] Add tooltips to ingame buttons --- .../replay/gui/overlay/GuiReplayOverlay.java | 18 ++++- .../simplepathing/gui/GuiPathing.java | 67 ++++++++++++++++--- 2 files changed, 74 insertions(+), 11 deletions(-) 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 19cc837b..97849c6a 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java @@ -7,8 +7,10 @@ import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.container.AbstractGuiOverlay; import de.johni0702.minecraft.gui.container.GuiPanel; +import de.johni0702.minecraft.gui.element.GuiElement; import de.johni0702.minecraft.gui.element.GuiSlider; import de.johni0702.minecraft.gui.element.GuiTexturedButton; +import de.johni0702.minecraft.gui.element.GuiTooltip; import de.johni0702.minecraft.gui.element.advanced.IGuiTimeline; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; @@ -27,8 +29,20 @@ public class GuiReplayOverlay extends AbstractGuiOverlay { public final GuiPanel topPanel = new GuiPanel(this) .setLayout(new HorizontalLayout(HorizontalLayout.Alignment.LEFT).setSpacing(5)); - public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20) - .setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE); + public final GuiTexturedButton playPauseButton = new GuiTexturedButton() { + @Override + public GuiElement getTooltip(RenderInfo renderInfo) { + GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo); + if (tooltip != null) { + if (getTextureNormal().getY() == 0) { // Play button + tooltip.setI18nText("replaymod.gui.ingame.menu.unpause"); + } else { // Pause button + tooltip.setI18nText("replaymod.gui.ingame.menu.pause"); + } + } + return tooltip; + } + }.setSize(20, 20).setTexture(ReplayMod.TEXTURE, TEXTURE_SIZE).setTooltip(new GuiTooltip()); public final GuiSlider speedSlider = new GuiSlider().setSize(100, 20).setSteps(37); // 0.0 is not included public final GuiMarkerTimeline timeline; diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index efae78eb..c1a4e69b 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -70,8 +70,22 @@ public class GuiPathing { private static final Logger logger = LogManager.getLogger(); - public final GuiTexturedButton playPauseButton = new GuiTexturedButton().setSize(20, 20) - .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE); + public final GuiTexturedButton playPauseButton = new GuiTexturedButton() { + @Override + public GuiElement getTooltip(RenderInfo renderInfo) { + GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo); + if (tooltip != null) { + if (player.isActive()) { + tooltip.setI18nText("replaymod.gui.ingame.menu.pausepath"); + } else if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) { + tooltip.setI18nText("replaymod.gui.ingame.menu.playpathfromstart"); + } else { + tooltip.setI18nText("replaymod.gui.ingame.menu.playpath"); + } + } + return tooltip; + } + }.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip()); public final GuiTexturedButton renderButton = new GuiTexturedButton().onClick(new Runnable() { @Override @@ -79,13 +93,46 @@ public class GuiPathing { if (!preparePathsForPlayback()) return; new GuiRenderSettings(replayHandler, mod.getCurrentTimeline()).display(); } - }).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0); + }).setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 0) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.renderpath")); - public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton().setSize(20, 20) - .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE); + public final GuiTexturedButton positionKeyframeButton = new GuiTexturedButton() { + @Override + public GuiElement getTooltip(RenderInfo renderInfo) { + GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo); + if (tooltip != null) { + if (getTextureNormal().getY() == 40) { // Add keyframe + if (getTextureNormal().getX() == 0) { // Position + tooltip.setI18nText("replaymod.gui.ingame.menu.addposkeyframe"); + } else { // Spectator + tooltip.setI18nText("replaymod.gui.ingame.menu.addspeckeyframe"); + } + } else { // Remove keyframe + if (getTextureNormal().getX() == 0) { // Position + tooltip.setI18nText("replaymod.gui.ingame.menu.removeposkeyframe"); + } else { // Spectator + tooltip.setI18nText("replaymod.gui.ingame.menu.removespeckeyframe"); + } + } + } + return tooltip; + } + }.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip()); - public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton().setSize(20, 20) - .setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE); + public final GuiTexturedButton timeKeyframeButton = new GuiTexturedButton() { + @Override + public GuiElement getTooltip(RenderInfo renderInfo) { + GuiTooltip tooltip = (GuiTooltip) super.getTooltip(renderInfo); + if (tooltip != null) { + if (getTextureNormal().getY() == 80) { // Add time keyframe + tooltip.setI18nText("replaymod.gui.ingame.menu.addtimekeyframe"); + } else { // Remove time keyframe + tooltip.setI18nText("replaymod.gui.ingame.menu.removetimekeyframe"); + } + } + return tooltip; + } + }.setSize(20, 20).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTooltip(new GuiTooltip()); public final GuiKeyframeTimeline timeline = new GuiKeyframeTimeline(this){ @Override @@ -114,14 +161,16 @@ public class GuiPathing { public void run() { zoomTimeline(2d / 3d); } - }).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20); + }).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 20) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomin")); public final GuiTexturedButton zoomOutButton = new GuiTexturedButton().setSize(9, 9).onClick(new Runnable() { @Override public void run() { zoomTimeline(3d / 2d); } - }).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30); + }).setTexture(ReplayMod.TEXTURE, ReplayMod.TEXTURE_SIZE).setTexturePosH(40, 30) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.ingame.menu.zoomout")); public final GuiPanel zoomButtonPanel = new GuiPanel() .setLayout(new VerticalLayout(VerticalLayout.Alignment.CENTER).setSpacing(2)) From cc65b26c31bdcdbacf653c702a50b0dd8c9e949a Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 23 Oct 2016 15:28:56 +0200 Subject: [PATCH 17/25] Replace custom update system with forge update checker --- .../java/com/replaymod/core/ReplayMod.java | 1 + .../com/replaymod/extras/ReplayModExtras.java | 3 +- .../com/replaymod/extras/VersionChecker.java | 66 ------------------- 3 files changed, 2 insertions(+), 68 deletions(-) delete mode 100644 src/main/java/com/replaymod/extras/VersionChecker.java diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 599112ff..4d2230bd 100755 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -35,6 +35,7 @@ import java.util.Queue; @Mod(modid = ReplayMod.MOD_ID, useMetadata = true, + updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json", guiFactory = "com.replaymod.core.gui.GuiFactory") public class ReplayMod { diff --git a/src/main/java/com/replaymod/extras/ReplayModExtras.java b/src/main/java/com/replaymod/extras/ReplayModExtras.java index 54a7eca4..750111a6 100644 --- a/src/main/java/com/replaymod/extras/ReplayModExtras.java +++ b/src/main/java/com/replaymod/extras/ReplayModExtras.java @@ -29,8 +29,7 @@ public class ReplayModExtras { FullBrightness.class, HotkeyButtons.class, LocalizationExtra.class, - OpenEyeExtra.class, - VersionChecker.class + OpenEyeExtra.class ); private Logger logger; diff --git a/src/main/java/com/replaymod/extras/VersionChecker.java b/src/main/java/com/replaymod/extras/VersionChecker.java deleted file mode 100644 index 017fbe81..00000000 --- a/src/main/java/com/replaymod/extras/VersionChecker.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.replaymod.extras; - -import com.replaymod.core.ReplayMod; -import com.replaymod.online.ReplayModOnline; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiMainMenu; -import net.minecraft.client.resources.I18n; -import net.minecraftforge.client.event.GuiScreenEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -import java.awt.*; -import java.util.List; - - -public class VersionChecker implements Extra { - @Mod.Instance(ReplayModOnline.MOD_ID) - private static ReplayModOnline module; - - @Override - public void register(ReplayMod mod) throws Exception { - final String currentVersion = mod.getVersion(); - new Thread(new Runnable() { - @Override - public void run() { - try { - boolean upToDate = module.getApiClient().isVersionUpToDate(currentVersion); - if (!upToDate) { - MinecraftForge.EVENT_BUS.register(VersionChecker.this); - } - } catch(Exception e) { - e.printStackTrace(); - } - } - }, "ReplayMod-VersionChecker").start(); - } - - @SubscribeEvent - public void onDrawScreen(GuiScreenEvent.DrawScreenEvent.Post event) { - if (!(event.gui instanceof GuiMainMenu)) { - return; - } - - int width = Math.max(100, event.gui.width / 2 - 100 - 10); - - @SuppressWarnings("unchecked") List lines = - event.gui.mc.fontRendererObj.listFormattedStringToWidth(I18n.format("replaymod.gui.outdated"), width); - - int maxLineWidth = 0; - for(String line : lines) { - int lineWidth = event.gui.mc.fontRendererObj.getStringWidth(line); - if(lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - - Gui.drawRect(2, 77, 5 + maxLineWidth + 3, 80 + (lines.size() * 10), 0x80FF0000); - - int i = 0; - for(String line : lines) { - event.gui.mc.fontRendererObj.drawStringWithShadow(line, 5, 80 + (i * 10), Color.WHITE.getRGB()); - i++; - } - } -} From d82cef36875981e7006022ac768d85ed563188c6 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sun, 6 Nov 2016 13:43:28 +0100 Subject: [PATCH 18/25] Make sure video folder exists before rendering (fixes #24) --- src/main/java/com/replaymod/render/VideoWriter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/replaymod/render/VideoWriter.java b/src/main/java/com/replaymod/render/VideoWriter.java index 6fcce914..66665768 100755 --- a/src/main/java/com/replaymod/render/VideoWriter.java +++ b/src/main/java/com/replaymod/render/VideoWriter.java @@ -8,6 +8,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import org.apache.commons.exec.CommandLine; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.TeeOutputStream; import org.lwjgl.util.ReadableDimension; @@ -34,6 +35,7 @@ public class VideoWriter implements FrameConsumer { this.settings = settings; File outputFolder = settings.getOutputFile().getParentFile(); + FileUtils.forceMkdir(outputFolder); String fileName = settings.getOutputFile().getName(); commandArgs = settings.getExportArguments() From 9ff767b054066dc08b8cb434f6afc8ce50136503 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Wed, 9 Nov 2016 10:23:58 +0100 Subject: [PATCH 19/25] Fix spectated player having the arm model of the camera player --- .../java/com/replaymod/replay/camera/CameraEntity.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 656b3951..dc20471b 100755 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -10,6 +10,7 @@ import lombok.Getter; import lombok.Setter; import net.minecraft.block.material.Material; import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.client.settings.KeyBinding; @@ -254,6 +255,15 @@ public class CameraEntity extends EntityPlayerSP { return super.getLocationSkin(); } + @Override + public String getSkinType() { + Entity view = mc.getRenderViewEntity(); + if (view != this && view instanceof AbstractClientPlayer) { + return ((AbstractClientPlayer) view).getSkinType(); + } + return super.getSkinType(); + } + @Override public float getSwingProgress(float renderPartialTicks) { Entity view = mc.getRenderViewEntity(); From 92eb11a61728d65d3af4f5c653340c867c6776cb Mon Sep 17 00:00:00 2001 From: johni0702 Date: Tue, 18 Oct 2016 21:57:26 +0200 Subject: [PATCH 20/25] Change video preview to be compatible with Optifine --- .../replaymod/render/gui/GuiVideoRenderer.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java index 7777bf6f..0f60f3b4 100644 --- a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java +++ b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java @@ -14,7 +14,6 @@ import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.client.resources.I18n; import net.minecraft.util.ResourceLocation; import org.lwjgl.util.Dimension; @@ -23,8 +22,6 @@ import org.lwjgl.util.ReadablePoint; import java.nio.ByteBuffer; -import static net.minecraft.client.renderer.GlStateManager.bindTexture; - public class GuiVideoRenderer extends GuiScreen { private static final ResourceLocation NO_PREVIEW_TEXTURE = new ResourceLocation("replaymod", "logo.jpg"); @@ -232,13 +229,7 @@ public class GuiVideoRenderer extends GuiScreen { final int videoHeight = videoSize.getHeight(); if (previewTexture == null) { - previewTexture = new DynamicTexture(videoWidth, videoHeight) { - @Override - public void updateDynamicTexture() { - bindTexture(getGlTextureId()); - TextureUtil.uploadTextureSub(0, getTextureData(), videoWidth, videoHeight, 0, 0, true, false, false); - } - }; + previewTexture = new DynamicTexture(videoWidth, videoHeight); } if (previewTextureDirty) { @@ -273,7 +264,10 @@ public class GuiVideoRenderer extends GuiScreen { buffer.mark(); synchronized (this) { int[] data = previewTexture.getTextureData(); - for (int i = 0; i < data.length; i++) { + // Optifine changes the texture data array to be three times as long (for use by shaders), + // we only want to initialize the first third which is why we use the length of the buffer instead + // of the length of the data array + for (int i = 0; buffer.remaining() > 0; i++) { data[i] = 0xff << 24 | (buffer.get() & 0xff) << 16 | (buffer.get() & 0xff) << 8 | (buffer.get() & 0xff); } previewTextureDirty = true; From 636ce7bdd4c91cbed2d76b18cbe509b4075fcd46 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Sat, 5 Nov 2016 18:22:07 +0100 Subject: [PATCH 21/25] Add compatibility for GLSL Shaders --- .../mixin/MixinShaderEntityRenderer.java | 52 +++++++++++++++++++ .../com/replaymod/core/LoadingPlugin.java | 1 + .../render/capturer/ODSFrameCapturer.java | 8 ++- .../render/capturer/OpenGlFrameCapturer.java | 22 ++++++-- .../render/capturer/WorldRenderer.java | 4 +- .../render/hooks/EntityRendererHandler.java | 49 +++++------------ .../render/mixin/MixinEntityRenderer.java | 9 ---- .../mixins.compat.shaders.replaymod.json | 10 ++++ 8 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java create mode 100644 src/main/resources/mixins.compat.shaders.replaymod.json diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java new file mode 100644 index 00000000..875b9703 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java @@ -0,0 +1,52 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.replay.ReplayHandler; +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EntityRenderer; +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.CallbackInfo; + +import java.lang.reflect.Field; + +@Mixin(EntityRenderer.class) +public abstract class MixinShaderEntityRenderer { + + @Shadow + public Minecraft mc; + + private static Field replayModCompat_frameTimeCounterField = null; + + static { + replayModCompat_initFrameTimeCounterField(); + } + + private static void replayModCompat_initFrameTimeCounterField() { + try { + replayModCompat_frameTimeCounterField = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("frameTimeCounter"); + replayModCompat_frameTimeCounterField.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableDepth()V")) + private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { + if (replayModCompat_frameTimeCounterField != null) { + ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); + float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; + try { + replayModCompat_frameTimeCounterField.set(null, timestamp); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/replaymod/core/LoadingPlugin.java b/src/main/java/com/replaymod/core/LoadingPlugin.java index af86c1ee..6ed89aa4 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 { MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.recording.replaymod.json"); MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.render.replaymod.json"); MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.replay.replaymod.json"); + MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.compat.shaders.replaymod.json"); CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); if (codeSource != null) { diff --git a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java index 33c9ae7a..01a480f0 100644 --- a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java @@ -6,11 +6,11 @@ import com.replaymod.render.frame.ODSOpenGlFrame; import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.shader.Program; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.crash.CrashReport; import net.minecraft.util.ReportedException; import net.minecraft.util.ResourceLocation; -import org.lwjgl.util.Dimension; import org.lwjgl.util.ReadableDimension; import java.io.IOException; @@ -31,6 +31,8 @@ public class ODSFrameCapturer implements FrameCapturer { private final BooleanState[] previousStates = new BooleanState[3]; private final BooleanState previousFogState; + private final Minecraft mc = Minecraft.getMinecraft(); + public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) { RenderInfo fakeInfo = new RenderInfo() { private int call; @@ -143,6 +145,8 @@ public class ODSFrameCapturer implements FrameCapturer { @Override protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) { + resize(getFrameWidth(), getFrameHeight()); + pushMatrix(); frameBuffer().bindFramebuffer(true); @@ -150,7 +154,7 @@ public class ODSFrameCapturer implements FrameCapturer { enableTexture2D(); directionVariable.set(captureData.ordinal()); - worldRenderer.renderWorld(new Dimension(getFrameWidth(), getFrameHeight()), partialTicks, null); + worldRenderer.renderWorld(partialTicks, null); frameBuffer().unbindFramebuffer(); popMatrix(); diff --git a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java index 4513f2ae..d0968582 100644 --- a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java @@ -4,6 +4,7 @@ import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.rendering.Frame; import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.utils.ByteBufferPool; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.shader.Framebuffer; import org.lwjgl.opengl.GL11; @@ -24,6 +25,8 @@ public abstract class OpenGlFrameCapturer Date: Sat, 5 Nov 2016 18:22:41 +0100 Subject: [PATCH 22/25] Draw the GuiVideoRenderer on a separate framebuffer that has the actual size of the window instead of MC's framebuffer which may have a different size --- .../render/rendering/VideoRenderer.java | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index 05a5a3d2..640eb8c0 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -20,6 +20,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.audio.SoundCategory; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.shader.Framebuffer; import net.minecraft.util.Timer; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; @@ -62,6 +63,9 @@ public class VideoRenderer implements RenderInfo { private boolean paused; private boolean cancelled; + private Framebuffer guiFramebuffer; + private int displayWidth, displayHeight; + public VideoRenderer(RenderSettings settings, ReplayHandler replayHandler, Timeline timeline) throws IOException { this.settings = settings; this.replayHandler = replayHandler; @@ -130,6 +134,13 @@ public class VideoRenderer implements RenderInfo { @Override public float updateForNextFrame() { + // because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily + int displayWidthBefore = mc.displayWidth; + int displayHeightBefore = mc.displayHeight; + + mc.displayWidth = displayWidth; + mc.displayHeight = displayHeight; + if (!settings.isHighPerformance() || framesDone % fps == 0) { drawGui(); } @@ -142,6 +153,10 @@ public class VideoRenderer implements RenderInfo { tick(); } + // change Minecraft's display size back + mc.displayWidth = displayWidthBefore; + mc.displayHeight = displayHeightBefore; + framesDone++; return mc.timer.renderPartialTicks; } @@ -192,10 +207,15 @@ public class VideoRenderer implements RenderInfo { totalFrames = (int) (duration*fps/1000); - ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + updateDisplaySize(); + + ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight); gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal); + + // Set up our own framebuffer to render the GUI to + guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true); } private void finish() { @@ -219,6 +239,9 @@ public class VideoRenderer implements RenderInfo { ReplayMod.soundHandler.playRenderSuccessSound(); new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display(); + + // Finally, resize the Minecraft framebuffer to the actual width/height of the window + mc.resize(displayWidth, displayHeight); } private void tick() { @@ -238,12 +261,22 @@ public class VideoRenderer implements RenderInfo { public void drawGui() { do { + // Resize the GUI framebuffer if the display size changed + if (!settings.isHighPerformance() && displaySizeChanged()) { + updateDisplaySize(); + guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight); + } + pushMatrix(); clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); enableTexture2D(); - mc.getFramebuffer().bindFramebuffer(true); + guiFramebuffer.bindFramebuffer(true); + mc.entityRenderer.setupOverlayRendering(); + ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); + try { gui.toMinecraft().handleInput(); } catch (IOException e) { @@ -252,18 +285,17 @@ public class VideoRenderer implements RenderInfo { throw new RuntimeException(e); } - ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth; int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1; + gui.toMinecraft().drawScreen(mouseX, mouseY, 0); - mc.getFramebuffer().unbindFramebuffer(); + guiFramebuffer.unbindFramebuffer(); popMatrix(); pushMatrix(); - mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight); + guiFramebuffer.framebufferRender(displayWidth, displayHeight); popMatrix(); - // if not in high performance mode, update the gui size if screen size changed // otherwise just swap the progress gui to screen if (settings.isHighPerformance()) { @@ -285,6 +317,15 @@ public class VideoRenderer implements RenderInfo { } while (paused); } + private boolean displaySizeChanged() { + return displayWidth != Display.getWidth() || displayHeight != Display.getHeight(); + } + + private void updateDisplaySize() { + displayWidth = Display.getWidth(); + displayHeight = Display.getHeight(); + } + public int getFramesDone() { return framesDone; } From e5eb982b984bf7f7471fee00db63899a6c21dc43 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Wed, 9 Nov 2016 04:28:56 +0100 Subject: [PATCH 23/25] Add Optifine compatibility to rendering by disabling their separate chunk loading queue Add compatibility to shaders that utilize the Shadow Map by manually setting displayListEntitiesDirty to false Call Shaders.beginRender in an Event Handler to avoid calling all of EntityRenderer#renderWorld --- .../com/replaymod/compat/ReplayModCompat.java | 23 ++++ .../compat/shaders/ShaderBeginRender.java | 39 +++++++ .../compat/shaders/ShaderReflection.java | 102 ++++++++++++++++++ .../mixin/MixinShaderEntityRenderer.java | 42 ++------ .../shaders/mixin/MixinShaderRenderChunk.java | 28 +++++ .../mixin/MixinShaderRenderGlobal.java | 40 +++++++ .../render/hooks/EntityRendererHandler.java | 16 +-- .../render/mixin/MixinRenderGlobal.java | 2 + src/main/resources/mcmod.info | 19 +++- .../mixins.compat.shaders.replaymod.json | 6 +- 10 files changed, 270 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/replaymod/compat/ReplayModCompat.java create mode 100644 src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java create mode 100644 src/main/java/com/replaymod/compat/shaders/ShaderReflection.java create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java diff --git a/src/main/java/com/replaymod/compat/ReplayModCompat.java b/src/main/java/com/replaymod/compat/ReplayModCompat.java new file mode 100644 index 00000000..0587764f --- /dev/null +++ b/src/main/java/com/replaymod/compat/ReplayModCompat.java @@ -0,0 +1,23 @@ +package com.replaymod.compat; + +import com.replaymod.compat.shaders.ShaderBeginRender; +import com.replaymod.core.ReplayMod; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.EventBus; + +@Mod(modid = ReplayModCompat.MOD_ID, useMetadata = true) +public class ReplayModCompat { + public static final String MOD_ID = "replaymod-compat"; + + @Mod.Instance(ReplayMod.MOD_ID) + private static ReplayMod core; + + @Mod.EventHandler + public void init(FMLInitializationEvent event) { + EventBus bus = FMLCommonHandler.instance().bus(); + bus.register(new ShaderBeginRender()); + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java new file mode 100644 index 00000000..8404801b --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java @@ -0,0 +1,39 @@ +package com.replaymod.compat.shaders; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +import java.lang.reflect.InvocationTargetException; + +public class ShaderBeginRender { + + private final Minecraft mc = Minecraft.getMinecraft(); + + /** + * Invokes Shaders#beginRender when rendering a video, + * as this would usually get called by EntityRenderer#renderWorld, + * which we're not calling during rendering. + */ + @SubscribeEvent + public void onRenderTickStart(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + if (ShaderReflection.shaders_beginRender == null) return; + if (ShaderReflection.config_isShaders == null) return; + + try { + // check if video is being rendered + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) + return; + + // check if Shaders are enabled + if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return; + + ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.elapsedPartialTicks, 0); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java b/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java new file mode 100644 index 00000000..55d785b2 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java @@ -0,0 +1,102 @@ +package com.replaymod.compat.shaders; + +import net.minecraft.client.Minecraft; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ShaderReflection { + + // Shaders.frameTimeCounter + public static Field shaders_frameTimeCounter; + + // Shaders.isShadowPass + public static Field shaders_isShadowPass; + + // Shaders.beginRender() + public static Method shaders_beginRender; + + // RenderGlobal.chunksToUpdateForced (Optifine only) + public static Field renderGlobal_chunksToUpdateForced; + + // Config.isShaders() (Optifine only) + public static Method config_isShaders; + + static { + initFrameTimeCounter(); + + initIsShadowPass(); + + initBeginRender(); + + initChunksToUpdateForced(); + + initConfigIsShaders(); + } + + private static void initFrameTimeCounter() { + try { + shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("frameTimeCounter"); + shaders_frameTimeCounter.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initIsShadowPass() { + try { + shaders_isShadowPass = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("isShadowPass"); + shaders_isShadowPass.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initBeginRender() { + try { + shaders_beginRender = Class.forName("shadersmod.client.Shaders") + .getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class); + shaders_frameTimeCounter.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchMethodException e) { + // the method wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initChunksToUpdateForced() { + try { + renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal") + .getDeclaredField("chunksToUpdateForced"); + renderGlobal_chunksToUpdateForced.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initConfigIsShaders() { + try { + config_isShaders = Class.forName("Config") + .getDeclaredMethod("isShaders"); + config_isShaders.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchMethodException e) { + // the method wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java index 875b9703..358ff118 100644 --- a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java @@ -1,52 +1,28 @@ package com.replaymod.compat.shaders.mixin; +import com.replaymod.compat.shaders.ShaderReflection; import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayModReplay; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.EntityRenderer; 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.CallbackInfo; -import java.lang.reflect.Field; - @Mixin(EntityRenderer.class) public abstract class MixinShaderEntityRenderer { - @Shadow - public Minecraft mc; + @Inject(method = "renderWorldPass", at = @At("HEAD")) + private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { + if (ReplayModReplay.instance.getReplayHandler() == null) return; + if (ShaderReflection.shaders_frameTimeCounter == null) return; - private static Field replayModCompat_frameTimeCounterField = null; - - static { - replayModCompat_initFrameTimeCounterField(); - } - - private static void replayModCompat_initFrameTimeCounterField() { + ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); + float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; try { - replayModCompat_frameTimeCounterField = Class.forName("shadersmod.client.Shaders") - .getDeclaredField("frameTimeCounter"); - replayModCompat_frameTimeCounterField.setAccessible(true); - } catch (ClassNotFoundException ignore) { - // no shaders mod installed - } catch (NoSuchFieldException e) { - // the field wasn't found. Has it been renamed? + ShaderReflection.shaders_frameTimeCounter.set(null, timestamp); + } catch (Exception e) { e.printStackTrace(); } } - - @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableDepth()V")) - private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { - if (replayModCompat_frameTimeCounterField != null) { - ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); - float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; - try { - replayModCompat_frameTimeCounterField.set(null, timestamp); - } catch (Exception e) { - e.printStackTrace(); - } - } - } } diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java new file mode 100644 index 00000000..3b4e9bc2 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java @@ -0,0 +1,28 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.RenderChunk; +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.CallbackInfoReturnable; + +@Mixin(RenderChunk.class) +public abstract class MixinShaderRenderChunk { + + private final Minecraft mc = Minecraft.getMinecraft(); + + /** + * Changes the RenderChunk#isPlayerUpdate method that Optifine adds + * to always return true while rendering so no chunks are being added + * to a separate rendering queue + */ + @Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true) + private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable ci) { + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return; + ci.setReturnValue(true); + } + + +} diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java new file mode 100644 index 00000000..be69cf00 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java @@ -0,0 +1,40 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.compat.shaders.ShaderReflection; +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.culling.ICamera; +import net.minecraft.entity.Entity; +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.CallbackInfo; + +@Mixin(RenderGlobal.class) +public abstract class MixinShaderRenderGlobal { + + private final Minecraft mc = Minecraft.getMinecraft(); + + @Shadow + public boolean displayListEntitiesDirty; + + @Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true) + public void replayModCompat_setupTerrain(Entity viewEntity, double partialTicks, ICamera camera, + int frameCount, boolean playerSpectator, CallbackInfo ci) { + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return; + if (ShaderReflection.shaders_isShadowPass == null) return; + + // when called by the shadow pass, displayListEntitiesDirty can't be set to false, as no chunk updates + // are being processed. As it's being set to true by ChunkLoadingRenderGlobal#updateChunks, we have to + // set it to false manually to exit the loop imposed by MixinRenderGlobal#replayModRender_setupTerrain. + try { + if ((boolean) ShaderReflection.shaders_isShadowPass.get(null) == true) { + displayListEntitiesDirty = false; + } + } catch (IllegalAccessException ignore) {} + + } + +} diff --git a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java index c38ba636..f27ada50 100644 --- a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java +++ b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java @@ -6,7 +6,6 @@ import com.replaymod.render.capturer.WorldRenderer; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; -import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.FMLCommonHandler; import java.io.IOException; @@ -36,18 +35,13 @@ public class EntityRendererHandler implements WorldRenderer { public void renderWorld(float partialTicks, long finishTimeNano) { FMLCommonHandler.instance().onRenderTickStart(partialTicks); - // the Shaders Mod does an initializing call in the EntityRenderer#renderWorld method - if (FMLClientHandler.instance().hasOptifine()) { - mc.entityRenderer.renderWorld(partialTicks, finishTimeNano); - } else { - mc.entityRenderer.updateLightmap(partialTicks); + mc.entityRenderer.updateLightmap(partialTicks); - GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(516, 0.5F); + GlStateManager.enableDepth(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(516, 0.5F); - mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano); - } + mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano); FMLCommonHandler.instance().onRenderTickEnd(partialTicks); } diff --git a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java b/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java index 34ee45e0..7f5cae0a 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java +++ b/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java @@ -39,6 +39,8 @@ public abstract class MixinRenderGlobal { setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator); } while (displayListEntitiesDirty); + displayListEntitiesDirty = true; + replayModRender_passThroughSetupTerrain = false; ci.cancel(); } diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index bf81b322..f6ac199a 100755 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -116,5 +116,22 @@ "parent": "replaymod", "screenshots": [], "dependencies": [] - } + }, + { + "modid": "replaymod-compat", + "name": "Replay Mod - Compatibility", + "description": "Compatibility Module of the ReplayMod - Adds compatibility with other mods", + "version": "${version}", + "mcversion": "${mcversion}", + "url": "https://replaymod.com", + "updateUrl": "https://replaymod.com/download", + "authorList": [ + "CrushedPixel", + "johni0702" + ], + "logoFile": "replaymod_logo.png", + "parent": "replaymod", + "screenshots": [], + "dependencies": [] + } ] diff --git a/src/main/resources/mixins.compat.shaders.replaymod.json b/src/main/resources/mixins.compat.shaders.replaymod.json index 656c092b..eea17922 100644 --- a/src/main/resources/mixins.compat.shaders.replaymod.json +++ b/src/main/resources/mixins.compat.shaders.replaymod.json @@ -1,10 +1,12 @@ { - "required": true, + "required": false, "package": "com.replaymod.compat.shaders.mixin", "mixins": [], "server": [], "client": [ - "MixinShaderEntityRenderer" + "MixinShaderEntityRenderer", + "MixinShaderRenderChunk", + "MixinShaderRenderGlobal" ], "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file From 5014edafe052fca705c7550fd93895b6163e6be6 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Thu, 10 Nov 2016 20:32:24 +0100 Subject: [PATCH 24/25] Remove javascript comments from mcmod.info --- src/main/resources/mcmod.info | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 144f7a72..bf81b322 100755 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -48,7 +48,7 @@ "parent": "replaymod", "screenshots": [], "dependencies": [] - }/*, + }, { "modid": "replaymod-online", "name": "Replay Mod - Online", @@ -116,5 +116,5 @@ "parent": "replaymod", "screenshots": [], "dependencies": [] - }*/ + } ] From 0db1b9ef5adbdba26172c5e94333d0b25c21ac47 Mon Sep 17 00:00:00 2001 From: johni0702 Date: Thu, 10 Nov 2016 20:52:07 +0100 Subject: [PATCH 25/25] Fix opening a replay multiple times by quickly double clicking (fixes #25) --- .../java/com/replaymod/replay/gui/screen/GuiReplayViewer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java b/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java index 307238f3..b9ba3961 100755 --- a/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java +++ b/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java @@ -101,6 +101,8 @@ public class GuiReplayViewer extends GuiScreen { }).onSelectionDoubleClicked(() -> { if (this.loadButton.isEnabled()) { this.loadButton.onClick(); + // Disable load button to prevent the player from opening the replay twice at the same time + this.loadButton.setDisabled(); } }).setDrawShadow(true).setDrawSlider(true);