Fix keyframes being required to open render queue window
Half the reason we swapped the render queue / settings parent relationship was so we don't need to go through the settings screen to access the queue. But for some reason I stopped half way through and didn't also move the "You need at last two keyframes." popup. This commit rectifies that.
This commit is contained in:
142
src/main/java/com/replaymod/core/utils/Result.java
Normal file
142
src/main/java/com/replaymod/core/utils/Result.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package com.replaymod.core.utils;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public abstract class Result<Ok, Err> {
|
||||||
|
|
||||||
|
public static <Ok, Err> OkImpl<Ok, Err> ok(Ok value) {
|
||||||
|
return new OkImpl<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Ok, Err> ErrImpl<Ok, Err> err(Err value) {
|
||||||
|
return new ErrImpl<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean isOk();
|
||||||
|
public abstract boolean isErr();
|
||||||
|
public abstract Ok okOrNull();
|
||||||
|
public abstract Err errOrNull();
|
||||||
|
public abstract void ifOk(Consumer<Ok> consumer);
|
||||||
|
public abstract void ifErr(Consumer<Err> consumer);
|
||||||
|
public abstract Ok okOrElse(Function<Err, Ok> orElse);
|
||||||
|
public abstract Err errOrElse(Function<Ok, Err> orElse);
|
||||||
|
public abstract <T> Result<T, Err> mapOk(Function<Ok, T> func);
|
||||||
|
public abstract <T> Result<Ok, T> mapErr(Function<Err, T> func);
|
||||||
|
|
||||||
|
private static class OkImpl<Ok, Err> extends Result<Ok, Err> {
|
||||||
|
private final Ok value;
|
||||||
|
|
||||||
|
public OkImpl(Ok value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOk() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErr() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ok okOrNull() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Err errOrNull() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ifOk(Consumer<Ok> consumer) {
|
||||||
|
consumer.accept(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ifErr(Consumer<Err> consumer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ok okOrElse(Function<Err, Ok> orElse) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Err errOrElse(Function<Ok, Err> orElse) {
|
||||||
|
return orElse.apply(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> Result<V, Err> mapOk(Function<Ok, V> func) {
|
||||||
|
return ok(func.apply(this.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> Result<Ok, T> mapErr(Function<Err, T> func) {
|
||||||
|
return (Result<Ok, T>) this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ErrImpl<Ok, Err> extends Result<Ok, Err> {
|
||||||
|
private final Err value;
|
||||||
|
|
||||||
|
public ErrImpl(Err value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOk() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErr() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ok okOrNull() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Err errOrNull() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ifOk(Consumer<Ok> consumer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ifErr(Consumer<Err> consumer) {
|
||||||
|
consumer.accept(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ok okOrElse(Function<Err, Ok> orElse) {
|
||||||
|
return orElse.apply(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Err errOrElse(Function<Ok, Err> orElse) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <V> Result<V, Err> mapOk(Function<Ok, V> func) {
|
||||||
|
return (Result<V, Err>) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Result<Ok, T> mapErr(Function<Err, T> func) {
|
||||||
|
return err(func.apply(this.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.replaymod.render.gui;
|
|||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.Result;
|
||||||
import com.replaymod.core.utils.Utils;
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.core.versions.MCVer;
|
import com.replaymod.core.versions.MCVer;
|
||||||
import com.replaymod.render.RenderSettings;
|
import com.replaymod.render.RenderSettings;
|
||||||
@@ -30,6 +31,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
|
|||||||
import de.johni0702.minecraft.gui.layout.GridLayout;
|
import de.johni0702.minecraft.gui.layout.GridLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
import de.johni0702.minecraft.gui.popup.AbstractGuiPopup;
|
||||||
|
import de.johni0702.minecraft.gui.popup.GuiInfoPopup;
|
||||||
import de.johni0702.minecraft.gui.utils.Colors;
|
import de.johni0702.minecraft.gui.utils.Colors;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
|
||||||
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
|
||||||
@@ -88,7 +90,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
private final AbstractGuiScreen<?> container;
|
private final AbstractGuiScreen<?> container;
|
||||||
private final ReplayHandler replayHandler;
|
private final ReplayHandler replayHandler;
|
||||||
private final Set<Entry> selectedEntries = new HashSet<>();
|
private final Set<Entry> selectedEntries = new HashSet<>();
|
||||||
private final Supplier<Timeline> timelineSupplier;
|
private final Supplier<Result<Timeline, String[]>> timelineSupplier;
|
||||||
private boolean opened;
|
private boolean opened;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -113,7 +115,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
private final ReplayModRender mod = ReplayModRender.instance;
|
private final ReplayModRender mod = ReplayModRender.instance;
|
||||||
private final List<RenderJob> jobs = mod.getRenderQueue();
|
private final List<RenderJob> jobs = mod.getRenderQueue();
|
||||||
|
|
||||||
public GuiRenderQueue(AbstractGuiScreen<?> container, ReplayHandler replayHandler, Supplier<Timeline> timelineSupplier) {
|
public GuiRenderQueue(AbstractGuiScreen<?> container, ReplayHandler replayHandler, Supplier<Result<Timeline, String[]>> timelineSupplier) {
|
||||||
super(container);
|
super(container);
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.replayHandler = replayHandler;
|
this.replayHandler = replayHandler;
|
||||||
@@ -127,7 +129,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
list.getListPanel().addElements(null, new Entry(renderJob));
|
list.getListPanel().addElements(null, new Entry(renderJob));
|
||||||
}
|
}
|
||||||
|
|
||||||
addButton.onClick(this::addButtonClicked);
|
addButton.onClick(() -> addButtonClicked().ifErr(lines -> GuiInfoPopup.open(container, lines)));
|
||||||
|
|
||||||
editButton.onClick(() -> {
|
editButton.onClick(() -> {
|
||||||
Entry job = selectedEntries.iterator().next();
|
Entry job = selectedEntries.iterator().next();
|
||||||
@@ -267,15 +269,12 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private GuiRenderSettings addButtonClicked() {
|
private Result<GuiRenderSettings, String[]> addButtonClicked() {
|
||||||
Timeline timeline = timelineSupplier.get();
|
return timelineSupplier.get().mapOk(timeline -> {
|
||||||
if (timeline != null) {
|
|
||||||
GuiRenderSettings popup = addJob(timeline);
|
GuiRenderSettings popup = addJob(timeline);
|
||||||
popup.open();
|
popup.open();
|
||||||
return popup;
|
return popup;
|
||||||
} else {
|
});
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GuiRenderSettings addJob(Timeline timeline) {
|
public GuiRenderSettings addJob(Timeline timeline) {
|
||||||
@@ -317,9 +316,8 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> implements
|
|||||||
@Override
|
@Override
|
||||||
public void open() {
|
public void open() {
|
||||||
if (jobs.isEmpty() && timelineSupplier != null) {
|
if (jobs.isEmpty() && timelineSupplier != null) {
|
||||||
if (addButtonClicked() == null) {
|
addButtonClicked().ifErr(lines ->
|
||||||
close();
|
GuiInfoPopup.open(container, lines).onClosed(this::close));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.google.common.util.concurrent.Futures;
|
|||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.Result;
|
||||||
import com.replaymod.core.utils.Utils;
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.pathing.gui.GuiKeyframeRepository;
|
import com.replaymod.pathing.gui.GuiKeyframeRepository;
|
||||||
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
|
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
|
||||||
@@ -101,8 +102,6 @@ public class GuiPathing {
|
|||||||
public final GuiButton renderButton = new GuiButton().onClick(new Runnable() {
|
public final GuiButton renderButton = new GuiButton().onClick(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Timeline timeline = preparePathsForPlayback(false);
|
|
||||||
if (timeline == null) return;
|
|
||||||
GuiScreen screen = GuiRenderSettings.createBaseScreen();
|
GuiScreen screen = GuiRenderSettings.createBaseScreen();
|
||||||
new GuiRenderQueue(screen, replayHandler, () -> preparePathsForPlayback(false)) {
|
new GuiRenderQueue(screen, replayHandler, () -> preparePathsForPlayback(false)) {
|
||||||
@Override
|
@Override
|
||||||
@@ -263,7 +262,10 @@ public class GuiPathing {
|
|||||||
} else {
|
} else {
|
||||||
boolean ignoreTimeKeyframes = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
|
boolean ignoreTimeKeyframes = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
|
||||||
|
|
||||||
Timeline timeline = preparePathsForPlayback(ignoreTimeKeyframes);
|
Timeline timeline = preparePathsForPlayback(ignoreTimeKeyframes).okOrElse(err -> {
|
||||||
|
GuiInfoPopup.open(overlay, err);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
if (timeline == null) return;
|
if (timeline == null) return;
|
||||||
|
|
||||||
Path timePath = new SPTimeline(timeline).getTimePath();
|
Path timePath = new SPTimeline(timeline).getTimePath();
|
||||||
@@ -497,11 +499,12 @@ public class GuiPathing {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Timeline preparePathsForPlayback(boolean ignoreTimeKeyframes) {
|
private Result<Timeline, String[]> preparePathsForPlayback(boolean ignoreTimeKeyframes) {
|
||||||
SPTimeline spTimeline = mod.getCurrentTimeline();
|
SPTimeline spTimeline = mod.getCurrentTimeline();
|
||||||
|
|
||||||
if (!validatePathsForPlayback(spTimeline, ignoreTimeKeyframes)) {
|
String[] errors = validatePathsForPlayback(spTimeline, ignoreTimeKeyframes);
|
||||||
return null;
|
if (errors != null) {
|
||||||
|
return Result.err(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -509,24 +512,23 @@ public class GuiPathing {
|
|||||||
String serialized = serialization.serialize(Collections.singletonMap("", spTimeline.getTimeline()));
|
String serialized = serialization.serialize(Collections.singletonMap("", spTimeline.getTimeline()));
|
||||||
Timeline timeline = serialization.deserialize(serialized).get("");
|
Timeline timeline = serialization.deserialize(serialized).get("");
|
||||||
timeline.getPaths().forEach(Path::updateAll);
|
timeline.getPaths().forEach(Path::updateAll);
|
||||||
return timeline;
|
return Result.ok(timeline);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
error(LOGGER, replayHandler.getOverlay(), CrashReport.create(t, "Cloning timeline"), () -> {});
|
error(LOGGER, replayHandler.getOverlay(), CrashReport.create(t, "Cloning timeline"), () -> {});
|
||||||
return null;
|
return Result.err(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validatePathsForPlayback(SPTimeline timeline, boolean ignoreTimeKeyframes) {
|
private String[] validatePathsForPlayback(SPTimeline timeline, boolean ignoreTimeKeyframes) {
|
||||||
timeline.getTimeline().getPaths().forEach(Path::updateAll);
|
timeline.getTimeline().getPaths().forEach(Path::updateAll);
|
||||||
|
|
||||||
// Make sure there are at least two position keyframes
|
// Make sure there are at least two position keyframes
|
||||||
if (timeline.getPositionPath().getSegments().isEmpty()) {
|
if (timeline.getPositionPath().getSegments().isEmpty()) {
|
||||||
GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes");
|
return new String[]{ "replaymod.chat.morekeyframes" };
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ignoreTimeKeyframes) {
|
if (ignoreTimeKeyframes) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure time keyframes's values are monotonically increasing
|
// Make sure time keyframes's values are monotonically increasing
|
||||||
@@ -535,22 +537,21 @@ public class GuiPathing {
|
|||||||
int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new);
|
||||||
if (time < lastTime) {
|
if (time < lastTime) {
|
||||||
// We are going backwards in time
|
// We are going backwards in time
|
||||||
GuiInfoPopup.open(replayHandler.getOverlay(),
|
return new String[]{
|
||||||
"replaymod.error.negativetime1",
|
"replaymod.error.negativetime1",
|
||||||
"replaymod.error.negativetime2",
|
"replaymod.error.negativetime2",
|
||||||
"replaymod.error.negativetime3");
|
"replaymod.error.negativetime3"
|
||||||
return false;
|
};
|
||||||
}
|
}
|
||||||
lastTime = time;
|
lastTime = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there are at least two time keyframes
|
// Make sure there are at least two time keyframes
|
||||||
if (timeline.getTimePath().getSegments().isEmpty()) {
|
if (timeline.getTimePath().getSegments().isEmpty()) {
|
||||||
GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes");
|
return new String[]{ "replaymod.chat.morekeyframes" };
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void zoomTimeline(double factor) {
|
public void zoomTimeline(double factor) {
|
||||||
|
|||||||
Reference in New Issue
Block a user