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:
Jonas Herzig
2021-07-24 11:54:14 +02:00
parent 3f4dae05db
commit 83cb459957
3 changed files with 171 additions and 30 deletions

View 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));
}
}
}

View File

@@ -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;
} }

View File

@@ -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) {