Merge branch '1.8' into 1.8.9

91573d9 Update jGui, ReplayStudio and Translations
d71358b Add confirmation dialog before overwriting path presets (fixes #65)
1a55983 Move addCallback method from integration test Utils to main Utils
18c5bcd Be more cooperative with other mods on the ingame menu (fixes #42)
c054fe8 Register keys for simplepathing only once (fixes #63)
f13297c Fix default interpolator handling in keyframe gui and after loading (fixes #64)
391f304 Show error popup if entity tracker fails to load
0e0eaaa Fix entity tracker being set even if it failed loading (fixes #50)
a34bbbc Fix NPE when spectating a player without a camera entity
60879fb Change local class to be an inner class because Srg2Source breaks with it
dfafbec Load translations from github repo, only reload language not all resource packs
8a6ec51 Add missing tooltips to player overview and various missing chat messages
5677fc5 Re-add warning to replay editor
748a91e Fix missing tooltips on fav/like/dislike buttons in replay center
ee24866 Fix upload replay button not being updated when name/tags change
dba085c Move translations into separate repo
3f0e3e7 Fix NPE when receiving Replay|Restrict messages (fixes #16 GH)
40e1d85 Fix crash when saving the last keyframe in the edit gui (fixes #61)
03aada1 Update Mixin to 0.6.8 (fixes #9 GH)
0c1dc65 Fix interpolator being lost when moving keyframe to the end (fixes #62)
fcbbbc9 Add integration test. Run with ./gradlew runIntegrationTest
fe6ded0 Fix movement of keyframe via GuiEditKeyframe not updating selected keyframe
f58fa8f Fix Login GUI not respecting other GUIs opened on startup
4fc3a31 Fix OpenEye being installed into working dir instead of mcDataDir
This commit is contained in:
Jonas Herzig
2017-05-31 09:36:46 +02:00
46 changed files with 1337 additions and 740 deletions

View File

@@ -14,6 +14,7 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.logging.log4j.Logger;
import org.lwjgl.input.Keyboard;
@Mod(modid = ReplayModSimplePathing.MOD_ID,
version = "@MOD_VERSION@",
@@ -22,6 +23,9 @@ import org.apache.logging.log4j.Logger;
public class ReplayModSimplePathing {
public static final String MOD_ID = "replaymod-simplepathing";
@Mod.Instance(MOD_ID)
public static ReplayModSimplePathing instance;
private ReplayMod core;
public static Logger LOGGER;
@@ -39,11 +43,24 @@ public class ReplayModSimplePathing {
PathPreview pathPreview = new PathPreview(this);
pathPreview.register();
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, () -> {
if (guiPathing != null) guiPathing.keyframeRepoButtonPressed();
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> {
if (guiPathing != null) guiPathing.clearKeyframesButtonPressed();
});
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
if (guiPathing != null) guiPathing.syncTimeButtonPressed();
});
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
if (guiPathing != null) guiPathing.deleteButtonPressed();
});
}
@SubscribeEvent
public void postReplayOpen(ReplayOpenEvent.Post event) {
currentTimeline = new SPTimeline();
clearCurrentTimeline();
guiPathing = new GuiPathing(core, this, event.getReplayHandler());
}
@@ -82,11 +99,11 @@ public class ReplayModSimplePathing {
public void setCurrentTimeline(SPTimeline newTimeline) {
selectedPath = null;
currentTimeline = newTimeline;
updateDefaultInterpolatorType();
}
public void clearCurrentTimeline() {
setCurrentTimeline(new SPTimeline());
updateDefaultInterpolatorType();
}
public SPTimeline getCurrentTimeline() {

View File

@@ -71,7 +71,7 @@ public class SPTimeline implements PathingRegistry {
@Getter
private EntityPositionTracker entityTracker;
private InterpolatorType defaultInterpolatorType = InterpolatorType.fromString("invalid string returns default");
private InterpolatorType defaultInterpolatorType;
public SPTimeline() {
this(createInitialTimeline());
@@ -340,13 +340,15 @@ public class SPTimeline implements PathingRegistry {
Optional<Interpolator> firstInterpolator =
path.getSegments().stream().findFirst().map(PathSegment::getInterpolator);
// The interpolator of the previous segment
Optional<Interpolator> interpolatorBefore =
path.getSegments().stream().filter(s -> s.getEndKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator);
// The interpolator of the following segment
Optional<Interpolator> interpolatorAfter =
path.getSegments().stream().filter(s -> s.getStartKeyframe() == keyframe).findFirst().map(PathSegment::getInterpolator);
// The interpolator that will be lost once we remove the old keyframe and has to be restored afterwards
Optional<Interpolator> lostInterpolator = path.getSegments().stream().filter(s -> {
// If this is the last keyframe,
if (Iterables.getLast(path.getKeyframes()) == keyframe) {
return s.getEndKeyframe() == keyframe; // the previous interpolator will be lost
} else { // otherwise
return s.getStartKeyframe() == keyframe; // the following interpolator will be lost
}
}).findFirst().map(PathSegment::getInterpolator);
// First remove the old keyframe
Change removeChange = create(path, keyframe);
@@ -369,18 +371,27 @@ public class SPTimeline implements PathingRegistry {
Keyframe newKf = path.getKeyframe(newTime);
if (Iterables.getLast(path.getKeyframes()) != newKf) { // Unless this is the last keyframe
// the interpolator of the following segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorAfter.<Change>flatMap(interpolator ->
restoreInterpolatorChange = lostInterpolator.<Change>flatMap(interpolator ->
path.getSegments().stream().filter(s -> s.getStartKeyframe() == newKf).findFirst().map(segment ->
SetInterpolator.create(segment, interpolator)
)
).orElseGet(CombinedChange::create);
} else { // If it is the last keyframe however,
// the interpolator of the previous segment has been lost and needs to be restored
restoreInterpolatorChange = interpolatorBefore.<Change>flatMap(interpolator ->
path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf).findFirst().map(segment ->
SetInterpolator.create(segment, interpolator)
)
).orElseGet(CombinedChange::create);
restoreInterpolatorChange = path.getSegments().stream().filter(s -> s.getEndKeyframe() == newKf)
.findFirst().flatMap(segment -> lostInterpolator.map(interpolator -> {
// additionally, if the interpolation of this keyframe was set to explicit, that property
// has to be transferred to the start keyframe of the new segment
if (newKf.getValue(ExplicitInterpolationProperty.PROPERTY).isPresent()) {
return CombinedChange.create(
SetInterpolator.create(segment, interpolator),
UpdateKeyframeProperties.create(path, segment.getStartKeyframe())
.setValue(ExplicitInterpolationProperty.PROPERTY, ObjectUtils.NULL).done()
);
} else {
return SetInterpolator.create(segment, interpolator);
}
})).orElseGet(CombinedChange::create);
}
restoreInterpolatorChange.apply(timeline);

View File

@@ -109,6 +109,9 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
if (newTime != time) {
change = CombinedChange.createFromApplied(change,
gui.getMod().getCurrentTimeline().moveKeyframe(path, time, newTime));
if (gui.getMod().getSelectedPath() == path && gui.getMod().getSelectedTime() == time) {
gui.getMod().setSelected(path, newTime);
}
}
gui.getMod().getCurrentTimeline().getTimeline().pushChange(change);
close();
@@ -252,6 +255,10 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
xField.getDouble(), yField.getDouble(), zField.getDouble(),
yawField.getFloat(), pitchField.getFloat(), rollField.getFloat()
);
if (interpolationPanel.getSettingsPanel() == null) {
// The last keyframe doesn't have interpolator settings because there is no segment following it
return positionChange;
}
Interpolator interpolator = interpolationPanel.getSettingsPanel().createInterpolator();
if (interpolationPanel.getInterpolatorType() == InterpolatorType.DEFAULT) {
return CombinedChange.createFromApplied(positionChange, timeline.setInterpolatorToDefault(time),
@@ -303,6 +310,7 @@ public abstract class GuiEditKeyframe<T extends GuiEditKeyframe<T>> extends Abst
dropdown.setSelected(type); // trigger the callback once to display settings panel
} else {
setSettingsPanel(InterpolatorType.DEFAULT);
type = InterpolatorType.DEFAULT;
}
if (getInterpolatorTypeNoDefault(type).getInterpolatorClass().isInstance(interpolator)) {
//noinspection unchecked

View File

@@ -1,10 +1,12 @@
package com.replaymod.simplepathing.gui;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.pathing.gui.GuiKeyframeRepository;
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
import com.replaymod.pathing.properties.CameraProperties;
@@ -255,6 +257,7 @@ public class GuiPathing {
}
};
private final ReplayMod core;
private final ReplayModSimplePathing mod;
private final ReplayHandler replayHandler;
private final RealtimeTimelinePlayer player;
@@ -264,6 +267,7 @@ public class GuiPathing {
private SettableFuture<Void> entityTrackerFuture;
public GuiPathing(final ReplayMod core, final ReplayModSimplePathing mod, final ReplayHandler replayHandler) {
this.core = core;
this.mod = mod;
this.replayHandler = replayHandler;
this.player = new RealtimeTimelinePlayer(replayHandler);
@@ -300,9 +304,15 @@ public class GuiPathing {
ListenableFuture<Void> future = player.start(mod.getCurrentTimeline().getTimeline(), startTime);
overlay.setCloseable(false);
overlay.setMouseVisible(true);
core.printInfoToChat("replaymod.chat.pathstarted");
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void result) {
if (future.isCancelled()) {
core.printInfoToChat("replaymod.chat.pathinterrupted");
} else {
core.printInfoToChat("replaymod.chat.pathfinished");
}
overlay.setCloseable(true);
timePath.setActive(true);
}
@@ -393,94 +403,90 @@ public class GuiPathing {
}
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.keyframerepository", Keyboard.KEY_X, new Runnable() {
@Override
public void run() {
if (!overlay.isVisible()) {
return;
}
try {
GuiKeyframeRepository gui = new GuiKeyframeRepository(
mod.getCurrentTimeline(), replayHandler.getReplayFile(), mod.getCurrentTimeline().getTimeline());
Futures.addCallback(gui.getFuture(), new FutureCallback<Timeline>() {
@Override
public void onSuccess(Timeline result) {
if (result != null) {
mod.setCurrentTimeline(new SPTimeline(result));
}
}
startLoadingEntityTracker();
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
core.printWarningToChat("Error loading timeline: " + t.getMessage());
}
});
gui.display();
} catch (IOException e) {
e.printStackTrace();
core.printWarningToChat("Error loading timeline: " + e.getMessage());
}
}
});
core.getKeyBindingRegistry().registerKeyBinding("replaymod.input.clearkeyframes", Keyboard.KEY_C, () -> {
GuiYesNoPopup popup = GuiYesNoPopup.open(overlay,
new GuiLabel().setI18nText("replaymod.gui.clearcallback.title").setColor(Colors.BLACK)
).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
public void keyframeRepoButtonPressed() {
try {
GuiKeyframeRepository gui = new GuiKeyframeRepository(
mod.getCurrentTimeline(), replayHandler.getReplayFile(), mod.getCurrentTimeline().getTimeline());
Futures.addCallback(gui.getFuture(), new FutureCallback<Timeline>() {
@Override
public void onSuccess(Boolean delete) {
if (delete) {
mod.clearCurrentTimeline();
if (entityTracker != null) {
mod.getCurrentTimeline().setEntityTracker(entityTracker);
}
public void onSuccess(Timeline result) {
if (result != null) {
mod.setCurrentTimeline(new SPTimeline(result));
}
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
core.printWarningToChat("Error loading timeline: " + t.getMessage());
}
});
});
gui.display();
} catch (IOException e) {
e.printStackTrace();
core.printWarningToChat("Error loading timeline: " + e.getMessage());
}
}
core.getKeyBindingRegistry().registerRepeatedKeyBinding("replaymod.input.synctimeline", Keyboard.KEY_V, () -> {
// Current replay time
int time = replayHandler.getReplaySender().currentTimeStamp();
// Position of the cursor
int cursor = timeline.getCursorPosition();
// Get the last time keyframe before the cursor
mod.getCurrentTimeline().getTimePath().getKeyframes().stream()
.filter(it -> it.getTime() <= cursor).reduce((__, last) -> last).ifPresent(keyframe -> {
// Cursor position at the keyframe
int keyframeCursor = (int) keyframe.getTime();
// Replay time at the keyframe
// This is a keyframe from the time path, so it _should_ always have a time property
int keyframeTime = keyframe.getValue(TimestampProperty.PROPERTY).get();
// Replay time passed
int timePassed = time - keyframeTime;
// Speed (set to 1 when shift is held)
double speed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 1 : overlay.getSpeedSliderValue();
// Cursor time passed
int cursorPassed = (int) (timePassed / speed);
// Move cursor to new position
timeline.setCursorPosition(keyframeCursor + cursorPassed);
// Deselect keyframe to allow the user to add a new one right away
mod.setSelected(null, 0);
});
});
core.getKeyBindingRegistry().registerRaw(Keyboard.KEY_DELETE, () -> {
if (!overlay.isVisible()) {
return;
public void clearKeyframesButtonPressed() {
GuiYesNoPopup popup = GuiYesNoPopup.open(replayHandler.getOverlay(),
new GuiLabel().setI18nText("replaymod.gui.clearcallback.title").setColor(Colors.BLACK)
).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean delete) {
if (delete) {
mod.clearCurrentTimeline();
if (entityTracker != null) {
mod.getCurrentTimeline().setEntityTracker(entityTracker);
}
}
}
if (mod.getSelectedPath() != null) {
updateKeyframe(mod.getSelectedPath());
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
}
public void syncTimeButtonPressed() {
// Current replay time
int time = replayHandler.getReplaySender().currentTimeStamp();
// Position of the cursor
int cursor = timeline.getCursorPosition();
// Get the last time keyframe before the cursor
mod.getCurrentTimeline().getTimePath().getKeyframes().stream()
.filter(it -> it.getTime() <= cursor).reduce((__, last) -> last).ifPresent(keyframe -> {
// Cursor position at the keyframe
int keyframeCursor = (int) keyframe.getTime();
// Replay time at the keyframe
// This is a keyframe from the time path, so it _should_ always have a time property
int keyframeTime = keyframe.getValue(TimestampProperty.PROPERTY).get();
// Replay time passed
int timePassed = time - keyframeTime;
// Speed (set to 1 when shift is held)
double speed = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) ? 1 : replayHandler.getOverlay().getSpeedSliderValue();
// Cursor time passed
int cursorPassed = (int) (timePassed / speed);
// Move cursor to new position
timeline.setCursorPosition(keyframeCursor + cursorPassed);
// Deselect keyframe to allow the user to add a new one right away
mod.setSelected(null, 0);
});
}
public void deleteButtonPressed() {
if (mod.getSelectedPath() != null) {
updateKeyframe(mod.getSelectedPath());
}
}
private void startLoadingEntityTracker() {
Preconditions.checkState(entityTrackerFuture == null);
// Start loading entity tracker
entityTrackerFuture = SettableFuture.create();
new Thread(() -> {
@@ -493,12 +499,13 @@ public class GuiPathing {
}
});
logger.info("Loaded entity tracker in " + (System.currentTimeMillis() - start) + "ms");
} catch (IOException e) {
} catch (Throwable e) {
logger.error("Loading entity tracker:", e);
mod.getCore().runLater(() -> {
mod.getCore().printWarningToChat("Error loading entity tracker: %s", e.getLocalizedMessage());
entityTrackerFuture.setException(e);
});
return;
}
entityTracker = tracker;
mod.getCore().runLater(() -> {
@@ -557,7 +564,9 @@ public class GuiPathing {
@Override
public void onFailure(@Nonnull Throwable t) {
popup.close();
String message = "Failed to load entity tracker, pathing will be unavailable.";
GuiReplayOverlay overlay = replayHandler.getOverlay();
Utils.error(LOGGER, overlay, CrashReport.makeCrashReport(t, message), popup::close);
}
});
return false;