Add ReplayStudio as subproject

Move large parts of pathing to ReplayStudio
Upgrade to Java 8
This commit is contained in:
johni0702
2016-07-27 18:15:27 +02:00
parent 25582b6a51
commit 372521f319
69 changed files with 138 additions and 3086 deletions

View File

@@ -2,6 +2,7 @@ package com.replaymod.core;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.replay.ReplaySender;
import com.replaymod.replaystudio.util.I18n;
import eu.crushedpixel.replaymod.chat.ChatMessageHandler;
import eu.crushedpixel.replaymod.events.handlers.CrosshairRenderHandler;
import eu.crushedpixel.replaymod.events.handlers.GuiEventHandler;
@@ -133,6 +134,8 @@ public class ReplayMod {
// Unfortunately lwjgl uses static methods so we have to make use of magic init calls as well
OpenGLUtils.init();
I18n.setI18n(net.minecraft.client.resources.I18n::format);
config = new Configuration(event.getSuggestedConfigurationFile());
config.load();
settingsRegistry.setConfiguration(config);

View File

@@ -9,9 +9,9 @@ import com.replaymod.online.handler.GuiHandler;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replay.events.ReplayCloseEvent;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;

View File

@@ -1,6 +1,6 @@
package com.replaymod.online.api.replay.holders;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@@ -33,7 +33,7 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import eu.crushedpixel.replaymod.registry.ResourceHelper;
import eu.crushedpixel.replaymod.utils.DurationUtils;
import net.minecraftforge.fml.common.FMLLog;

View File

@@ -4,10 +4,10 @@ import com.google.common.base.Optional;
import com.replaymod.core.ReplayMod;
import com.replaymod.online.api.ApiClient;
import com.replaymod.replay.gui.screen.GuiReplayViewer;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.online.api.replay.FileUploader;
import com.replaymod.online.api.replay.holders.Category;
import eu.crushedpixel.replaymod.gui.GuiConstants;

View File

@@ -1,17 +0,0 @@
package com.replaymod.pathing;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.path.Timeline;
import java.io.IOException;
/**
* Contains mappings required for serialization.
*/
public interface PathingRegistry {
Timeline createTimeline();
void serializeInterpolator(JsonWriter writer, Interpolator interpolator) throws IOException;
Interpolator deserializeInterpolator(JsonReader reader) throws IOException;
}

View File

@@ -1,62 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import lombok.NonNull;
/**
* Adds a new property.
*/
public final class AddKeyframe implements Change {
@NonNull
public static AddKeyframe create(Path path, long time) {
return new AddKeyframe(path.getTimeline().getPaths().indexOf(path), time);
}
AddKeyframe(int path, long time) {
this.path = path;
this.time = time;
}
/**
* Path index
*/
private final int path;
/**
* Time at which the property should be injected.
*/
private final long time;
/**
* Index of the newly created property.
*/
private int index;
private boolean applied;
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
Path path = timeline.getPaths().get(this.path);
Keyframe keyframe = path.insert(time);
index = Iterables.indexOf(path.getKeyframes(), Predicates.equalTo(keyframe));
applied = true;
}
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
Path path = timeline.getPaths().get(this.path);
path.remove(Iterables.get(path.getKeyframes(), index), true);
applied = false;
}
}

View File

@@ -1,35 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Preconditions;
import com.replaymod.pathing.path.Timeline;
import lombok.NonNull;
/**
* Adds a new property.
*/
public final class AddPath implements Change {
@NonNull
public static AddPath create() {
return new AddPath();
}
private boolean applied;
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
timeline.createPath();
applied = true;
}
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
timeline.getPaths().remove(timeline.getPaths().size() - 1);
applied = false;
}
}

View File

@@ -1,29 +0,0 @@
package com.replaymod.pathing.change;
import com.replaymod.pathing.path.Timeline;
/**
* A change to any part of a timeline.
* If {@link #undo(Timeline)} is not called in the reverse order of {@link #apply(Timeline)}, the behavior is unspecified.
* Instances implementing this interfaces must not reference any outside objects required for applying
* as they might be serialized and deserialized at any time.
* After deserialization, only the {@link #apply(Timeline)} method is guaranteed to work.
*/
public interface Change {
/**
* Apply this change.
*
* @param timeline The timeline
* @throws IllegalStateException If already applied.
*/
void apply(Timeline timeline);
/**
* Undo this change.
*
* @param timeline The timeline
* @throws IllegalStateException If not yet applied.
*/
void undo(Timeline timeline);
}

View File

@@ -1,70 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Preconditions;
import com.replaymod.pathing.path.Timeline;
import lombok.NonNull;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
/**
* Represents multiple changes as one change.
*/
public class CombinedChange implements Change {
/**
* Combines the specified changes into one change in the order given.
* All changes must not yet have been applied.
*
* @param changes List of changes
* @return A new CombinedChange instance
*/
@NonNull
public static CombinedChange create(Change... changes) {
return new CombinedChange(Arrays.asList(changes), false);
}
/**
* Combines the specified changes into one change in the order given.
* All changes must have been applied.
*
* @param changes List of changes
* @return A new CombinedChange instance
*/
@NonNull
public static CombinedChange createFromApplied(Change... changes) {
return new CombinedChange(Arrays.asList(changes), true);
}
CombinedChange(List<Change> changeList, boolean applied) {
this.changeList = changeList;
this.applied = applied;
}
private final List<Change> changeList;
private boolean applied;
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
for (Change change : changeList) {
change.apply(timeline);
}
applied = true;
}
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
ListIterator<Change> iterator = changeList.listIterator(changeList.size());
while (iterator.hasPrevious()) {
iterator.previous().undo(timeline);
}
applied = false;
}
}

View File

@@ -1,67 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import lombok.NonNull;
/**
* Removes a property.
*/
public final class RemoveKeyframe implements Change {
@NonNull
public static RemoveKeyframe create(@NonNull Path path, @NonNull Keyframe keyframe) {
return new RemoveKeyframe(path.getTimeline().getPaths().indexOf(path),
Iterables.indexOf(path.getKeyframes(), Predicates.equalTo(keyframe)));
}
RemoveKeyframe(int path, int index) {
this.path = path;
this.index = index;
}
/**
* Path index
*/
private final int path;
/**
* Index of the property to be removed.
*/
private final int index;
private volatile Keyframe removedKeyframe;
private volatile Interpolator removedInterpolator;
private boolean applied;
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
Path path = timeline.getPaths().get(this.path);
if (index < path.getSegments().size()) {
removedInterpolator = Iterables.get(path.getSegments(), index).getInterpolator();
}
path.remove(removedKeyframe = Iterables.get(path.getKeyframes(), index), true);
applied = true;
}
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
Path path = timeline.getPaths().get(this.path);
path.insert(removedKeyframe);
if (removedInterpolator != null) {
Iterables.get(path.getSegments(), index).setInterpolator(removedInterpolator);
}
applied = false;
}
}

View File

@@ -1,50 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Preconditions;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import lombok.NonNull;
/**
* Adds a new property.
*/
public final class RemovePath implements Change {
@NonNull
public static RemovePath create(Path path) {
return new RemovePath(path.getTimeline().getPaths().indexOf(path));
}
RemovePath(int path) {
this.path = path;
}
/**
* Path index
*/
private final int path;
/**
* The removed path
*/
private volatile Path oldPath;
private boolean applied;
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
oldPath = timeline.getPaths().remove(path);
applied = true;
}
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
timeline.getPaths().add(path, oldPath);
applied = false;
}
}

View File

@@ -1,131 +0,0 @@
package com.replaymod.pathing.change;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyGroup;
import lombok.NonNull;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.indexOf;
/**
* Updates some properties of a property.
*/
public final class UpdateKeyframeProperties implements Change {
private static String toId(Property property) {
assert property != null;
PropertyGroup group = property.getGroup();
return (group != null ? group.getId() + ":" : "") + property.getId();
}
public static class Builder {
private final int path;
private final int keyframe;
private final Map<String, Optional<Object>> updates = new HashMap<>();
private Builder(int path, int keyframe) {
this.path = path;
this.keyframe = keyframe;
}
/**
* Set the value for the property at this property.
* If the property is not present, adds it.
*
* @param property The property
* @param value Value of the property, may be {@code null}
* @param <T> Type of the property
* @return {@code this} for chaining
*/
public <T> Builder setValue(Property<T> property, T value) {
updates.put(toId(property), Optional.of((Object) value));
return this;
}
/**
* Remove the specified property from this property.
*
* @param property The property to be removed
* @return {@code this} for chaining
*/
public Builder removeProperty(Property property) {
updates.put(toId(property), Optional.absent());
return this;
}
public UpdateKeyframeProperties done() {
return new UpdateKeyframeProperties(path, keyframe, updates);
}
}
@NonNull
public static UpdateKeyframeProperties.Builder create(@NonNull Path path, @NonNull Keyframe keyframe) {
return new UpdateKeyframeProperties.Builder(path.getTimeline().getPaths().indexOf(path),
indexOf(path.getKeyframes(), equalTo(keyframe)));
}
UpdateKeyframeProperties(int path, int index, Map<String, Optional<Object>> newValues) {
this.path = path;
this.index = index;
this.newValues = newValues;
}
private final int path;
private final int index;
private final Map<String, Optional<Object>> newValues;
private final Map<String, Optional<Object>> oldValues = new HashMap<>();
private boolean applied;
@SuppressWarnings("unchecked")
@Override
public void apply(Timeline timeline) {
Preconditions.checkState(!applied, "Already applied!");
Path path = timeline.getPaths().get(this.path);
Keyframe keyframe = get(path.getKeyframes(), index);
for (Map.Entry<String, Optional<Object>> entry : newValues.entrySet()) {
Property property = timeline.getProperty(entry.getKey());
if (property == null) throw new IllegalStateException("Property " + entry.getKey() + " unknown.");
Optional<Object> newValue = entry.getValue();
oldValues.put(entry.getKey(), keyframe.getValue(property));
if (newValue.isPresent()) {
keyframe.setValue(property, newValue.get());
} else {
keyframe.removeProperty(property);
}
}
applied = true;
}
@SuppressWarnings("unchecked")
@Override
public void undo(Timeline timeline) {
Preconditions.checkState(applied, "Not yet applied!");
Path path = timeline.getPaths().get(this.path);
Keyframe keyframe = get(path.getKeyframes(), index);
for (Map.Entry<String, Optional<Object>> entry : oldValues.entrySet()) {
Property property = timeline.getProperty(entry.getKey());
if (property == null) throw new IllegalStateException("Property " + entry.getKey() + " unknown.");
Optional<Object> oldValue = entry.getValue();
newValues.put(entry.getKey(), keyframe.getValue(property));
if (oldValue.isPresent()) {
keyframe.setValue(property, oldValue.get());
} else {
keyframe.removeProperty(property);
}
}
applied = false;
}
}

View File

@@ -4,10 +4,10 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.PathingRegistry;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.serialize.TimelineSerialization;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.pathing.PathingRegistry;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.replay.ReplayFile;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.*;
@@ -22,7 +22,6 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.replaystudio.replay.ReplayFile;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
@@ -31,7 +30,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
* Gui for loading and saving {@link com.replaymod.pathing.path.Timeline Timelines}.
* Gui for loading and saving {@link Timeline Timelines}.
*/
public class GuiKeyframeRepository extends GuiScreen implements Closeable {
public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT);
@@ -171,7 +170,8 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
private final Map<String, Timeline> timelines = new LinkedHashMap<>();
private final Timeline currentTimeline;
private final SettableFuture<Timeline> future = SettableFuture.create();
private final TimelineSerialization serialization;
private final PathingRegistry registry;
private final ReplayFile replayFile;
private Entry selectedEntry;
@@ -201,10 +201,11 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
}
public GuiKeyframeRepository(PathingRegistry registry, ReplayFile replayFile, Timeline currentTimeline) throws IOException {
this.registry = registry;
this.replayFile = replayFile;
this.currentTimeline = currentTimeline;
this.serialization = new TimelineSerialization(registry, replayFile);
timelines.putAll(serialization.load());
timelines.putAll(replayFile.getTimelines(registry));
for (Map.Entry<String, Timeline> entry : timelines.entrySet()) {
list.getListPanel().addElements(null, new Entry(entry.getKey()));
@@ -228,7 +229,7 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable {
public void save() {
try {
serialization.save(timelines);
replayFile.writeTimelines(registry, timelines);
} catch (IOException e) {
e.printStackTrace();
ReplayMod.instance.printWarningToChat("Error saving timelines: " + e.getMessage());

View File

@@ -1,43 +0,0 @@
package com.replaymod.pathing.impl;
import com.google.common.base.Optional;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.property.Property;
import lombok.RequiredArgsConstructor;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@RequiredArgsConstructor
public class KeyframeImpl implements Keyframe {
private final long time;
private final Map<Property, Object> properties = new HashMap<>();
@Override
public long getTime() {
return time;
}
@SuppressWarnings("unchecked")
@Override
public <T> Optional<T> getValue(Property<T> property) {
return properties.containsKey(property) ? Optional.of((T) properties.get(property)) : Optional.<T>absent();
}
@Override
public <T> void setValue(Property<T> property, T value) {
properties.put(property, value);
}
@Override
public void removeProperty(Property property) {
properties.remove(property);
}
@Override
public Set<Property> getProperties() {
return Collections.unmodifiableSet(properties.keySet());
}
}

View File

@@ -1,186 +0,0 @@
package com.replaymod.pathing.impl;
import com.google.common.base.Optional;
import com.replaymod.pathing.interpolation.InterpolationParameters;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyPart;
import java.util.*;
public class PathImpl implements Path {
private final Timeline timeline;
private Map<Long, Keyframe> keyframes = new TreeMap<>();
private List<PathSegment> segments = new LinkedList<>();
private boolean active = true;
public PathImpl(Timeline timeline) {
this.timeline = timeline;
}
@Override
public Timeline getTimeline() {
return timeline;
}
@Override
public Collection<Keyframe> getKeyframes() {
return Collections.unmodifiableCollection(keyframes.values());
}
@Override
public Collection<PathSegment> getSegments() {
return Collections.unmodifiableCollection(segments);
}
@Override
public void update() {
update(false);
}
@Override
public void updateAll() {
update(false);
}
private void update(boolean force) {
Interpolator interpolator = null;
Map<PropertyPart, InterpolationParameters> parameters = new HashMap<>();
for (PathSegment segment : segments) {
if (segment.getInterpolator() != interpolator) {
interpolator = segment.getInterpolator();
if (force || interpolator.isDirty()) {
parameters = interpolator.bake(parameters);
}
}
}
}
@Override
public <T> Optional<T> getValue(Property<T> property, long time) {
PathSegment segment = getSegment(time);
if (segment != null) {
Interpolator interpolator = segment.getInterpolator();
if (interpolator != null) {
if (interpolator.getKeyframeProperties().contains(property)) {
return interpolator.getValue(property, time);
}
}
}
return Optional.absent();
}
@Override
public Keyframe insert(long time) {
Keyframe keyframe = new KeyframeImpl(time);
insert(keyframe);
return keyframe;
}
@Override
public Keyframe getKeyframe(long time) {
return keyframes.get(time);
}
@Override
public void insert(Keyframe keyframe) {
if (keyframes.containsKey(keyframe.getTime())) {
throw new IllegalStateException("A keyframe at " + keyframe.getTime() + " already exists.");
}
keyframes.put(keyframe.getTime(), keyframe);
if (segments.isEmpty()) {
if (keyframes.size() >= 2) {
Iterator<Keyframe> iter = keyframes.values().iterator();
segments.add(new PathSegmentImpl(iter.next(), iter.next()));
}
return;
}
ListIterator<PathSegment> iter = segments.listIterator();
PathSegment next = iter.next();
if (keyframe.getTime() < next.getStartKeyframe().getTime()) {
iter.previous();
iter.add(new PathSegmentImpl(keyframe, next.getStartKeyframe(), next.getInterpolator()));
}
while (true) {
if (next.getStartKeyframe().getTime() <= keyframe.getTime()
&& next.getEndKeyframe().getTime() >= keyframe.getTime()) {
iter.remove();
next.setInterpolator(null);
iter.add(new PathSegmentImpl(next.getStartKeyframe(), keyframe, next.getInterpolator()));
iter.add(new PathSegmentImpl(keyframe, next.getEndKeyframe(), next.getInterpolator()));
return;
}
if (iter.hasNext()) {
next = iter.next();
} else {
iter.add(new PathSegmentImpl(next.getEndKeyframe(), keyframe, next.getInterpolator()));
return;
}
}
}
@Override
public void remove(Keyframe keyframe, boolean useFirstInterpolator) {
if (keyframes.get(keyframe.getTime()) != keyframe) {
throw new IllegalArgumentException("The keyframe " + keyframe + " is not part of this path.");
}
keyframes.remove(keyframe.getTime());
if (segments.size() < 2) {
for (PathSegment segment : segments) {
segment.setInterpolator(null);
}
segments.clear();
return;
}
ListIterator<PathSegment> iter = segments.listIterator();
while (iter.hasNext()) {
PathSegment next = iter.next();
if (next.getEndKeyframe() == keyframe) {
iter.remove();
if (iter.hasNext()) {
PathSegment next2 = iter.next();
iter.remove();
iter.add(new PathSegmentImpl(next.getStartKeyframe(), next2.getEndKeyframe(),
(useFirstInterpolator ? next : next2).getInterpolator()));
} else {
next.setInterpolator(null);
}
return;
}
if (next.getStartKeyframe() == keyframe) {
next.setInterpolator(null);
iter.remove();
return;
}
}
throw new AssertionError("No segment for keyframe found!");
}
@Override
public void setActive(boolean active) {
this.active = active;
}
@Override
public boolean isActive() {
return active;
}
private PathSegment getSegment(long time) {
for (PathSegment segment : segments) {
if (segment.getStartKeyframe().getTime() <= time && segment.getEndKeyframe().getTime() >= time) {
return segment;
}
}
return null;
}
}

View File

@@ -1,32 +0,0 @@
package com.replaymod.pathing.impl;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.PathSegment;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class PathSegmentImpl implements PathSegment {
private final Keyframe startKeyframe;
private final Keyframe endKeyframe;
private Interpolator interpolator;
public PathSegmentImpl(Keyframe startKeyframe, Keyframe endKeyframe, Interpolator interpolator) {
this.startKeyframe = startKeyframe;
this.endKeyframe = endKeyframe;
setInterpolator(interpolator);
}
@Override
public void setInterpolator(Interpolator interpolator) {
if (this.interpolator != null) {
this.interpolator.removeSegment(this);
}
this.interpolator = interpolator;
if (this.interpolator != null) {
this.interpolator.addSegment(this);
}
}
}

View File

@@ -1,103 +0,0 @@
package com.replaymod.pathing.impl;
import com.google.common.base.Optional;
import com.replaymod.pathing.change.Change;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.property.Property;
import com.replaymod.replay.ReplayHandler;
import java.util.*;
public class TimelineImpl implements Timeline {
private final List<Path> paths = new ArrayList<>();
private Map<String, Property> properties = new HashMap<>();
private Deque<Change> undoStack = new ArrayDeque<>();
private Deque<Change> redoStack = new ArrayDeque<>();
@Override
public List<Path> getPaths() {
return paths;
}
@Override
public Path createPath() {
Path path = new PathImpl(this);
paths.add(path);
return path;
}
@Override
public <T> Optional<T> getValue(Property<T> property, long time) {
for (Path path : paths) {
if (path.isActive()) {
Optional<T> value = path.getValue(property, time);
if (value.isPresent()) {
return value;
}
}
}
return Optional.absent();
}
@Override
public void applyToGame(long time, ReplayHandler replayHandler) {
for (Property<?> property : properties.values()) {
applyToGame(time, replayHandler, property);
}
}
private <T> void applyToGame(long time, ReplayHandler replayHandler, Property<T> property) {
Optional<T> value = getValue(property, time);
if (value.isPresent()) {
property.applyToGame(value.get(), replayHandler);
}
}
@Override
public void registerProperty(Property property) {
String id = (property.getGroup() == null ? "" : property.getGroup().getId() + ":") + property.getId();
properties.put(id, property);
}
@Override
public Property getProperty(String id) {
return properties.get(id);
}
@Override
public void applyChange(Change change) {
change.apply(this);
pushChange(change);
}
@Override
public void pushChange(Change change) {
undoStack.push(change);
redoStack.clear();
}
@Override
public void undoLastChange() {
Change change = undoStack.pop();
change.undo(this);
redoStack.push(change);
}
@Override
public void redoLastChange() {
Change change = redoStack.pop();
change.apply(this);
undoStack.push(change);
}
@Override
public Change peekUndoStack() {
return undoStack.peek();
}
@Override
public Change peekRedoStack() {
return undoStack.peek();
}
}

View File

@@ -1,74 +0,0 @@
package com.replaymod.pathing.interpolation;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyPart;
import java.util.*;
public abstract class AbstractInterpolator implements Interpolator {
private List<PathSegment> segments = new LinkedList<>();
private boolean dirty;
private final Set<Property> properties = new HashSet<>();
@Override
public Collection<Property> getKeyframeProperties() {
return Collections.unmodifiableCollection(properties);
}
public void registerProperty(Property property) {
properties.add(property);
}
@Override
public void addSegment(PathSegment segment) {
segments.add(segment);
dirty = true;
}
@Override
public void removeSegment(PathSegment segment) {
segments.remove(segment);
dirty = true;
}
@Override
public List<PathSegment> getSegments() {
return Collections.unmodifiableList(segments);
}
@Override
public Map<PropertyPart, InterpolationParameters> bake(Map<PropertyPart, InterpolationParameters> parameters) {
if (segments.isEmpty()) throw new IllegalStateException("No segments have been added yet.");
Collections.sort(segments, new Comparator<PathSegment>() {
@Override
public int compare(PathSegment s1, PathSegment s2) {
return Long.compare(s1.getStartKeyframe().getTime(), s2.getStartKeyframe().getTime());
}
});
// Check for continuity
Iterator<PathSegment> iter = segments.iterator();
PathSegment last = iter.next();
while (iter.hasNext()) {
if (last.getEndKeyframe() != (last = iter.next()).getStartKeyframe()) {
throw new IllegalStateException("Segments are not continuous.");
}
}
return bakeInterpolation(parameters);
}
/**
* Bake the interpolation of the current path segments with the specified parameters.
* Order of {@link #getSegments()} is guaranteed.
* @param parameters Map of parameters for some properties
* @return Map of parameters for the next interpolator
*/
protected abstract Map<PropertyPart, InterpolationParameters> bakeInterpolation(Map<PropertyPart, InterpolationParameters> parameters);
@Override
public boolean isDirty() {
return dirty;
}
}

View File

@@ -1,79 +0,0 @@
package com.replaymod.pathing.interpolation;
public class CubicSplineInterpolator extends PolynomialSplineInterpolator {
public CubicSplineInterpolator() {
super(3);
}
@Override
protected void fillMatrix(double[][] matrix, double[] xs, double[] ys, int num, InterpolationParameters params) {
int row = 0;
double x;
if (params != null) {
// Apply previous values
ys[0] = params.getValue();
x = xs[0];
matrix[row][0] = 3 * x * x;
matrix[row][1] = 2 * x;
matrix[row][2] = 1;
matrix[row][num * 4] = params.getVelocity();
row++;
matrix[row][0] = 6 * x;
matrix[row][1] = 2;
matrix[row][num * 4] = params.getAcceleration();
row++;
} else {
// Set second derivative at the first and the last knot to 0
matrix[row][0] = 6 * xs[0];
matrix[row][1] = 2;
row++;
matrix[row][(num - 1) * 4] = 6 * xs[xs.length - 1];
matrix[row][(num - 1) * 4 + 1] = 2;
row++;
}
for (int i = 0; i < num; i++) {
// Each cubic i must produce the correct result at x[i] and x[i+1], that is y[i] and y[i+1]
// Resulting in these linear equations for every value
// a[i] b[i] c[i] d[i] ... y
// (... x[i]³ x[i]² x[i] 1 ... y[i] ...) or f[i](x[i]) = y[i]
// (... x[i+1]³ x[i+1]² x[i+1] 1 ... y[i+1] ...) or f[i](x[i+1]) = y[i+1]
x = xs[i];
matrix[row][i * 4 ] = x * x * x;
matrix[row][i * 4 + 1] = x * x;
matrix[row][i * 4 + 2] = x;
matrix[row][i * 4 + 3] = 1;
matrix[row][num * 4] = ys[i];
row++;
x = xs[i + 1];
matrix[row][i * 4 ] = x * x * x;
matrix[row][i * 4 + 1] = x * x;
matrix[row][i * 4 + 2] = x;
matrix[row][i * 4 + 3] = 1;
matrix[row][num * 4] = ys[i + 1];
row++;
// The first derivative should be defined at all knots
// Therefore two adjacent cubics have to have the same derivative at their common knot
// Linear equation for every value (except the last one)
// a[i] b[i] c[i] d[i] a[i+1] b[i+1] c[i+1] d[i+1]
// (... 3x[i+1]² 2x[i+1] 1 0 3x[i+1]² 2x[i+1] 1 0 ...)
if (i < num - 1) {
x = xs[i + 1];
matrix[row][i * 4] = -(matrix[row][i * 4 + 4] = 3 * x * x);
matrix[row][i * 4 + 1] = -(matrix[row][i * 4 + 5] = 2 * x);
matrix[row][i * 4 + 2] = -(matrix[row][i * 4 + 6] = 1);
row++;
}
// Same for the second derivative
if (i < num - 1) {
x = xs[i + 1];
matrix[row][i * 4] = -(matrix[row][i * 4 + 4] = 6 * x);
matrix[row][i * 4 + 1] = -(matrix[row][i * 4 + 5] = 2);
row++;
}
}
}
}

View File

@@ -1,28 +0,0 @@
package com.replaymod.pathing.interpolation;
import lombok.Data;
/**
* Parameters which can be used to make an interpolation more fitting to the previous one.
*/
@Data
public class InterpolationParameters {
/**
* The final value.
* Should be the same as for the final property.
* If it differs though, this value should be preferred.
*/
private final double value;
/**
* Velocity at the end of the interpolation.
* Normally this is the first derivative of the interpolating function at the end of the interpolation.
*/
private final double velocity;
/**
* Acceleration at the end of the interpolation.
* Normally this is the second derivative of the interpolating function at the end of the interpolation.
*/
private final double acceleration;
}

View File

@@ -1,89 +0,0 @@
package com.replaymod.pathing.interpolation;
import com.google.common.base.Optional;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyPart;
import lombok.NonNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Interpolates multiple keyframes. Also provides all Properties it can handle.
* Each Interpolator may provide certain outputs for the next Interpolator.
* Interpolators are evaluated first to last.
*/
public interface Interpolator {
/**
* Returns a collection of all properties applicable for this Interpolator.
*
* @return Collection of properties or empty collection if none
*/
@NonNull
Collection<Property> getKeyframeProperties();
/**
* Add the specified path segment to this interpolator.
* <p/>
* This method should be called by the PathSegment itself.
*
* @param segment The path segments
*/
void addSegment(PathSegment segment);
/**
* Remove the specified path segment from this interpolator.
* <p/>
* This method should be called by the PathSegment itself.
*
* @param segment The path segments
*/
void removeSegment(PathSegment segment);
/**
* Returns an immutable list of all path segments handled by this interpolator.
* Ordering is only guaranteed after {@link #bake(Map)} has been called and only until
* the list is modified.
*
* @return List of path segments or empty list if none
*/
@NonNull
List<PathSegment> getSegments();
/**
* Bake the interpolation of the current path segments with the specified parameters.
* Note that when this method is called directly, the path might need to be updated via {@link Path#update()}
*
* @param parameters Map of parameters for some properties
* @return Map of parameters for the next interpolator
* @throws IllegalStateException If no segments have been set yet
* or the segments are not continuous
*/
@NonNull
Map<PropertyPart, InterpolationParameters> bake(Map<PropertyPart, InterpolationParameters> parameters);
/**
* Returns whether the segments handled by this interpolator have changed since the last
* call of {@link #bake(Map)}.
* This only includes the segments themselves, not the properties of their keyframes, those
* have to be tracked manually.
* @return {@code true} if segments have changed, {@code false} otherwise
*/
boolean isDirty();
/**
* Return the value of the property at the specified point in time.
*
* @param property The property
* @param time Time in milliseconds since the start
* @param <T> Type of the property
* @return Optional value of the property
* @throws IllegalStateException If {@link #bake(Map)} has not yet been called
* or {@link #addSegment(PathSegment)}/{@link #removeSegment(PathSegment)}
* has been changed since the last bake
*/
<T> Optional<T> getValue(Property<T> property, long time);
}

View File

@@ -1,89 +0,0 @@
package com.replaymod.pathing.interpolation;
import com.google.common.base.Optional;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyPart;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
public class LinearInterpolator extends AbstractInterpolator {
private Map<Property, Set<Keyframe>> framesToProperty = new HashMap<>();
private void addToMap(Property property, Keyframe keyframe) {
Set<Keyframe> set = framesToProperty.get(property);
if (set == null) {
framesToProperty.put(property, set = new LinkedHashSet<>());
}
set.add(keyframe);
}
@SuppressWarnings("unchecked")
@Override
protected Map<PropertyPart, InterpolationParameters> bakeInterpolation(Map<PropertyPart, InterpolationParameters> parameters) {
framesToProperty.clear();
for (PathSegment segment : getSegments()) {
for (Property property : getKeyframeProperties()) {
if (segment.getStartKeyframe().getValue(property).isPresent()) {
addToMap(property, segment.getStartKeyframe());
}
if (segment.getEndKeyframe().getValue(property).isPresent()) {
addToMap(property, segment.getEndKeyframe());
}
}
}
Keyframe lastKeyframe = getSegments().get(getSegments().size() - 1).getEndKeyframe();
Map<PropertyPart, InterpolationParameters> lastParameters = new HashMap<>();
for (Property<?> property : getKeyframeProperties()) {
Optional optionalValue = lastKeyframe.getValue(property);
if (optionalValue.isPresent()) {
Object value = optionalValue.get();
for (PropertyPart part : property.getParts()) {
lastParameters.put(part, new InterpolationParameters(part.toDouble(value), 1, 0));
}
}
}
return lastParameters;
}
@Override
public <T> Optional<T> getValue(Property<T> property, long time) {
Set<Keyframe> kfSet = framesToProperty.get(property);
if (kfSet == null) {
return Optional.absent();
}
Keyframe kfBefore = null, kfAfter = null;
for (Keyframe keyframe : kfSet) {
if (keyframe.getTime() == time) {
return keyframe.getValue(property);
} else if (keyframe.getTime() < time) {
kfBefore = keyframe;
} else if (keyframe.getTime() > time) {
kfAfter = keyframe;
break;
}
}
if (kfBefore == null || kfAfter == null) {
return Optional.absent();
}
T valueBefore = kfBefore.getValue(property).get();
T valueAfter = kfAfter.getValue(property).get();
double fraction = (time - kfBefore.getTime()) / (double) (kfAfter.getTime() - kfBefore.getTime());
T interpolated = valueBefore;
for (PropertyPart<T> part : property.getParts()) {
if (part.isInterpolatable()) {
double before = part.toDouble(valueBefore);
double after = part.toDouble(valueAfter);
interpolated = part.fromDouble(interpolated, (after - before) * fraction + before);
}
}
return Optional.of(interpolated);
}
}

View File

@@ -1,232 +0,0 @@
package com.replaymod.pathing.interpolation;
import com.google.common.base.Optional;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.property.Property;
import com.replaymod.pathing.property.PropertyPart;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
public abstract class PolynomialSplineInterpolator extends AbstractInterpolator {
private final int degree;
private Map<Property<?>, Set<Keyframe>> framesToProperty = new HashMap<>();
private Map<PropertyPart, Polynomials> polynomials = new HashMap<>();
protected PolynomialSplineInterpolator(int degree) {
this.degree = degree;
}
@Override
protected Map<PropertyPart, InterpolationParameters> bakeInterpolation(Map<PropertyPart, InterpolationParameters> parameters) {
framesToProperty.clear();
for (PathSegment segment : getSegments()) {
for (Property property : getKeyframeProperties()) {
if (segment.getStartKeyframe().getValue(property).isPresent()) {
addToMap(framesToProperty, property, segment.getStartKeyframe());
}
if (segment.getEndKeyframe().getValue(property).isPresent()) {
addToMap(framesToProperty, property, segment.getEndKeyframe());
}
}
}
polynomials.clear();
parameters = new HashMap<>(parameters);
for (Map.Entry<Property<?>, Set<Keyframe>> entry : framesToProperty.entrySet()) {
prepareProperty(entry.getKey(), entry.getValue(), parameters);
}
return parameters;
}
private <U> void prepareProperty(Property<U> property, Set<Keyframe> keyframes, Map<PropertyPart, InterpolationParameters> parameters) {
for (PropertyPart<U> part : property.getParts()) {
if (part.isInterpolatable()) {
double[] time = new double[keyframes.size()];
double[] values = new double[keyframes.size()];
int i = 0;
for (Keyframe keyframe : keyframes) {
time[i] = keyframe.getTime();
values[i++] = part.toDouble(keyframe.getValue(property).get());
}
Polynomials polynomials = calcPolynomials(time, values, parameters.get(part));
double lastTime = time[time.length - 1];
Polynomial lastPolynomial = polynomials.polynomials[polynomials.polynomials.length - 1];
double lastValue = lastPolynomial.eval(lastTime) + polynomials.yOffset;
double lastVelocity = (lastPolynomial = lastPolynomial.derivative()).eval(lastTime);
double lastAcceleration = lastPolynomial.derivative().eval(lastTime);
parameters.put(part, new InterpolationParameters(lastValue, lastVelocity, lastAcceleration));
this.polynomials.put(part, polynomials);
}
}
}
private void addToMap(Map<Property<?>, Set<Keyframe>> map, Property property, Keyframe keyframe) {
Set<Keyframe> set = map.get(property);
if (set == null) {
map.put(property, set = new LinkedHashSet<>());
}
set.add(keyframe);
}
protected Polynomials calcPolynomials(double[] xs, double[] ys, InterpolationParameters params) {
int unknowns = degree + 1;
int num = xs.length - 1;
if (num == 0) {
return new Polynomials(0, new Polynomial[]{new Polynomial(new double[]{ys[0]})});
}
double yOffset;
{
double total = 0;
for (double y : ys) {
total += y;
}
yOffset = total / ys.length;
for (int i = 0; i < ys.length; i++) {
ys[i] -= yOffset;
}
for (int i = 0; i < xs.length; i++) {
xs[i] /= 1000;
}
}
// We want to find cubic equations y = ax³ + bx² + cx + d, one for each pair of values
double[][] matrix = new double[num * unknowns][num * unknowns + 1];
fillMatrix(matrix, xs, ys, num, params);
solveMatrix(matrix);
Polynomial[] polynomials = new Polynomial[num];
for (int i = 0; i < num; i++) {
double[] coefficients = new double[degree + 1];
for (int j = 0; j <= degree; j++) {
coefficients[j] = matrix[i * unknowns + j][num * unknowns];
}
polynomials[i] = new Polynomial(coefficients);
}
return new Polynomials(yOffset, polynomials);
}
protected abstract void fillMatrix(double[][] matrix, double[] xs, double[] ys, int num, InterpolationParameters params);
protected static void solveMatrix(double[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
if (matrix[i][i] == 0) {
for (int j = i + 1; j < matrix.length; j++) {
if (matrix[j][i] != 0) {
double[] s = matrix[j];
matrix[j] = matrix[i];
matrix[i] = s;
break;
}
}
}
double factor = matrix[i][i];
if (factor != 1) {
matrix[i][i] = 1;
for (int j = i + 1; j < matrix[i].length; j++) {
matrix[i][j] /= factor;
}
}
for (int j = i + 1; j < matrix.length; j++) {
factor = matrix[j][i];
if (factor != 0) {
matrix[j][i] = 0;
for (int k = i + 1; k < matrix[j].length; k++) {
matrix[j][k] = matrix[j][k] - matrix[i][k] * factor;
}
}
}
}
for (int i = matrix.length - 1; i >= 0; i--) {
for (int j = i - 1; j >= 0; j--) {
if (matrix[j][i] != 0) {
int k = matrix[j].length - 1;
matrix[j][k] -= matrix[j][i] / matrix[i][i] * matrix[i][k];
matrix[j][i] = 0;
}
}
}
}
@Override
public <T> Optional<T> getValue(Property<T> property, long time) {
Set<Keyframe> kfSet = framesToProperty.get(property);
if (kfSet == null) {
return Optional.absent();
}
Keyframe kfBefore = null, kfAfter = null;
int index = 0;
for (Keyframe keyframe : kfSet) {
if (keyframe.getTime() == time) {
return keyframe.getValue(property);
} else if (keyframe.getTime() < time) {
kfBefore = keyframe;
} else if (keyframe.getTime() > time) {
kfAfter = keyframe;
index--;
break;
}
index++;
}
if (kfBefore == null || kfAfter == null) {
return Optional.absent();
}
T interpolated = kfBefore.getValue(property).get();
for (PropertyPart<T> part : property.getParts()) {
if (part.isInterpolatable()) {
interpolated = part.fromDouble(interpolated, polynomials.get(part).eval(time, index));
}
}
return Optional.of(interpolated);
}
private static class Polynomials {
private final double yOffset;
private final Polynomial[] polynomials;
private Polynomials(double yOffset, Polynomial[] polynomials) {
this.yOffset = yOffset;
this.polynomials = polynomials;
}
public double eval(double time, int index) {
return polynomials[index].eval(time / 1000) + yOffset;
}
}
public static class Polynomial {
public final double[] coefficients;
public Polynomial(double[] coefficients) {
this.coefficients = coefficients;
}
public double eval(double at) {
double val = 0;
for (double coefficient : coefficients) {
val = val * at + coefficient;
}
return val;
}
public Polynomial derivative() {
if (coefficients.length == 0) {
return this;
}
Polynomial derived = new Polynomial(new double[coefficients.length - 1]);
for (int i = 0; i < coefficients.length - 1; i++) {
derived.coefficients[i] = coefficients[i] * (coefficients.length - 1 - i);
}
return derived;
}
}
}

View File

@@ -1,55 +0,0 @@
package com.replaymod.pathing.path;
import com.google.common.base.Optional;
import com.replaymod.pathing.property.Property;
import lombok.NonNull;
import java.util.Set;
/**
* Represents a key frame in time that is used to compute frames in between multiple key frames.
* Keyframes have different properties depending on the Interpolator tying them together.
* One property tied by a different Interpolator to the previous one than to the next one inherits both properties.
*/
public interface Keyframe {
/**
* Return the time at which this property is set.
*
* @return Time in milliseconds since the start
*/
long getTime();
/**
* Return the value of the property set at this property.
*
* @param property The property
* @param <T> Type of the property
* @return Optional value of the property
*/
@NonNull
<T> Optional<T> getValue(Property<T> property);
/**
* Set the value for the property at this property.
* If the property is not present, adds it.
*
* @param property The property
* @param value Value of the property, may be {@code null}
* @param <T> Type of the property
*/
<T> void setValue(Property<T> property, T value);
/**
* Remove the specified property from this property.
*
* @param property The property to be removed
*/
void removeProperty(Property property);
/**
* Returns all properties of this property
* @return Set of properties
*/
Set<Property> getProperties();
}

View File

@@ -1,116 +0,0 @@
package com.replaymod.pathing.path;
import com.google.common.base.Optional;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.property.Property;
import lombok.NonNull;
import java.util.Collection;
import java.util.Map;
/**
* Represents a path for some object consisting of keyframes, path segments and interpolators.
*/
public interface Path {
/**
* Returns the timeline this path belongs to.
* @return The timeline
*/
Timeline getTimeline();
/**
* Return an immutable collection of all keyframes in this path.
*
* @return Collection of keyframes or empty list if none
*/
@NonNull
Collection<Keyframe> getKeyframes();
/**
* Return an immutable collection of all segments of this path.
*
* @return Collection of segments or empty list if none
*/
@NonNull
Collection<PathSegment> getSegments();
/**
* Update all interpolators that need updating.
* This does <b>not</b> detect changes to keyframes, only interpolators.
* If a property has changed, call {@link Interpolator#bake(Map)} before calling this method
* or call {@link #updateAll()}.
*
* @throws IllegalStateException If any path segments do not have an interpolator set.
*/
void update();
/**
* Update all interpolations.
* This method is significantly slower than {@link #update()}.
*
* @throws IllegalStateException If any path segments do not have an interpolator set.
*/
void updateAll();
/**
* Return the value of the property at the specified point in time.
*
* @param property The property
* @param time Time in milliseconds since the start
* @param <T> Type of the property
* @return Optional value of the property
* @throws IllegalStateException If {@link #update()} has not yet been called
* or interpolators have changed since the last call
*/
<T> Optional<T> getValue(Property<T> property, long time);
/**
* Insert a new property at the specified time.
* The two new path segments inherit the interpolator of the previous one.
* Does <b>not</b> update the interpolators, call {@link #update()} to do so.
*
* @param time Time in milliseconds
* @return The new property
*/
Keyframe insert(long time);
/**
* Returns the property at the specified time.
* @param time Time in milliseconds
* @return The property or {@code null} if none exists at that timestamp
*/
Keyframe getKeyframe(long time);
/**
* Insert the specified property.
* The two new path segments inherit the interpolator of the previous one.
* Does <b>not</b> update the interpolators, call {@link #update()} to do so.
*
* @param keyframe The property
*/
void insert(Keyframe keyframe);
/**
* Removes the specified property.
* Does <b>not</b> update the interpolators, call {@link #update()} to do so.
*
* @param keyframe The property to remove
* @param useFirstInterpolator {@code true} if the new path segment inherits the interpolator of the first segment,
* {@code false} if it inherits the one of the second segment
*/
void remove(Keyframe keyframe, boolean useFirstInterpolator);
/**
* Set whether this path is active.
* If this path is not active, it shouldn't be applied to the game during playback.
* @param active {@code true} if active, {@code false} otherwise
*/
void setActive(boolean active);
/**
* Returns whether this path is active.
* @return {@code true} if active, {@code false} otherwise
* @see #setActive(boolean)
*/
boolean isActive();
}

View File

@@ -1,42 +0,0 @@
package com.replaymod.pathing.path;
import com.replaymod.pathing.interpolation.Interpolator;
import lombok.NonNull;
/**
* Represents a segment of a Path consisting of one start property and one end property.
* Each segment is interpolated by one Interpolator. Multiple segments may have the same Interpolator and can only
* be interpolated together (they may or may not influence each other indirectly).
*/
public interface PathSegment {
/**
* Return the start property of this segment.
*
* @return The first property
*/
@NonNull
Keyframe getStartKeyframe();
/**
* Return the end property of this segment.
*
* @return The second property
*/
@NonNull
Keyframe getEndKeyframe();
/**
* Return the interpolator responsible for this path segment.
*
* @return The interpolator or {@code null} if not yet set
* @throws IllegalStateException If no interpolator has been set yet
*/
@NonNull
Interpolator getInterpolator();
/**
* Set the interpolator responsible for this path segment.
* @param interpolator The interpolator
*/
void setInterpolator(Interpolator interpolator);
}

View File

@@ -1,104 +0,0 @@
package com.replaymod.pathing.path;
import com.google.common.base.Optional;
import com.replaymod.pathing.change.Change;
import com.replaymod.pathing.property.Property;
import com.replaymod.replay.ReplayHandler;
import java.util.List;
/**
* A timeline is a collection of paths that are played together.
*/
public interface Timeline {
/**
* Returns the list of paths that compose this timeline.
* The returned list can be modified and changes are reflected on the timeline.
* The order of the paths is their priority in case of conflicting properties.
* The first path to support a property is the one whose value is used.
* @return List of paths
*/
List<Path> getPaths();
/**
* Creates a new path and adds it to this timeline.
* @return A new Path instance
*/
Path createPath();
/**
* Return the value of the property at the specified point in time.
*
* @param property The property
* @param time Time in milliseconds since the start
* @param <T> Type of the property
* @return Optional value of the property
* @throws IllegalStateException If {@link Path#update()} has not yet been called
* or interpolators have changed since the last call
*/
<T> Optional<T> getValue(Property<T> property, long time);
/**
* Apply the values of all properties at the specified time to the game.
*
* @param time The time on this path
* @param replayHandler The ReplayHandler instance
*/
void applyToGame(long time, ReplayHandler replayHandler);
/**
* Registers the specified property for use in keyframes in this path.
* @param property The property
*/
void registerProperty(Property property);
/**
* Returns the property corresponding to the specified id.
* The id is either "groupId:propertyId" or "propertyId" if the property doesn't belong to any group.
* @param id Id of the property
* @return The property or {@code null} if not existent
*/
Property getProperty(String id);
/**
* Apply the change and push it on the undo stack.
* Clears the redo stack.
* @param change The change
* @throws IllegalStateException if the change has already been applied
*/
void applyChange(Change change);
/**
* Push the change on the undo stack.
* Clears the redo stack.
* @param change The change
* @throws IllegalStateException if the change has not yet been applied
*/
void pushChange(Change change);
/**
* Undo the last change and push it on the redo stack.
* @throws java.util.NoSuchElementException if the stack is empty
*/
void undoLastChange();
/**
* Redo the last undone change and push it back on the undo stack.
* @throws java.util.NoSuchElementException if the stack is empty
*/
void redoLastChange();
/**
* Peek at the top element of the undo stack.
* The returned element must never be undone manually.
* @return Top element on the undo stack, or {@code null} if the stack is empty
*/
Change peekUndoStack();
/**
* Peek at the top element of the redo stack.
* The returned element must never be redone manually.
* @return Top element on the redo stack, or {@code null} if the stack is empty
*/
Change peekRedoStack();
}

View File

@@ -6,10 +6,10 @@ import com.google.common.collect.Ordering;
import com.google.common.primitives.Longs;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;

View File

@@ -1,8 +1,8 @@
package com.replaymod.pathing.player;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraftforge.fml.common.gameevent.TickEvent;
/**

View File

@@ -1,20 +1,20 @@
package com.replaymod.pathing.properties;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.replaymod.pathing.change.Change;
import com.replaymod.pathing.property.AbstractProperty;
import com.replaymod.pathing.property.AbstractPropertyGroup;
import com.replaymod.pathing.property.PropertyPart;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.change.Change;
import com.replaymod.replaystudio.pathing.property.AbstractProperty;
import com.replaymod.replaystudio.pathing.property.AbstractPropertyGroup;
import com.replaymod.replaystudio.pathing.property.PropertyPart;
import com.replaymod.replaystudio.pathing.property.PropertyParts;
import lombok.NonNull;
import org.apache.commons.lang3.tuple.Triple;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.Callable;
/**
@@ -29,8 +29,8 @@ public class CameraProperties extends AbstractPropertyGroup {
}
@Override
public Optional<Callable<ListenableFuture<Change>>> getSetter() {
return Optional.absent();
public Optional<Callable<Change>> getSetter() {
return Optional.empty();
}
public static class Position extends AbstractProperty<Triple<Double, Double, Double>> {
@@ -49,8 +49,8 @@ public class CameraProperties extends AbstractPropertyGroup {
}
@Override
public void applyToGame(Triple<Double, Double, Double> value, @NonNull ReplayHandler replayHandler) {
replayHandler.getCameraEntity().setCameraPosition(value.getLeft(), value.getMiddle(), value.getRight());
public void applyToGame(Triple<Double, Double, Double> value, @NonNull Object replayHandler) {
((ReplayHandler) replayHandler).getCameraEntity().setCameraPosition(value.getLeft(), value.getMiddle(), value.getRight());
}
@Override
@@ -85,8 +85,8 @@ public class CameraProperties extends AbstractPropertyGroup {
}
@Override
public void applyToGame(Triple<Float, Float, Float> value, @NonNull ReplayHandler replayHandler) {
replayHandler.getCameraEntity().setCameraRotation(value.getLeft(), value.getMiddle(), value.getRight());
public void applyToGame(Triple<Float, Float, Float> value, @NonNull Object replayHandler) {
((ReplayHandler) replayHandler).getCameraEntity().setCameraRotation(value.getLeft(), value.getMiddle(), value.getRight());
}
@Override

View File

@@ -1,85 +0,0 @@
package com.replaymod.pathing.properties;
import com.replaymod.pathing.property.AbstractPropertyPart;
import com.replaymod.pathing.property.Property;
import org.apache.commons.lang3.tuple.Triple;
public class PropertyParts {
private PropertyParts(){}
public static class ForInteger extends AbstractPropertyPart<Integer> {
public ForInteger(Property<Integer> property, boolean interpolatable) {
super(property, interpolatable);
}
@Override
public double toDouble(Integer value) {
return value;
}
@Override
public Integer fromDouble(Integer value, double d) {
return (int) Math.round(d);
}
}
public static class ForDoubleTriple extends AbstractPropertyPart<Triple<Double, Double, Double>> {
private final TripleElement element;
public ForDoubleTriple(Property<Triple<Double, Double, Double>> property, boolean interpolatable, TripleElement element) {
super(property, interpolatable);
this.element = element;
}
@Override
public double toDouble(Triple<Double, Double, Double> value) {
switch (element) {
case LEFT: return value.getLeft();
case MIDDLE: return value.getMiddle();
case RIGHT: return value.getRight();
}
throw new AssertionError(element);
}
@Override
public Triple<Double, Double, Double> fromDouble(Triple<Double, Double, Double> value, double d) {
switch (element) {
case LEFT: return Triple.of(d, value.getMiddle(), value.getRight());
case MIDDLE: return Triple.of(value.getLeft(), d, value.getRight());
case RIGHT: return Triple.of(value.getLeft(), value.getMiddle(), d);
}
throw new AssertionError(element);
}
}
public static class ForFloatTriple extends AbstractPropertyPart<Triple<Float, Float, Float>> {
private final TripleElement element;
public ForFloatTriple(Property<Triple<Float, Float, Float>> property, boolean interpolatable, TripleElement element) {
super(property, interpolatable);
this.element = element;
}
@Override
public double toDouble(Triple<Float, Float, Float> value) {
switch (element) {
case LEFT: return value.getLeft();
case MIDDLE: return value.getMiddle();
case RIGHT: return value.getRight();
}
throw new AssertionError(element);
}
@Override
public Triple<Float, Float, Float> fromDouble(Triple<Float, Float, Float> value, double d) {
switch (element) {
case LEFT: return Triple.of((float) d, value.getMiddle(), value.getRight());
case MIDDLE: return Triple.of(value.getLeft(), (float) d, value.getRight());
case RIGHT: return Triple.of(value.getLeft(), value.getMiddle(), (float) d);
}
throw new AssertionError(element);
}
}
public enum TripleElement {
LEFT, MIDDLE, RIGHT;
}
}

View File

@@ -2,10 +2,11 @@ package com.replaymod.pathing.properties;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.replaymod.pathing.property.AbstractProperty;
import com.replaymod.pathing.property.PropertyPart;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.ReplaySender;
import com.replaymod.replaystudio.pathing.property.AbstractProperty;
import com.replaymod.replaystudio.pathing.property.PropertyPart;
import com.replaymod.replaystudio.pathing.property.PropertyParts;
import lombok.NonNull;
import java.io.IOException;
@@ -28,8 +29,8 @@ public class TimestampProperty extends AbstractProperty<Integer> {
}
@Override
public void applyToGame(Integer value, @NonNull ReplayHandler replayHandler) {
ReplaySender replaySender = replayHandler.getReplaySender();
public void applyToGame(Integer value, @NonNull Object replayHandler) {
ReplaySender replaySender = ((ReplayHandler) replayHandler).getReplaySender();
if (replaySender.isAsyncMode()) {
replaySender.jumpToTime(value);
} else {

View File

@@ -1,43 +0,0 @@
package com.replaymod.pathing.property;
import net.minecraft.client.resources.I18n;
/**
* Abstract base class for most properties.
*/
public abstract class AbstractProperty<T> implements Property<T> {
private final String id, localizationKey;
private final PropertyGroup propertyGroup;
private final T initialValue;
public AbstractProperty(String id, String localizationKey, PropertyGroup propertyGroup, T initialValue) {
this.id = id;
this.localizationKey = localizationKey;
this.propertyGroup = propertyGroup;
this.initialValue = initialValue;
if (propertyGroup != null) {
propertyGroup.getProperties().add(this);
}
}
@Override
public String getLocalizedName() {
return I18n.format(localizationKey);
}
@Override
public PropertyGroup getGroup() {
return propertyGroup;
}
@Override
public String getId() {
return id;
}
@Override
public T getNewValue() {
return initialValue;
}
}

View File

@@ -1,31 +0,0 @@
package com.replaymod.pathing.property;
import lombok.RequiredArgsConstructor;
import net.minecraft.client.resources.I18n;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract base class for most property groups.
*/
@RequiredArgsConstructor
public abstract class AbstractPropertyGroup implements PropertyGroup {
private final String id, localizationKey;
private final List<Property> properties = new ArrayList<>();
@Override
public String getLocalizedName() {
return I18n.format(localizationKey);
}
@Override
public String getId() {
return id;
}
@Override
public List<Property> getProperties() {
return properties;
}
}

View File

@@ -1,21 +0,0 @@
package com.replaymod.pathing.property;
public abstract class AbstractPropertyPart<T> implements PropertyPart<T> {
private final Property<T> property;
private final boolean interpolatable;
public AbstractPropertyPart(Property<T> property, boolean interpolatable) {
this.property = property;
this.interpolatable = interpolatable;
}
@Override
public Property<T> getProperty() {
return property;
}
@Override
public boolean isInterpolatable() {
return interpolatable;
}
}

View File

@@ -1,78 +0,0 @@
package com.replaymod.pathing.property;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.replaymod.replay.ReplayHandler;
import lombok.NonNull;
import java.io.IOException;
import java.util.Collection;
/**
* Represents a property of a property.
* Such properties may or may not be interpolated between keyframes.
* <br>
* If a property cannot be interpolated between keyframes, it is only active between two keyframes having the same
* value for that property.
*
* @param <T> The type of the property, must be immutable
*/
public interface Property<T> {
/**
* Returns the localized name of this property.
*
* @return Localized name.
*/
@NonNull
String getLocalizedName();
/**
* Returns the group this property belongs to.
*
* @return The group
*/
PropertyGroup getGroup();
/**
* Returns an ID unique for this property.
* There may be multiple instances for the same property ID
* if they all represent the same concept (all x-Coordinate).
*
* @return Unique ID
*/
@NonNull
String getId();
/**
* Returns a new value for this property.
* @return New value, may be {@code null}
*/
T getNewValue();
/**
* Returns (optionally) interpolatable parts of this property.
* @return Collection of parts
*/
Collection<PropertyPart<T>> getParts();
/**
* Appy the specified value of this property to the game.
* @param value The value of this property
* @param replayHandler The ReplayHandler instance
*/
void applyToGame(T value, ReplayHandler replayHandler);
/**
* Writes the specified value of this property to JSON.
* @param writer The json writer
* @param value The value
*/
void toJson(JsonWriter writer, T value) throws IOException;
/**
* Reads the value of this property from JSON.
* @param reader The json reader
* @return The value
*/
T fromJson(JsonReader reader) throws IOException;
}

View File

@@ -1,52 +0,0 @@
package com.replaymod.pathing.property;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.pathing.change.Change;
import lombok.NonNull;
import java.util.List;
import java.util.concurrent.Callable;
/**
* Represents a group of property properties.
* These groups are displayed and stored together.
* The same property id may exist in multiple groups.<br>
* Groups may also define a way of setting their properties in one simple way (e.g. "set to current position").
*/
public interface PropertyGroup {
/**
* Returns the localized name of this group.
*
* @return Localized name.
*/
@NonNull
String getLocalizedName();
/**
* Returns an ID unique for this group.
*
* @return Unique ID
*/
@NonNull
String getId();
/**
* Return a list of all properties in this group.
*
* @return List of properties or empty list if none.
*/
@NonNull
List<Property> getProperties();
/**
* Return a Callable which can be used to set all properties in this group at once.
* The Callable returns a future which is completed once all properties have been updated.
* It is up to the caller to apply those changes to the path. If those changes are not applied
* immediately, their content may become invalid and applying them results in undefined behavior.
*
* @return Optional callable
*/
@NonNull
Optional<Callable<ListenableFuture<Change>>> getSetter();
}

View File

@@ -1,34 +0,0 @@
package com.replaymod.pathing.property;
/**
* Represents an (optionally) interpolatable part of a property (such as the X coordinate of the position property).
* @param <T> Type of the property
*/
public interface PropertyPart<T> {
Property<T> getProperty();
/**
* Returns whether this part should be interpolated between keyframes.
* Examples would be x/y/z coordinates or sun location.
* Counterexamples are spectated entity id or
* @return {@code true} if this part should be interpolated, {@code false} otherwise
*/
boolean isInterpolatable();
/**
* Convert this part of the value to a double for interpolation.
* @param value The value, may be {@code null}
* @return A double representing this value
* @throws UnsupportedOperationException if this part is not interpolatable
*/
double toDouble(T value);
/**
* Convert the specified double to this part of the value and return it combined with the value for other parts.
* @param value Value of other parts
* @param d Value for this part
* @return Combined value
* @throws UnsupportedOperationException if this part is not interpolatable
*/
T fromDouble(T value, double d);
}

View File

@@ -1,128 +0,0 @@
package com.replaymod.pathing.serialize;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.replaymod.pathing.serialize.LegacyTimelineConverter.*;
public class LegacyKeyframeSetAdapter extends TypeAdapter<KeyframeSet[]> {
public LegacyKeyframeSetAdapter() {
super();
}
@SuppressWarnings("unchecked")
@Override
public KeyframeSet[] read(JsonReader in) throws IOException {
List<KeyframeSet> sets = new ArrayList<>();
in.beginArray();
while(in.hasNext()) { //iterate over all array entries
KeyframeSet set = new KeyframeSet();
List<Keyframe> positionKeyframes = new ArrayList<>();
List<Keyframe> timeKeyframes = new ArrayList<>();
in.beginObject();
while(in.hasNext()) { //iterate over all object entries
String jsonTag = in.nextName();
if("name".equals(jsonTag)) {
set.name = in.nextString();
//TODO: Adapt to new Spectator Keyframe system
} else if("positionKeyframes".equals(jsonTag)) {
in.beginArray();
while(in.hasNext()) {
Keyframe<AdvancedPosition> newKeyframe = new Keyframe<>();
Integer spectatedEntityID = null;
in.beginObject();
while(in.hasNext()) {
String jsonKeyframeTag = in.nextName();
if("value".equals(jsonKeyframeTag) || "position".equals(jsonKeyframeTag)) {
SpectatorData spectatorData = new Gson().fromJson(in, SpectatorData.class);
if (spectatorData.spectatedEntityID != null) {
newKeyframe.value = spectatorData;
} else {
newKeyframe.value = new AdvancedPosition();
newKeyframe.value.x = spectatorData.x;
newKeyframe.value.y = spectatorData.y;
newKeyframe.value.z = spectatorData.z;
newKeyframe.value.yaw = spectatorData.yaw;
newKeyframe.value.pitch = spectatorData.pitch;
newKeyframe.value.roll = spectatorData.roll;
}
} else if("realTimestamp".equals(jsonKeyframeTag)) {
newKeyframe.realTimestamp = in.nextInt();
} else if("spectatedEntityID".equals(jsonKeyframeTag)) {
spectatedEntityID = in.nextInt();
}
}
if(spectatedEntityID != null) {
AdvancedPosition pos = newKeyframe.value;
SpectatorData spectatorData = new SpectatorData();
spectatorData.spectatedEntityID = spectatedEntityID;
newKeyframe.value = spectatorData;
newKeyframe.value.x = pos.x;
newKeyframe.value.y = pos.y;
newKeyframe.value.z = pos.z;
newKeyframe.value.yaw = pos.yaw;
newKeyframe.value.pitch = pos.pitch;
newKeyframe.value.roll = pos.roll;
}
in.endObject();
positionKeyframes.add(newKeyframe);
}
in.endArray();
} else if("timeKeyframes".equals(jsonTag)) {
in.beginArray();
while(in.hasNext()) {
Keyframe<TimestampValue> newKeyframe = new Keyframe<>();
in.beginObject();
while(in.hasNext()) {
String jsonKeyframeTag = in.nextName();
if("timestamp".equals(jsonKeyframeTag)) {
TimestampValue timestampValue = new TimestampValue();
timestampValue.value = in.nextInt();
newKeyframe.value = timestampValue;
} else if("value".equals(jsonKeyframeTag)) {
newKeyframe.value = new Gson().fromJson(in, TimestampValue.class);
} else if("realTimestamp".equals(jsonKeyframeTag)) {
newKeyframe.realTimestamp = in.nextInt();
}
}
in.endObject();
timeKeyframes.add(newKeyframe);
}
in.endArray();
} else if("customObjects".equals(jsonTag)) {
set.customObjects = new Gson().fromJson(in, CustomImageObject[].class);
}
}
in.endObject();
set.positionKeyframes = positionKeyframes.toArray(new Keyframe[positionKeyframes.size()]);
set.timeKeyframes = timeKeyframes.toArray(new Keyframe[timeKeyframes.size()]);
sets.add(set);
}
in.endArray();
return sets.toArray(new KeyframeSet[sets.size()]);
}
@Override
public void write(JsonWriter out, KeyframeSet[] value) throws IOException {}
}

View File

@@ -1,140 +0,0 @@
package com.replaymod.pathing.serialize;
import com.google.common.base.Optional;
import com.google.gson.GsonBuilder;
import com.replaymod.pathing.PathingRegistry;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.properties.CameraProperties;
import com.replaymod.pathing.properties.TimestampProperty;
import de.johni0702.replaystudio.replay.ReplayFile;
import org.apache.commons.lang3.tuple.Triple;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
public class LegacyTimelineConverter {
public static Map<String, Timeline> convert(PathingRegistry registry, ReplayFile replayFile) throws IOException {
KeyframeSet[] keyframeSets = readAndParse(replayFile);
if (keyframeSets == null) {
return Collections.emptyMap();
}
Map<String, Timeline> timelines = new LinkedHashMap<>();
for (KeyframeSet keyframeSet : keyframeSets) {
timelines.put(keyframeSet.name, convert(registry, keyframeSet));
}
return timelines;
}
private static Optional<InputStream> read(ReplayFile replayFile) throws IOException {
Optional<InputStream> in = replayFile.get("paths.json");
if (!in.isPresent()) {
in = replayFile.get("paths");
}
return in;
}
private static KeyframeSet[] parse(InputStream in) {
return new GsonBuilder()
.registerTypeAdapter(KeyframeSet[].class, new LegacyKeyframeSetAdapter())
.create().fromJson(new InputStreamReader(in), KeyframeSet[].class);
}
private static KeyframeSet[] readAndParse(ReplayFile replayFile) throws IOException {
Optional<InputStream> optIn = read(replayFile);
if (!optIn.isPresent()) {
return null;
}
KeyframeSet[] keyframeSets;
try (InputStream in = optIn.get()) {
keyframeSets = parse(in);
}
return keyframeSets;
}
private static Timeline convert(PathingRegistry registry, KeyframeSet keyframeSet) {
Timeline timeline = registry.createTimeline();
Path timePath = timeline.createPath();
Path positionPath = timeline.createPath();
for (Keyframe<AdvancedPosition> positionKeyframe : keyframeSet.positionKeyframes) {
AdvancedPosition value = positionKeyframe.value;
com.replaymod.pathing.path.Keyframe keyframe = getKeyframe(positionPath, positionKeyframe.realTimestamp);
keyframe.setValue(CameraProperties.POSITION, Triple.of(value.x, value.y, value.z));
keyframe.setValue(CameraProperties.ROTATION, Triple.of(value.yaw, value.pitch, value.roll));
if (value instanceof SpectatorData) {
// TODO Spectator keyframes
}
}
for (Keyframe<TimestampValue> timeKeyframe : keyframeSet.timeKeyframes) {
TimestampValue value = timeKeyframe.value;
com.replaymod.pathing.path.Keyframe keyframe = getKeyframe(timePath, timeKeyframe.realTimestamp);
keyframe.setValue(TimestampProperty.PROPERTY, (int) value.value);
}
return timeline;
}
private static com.replaymod.pathing.path.Keyframe getKeyframe(Path path, long time) {
com.replaymod.pathing.path.Keyframe keyframe = path.getKeyframe(time);
if (keyframe == null) {
keyframe = path.insert(time);
}
return keyframe;
}
static class KeyframeSet {
String name;
Keyframe<AdvancedPosition>[] positionKeyframes;
Keyframe<TimestampValue>[] timeKeyframes;
CustomImageObject[] customObjects;
}
static class Keyframe<T> {
int realTimestamp;
T value;
}
static class Position {
double x, y, z;
}
static class AdvancedPosition extends Position {
float pitch, yaw, roll;
}
static class SpectatorData extends AdvancedPosition {
Integer spectatedEntityID;
SpectatingMethod spectatingMethod;
SpectatorDataThirdPersonInfo thirdPersonInfo;
enum SpectatingMethod {
FIRST_PERSON, SHOULDER_CAM;
}
}
static class SpectatorDataThirdPersonInfo {
double shoulderCamDistance;
double shoulderCamPitchOffset;
double shoulderCamYawOffset;
double shoulderCamSmoothness;
}
static class TimestampValue {
double value;
}
static class CustomImageObject {
String name;
UUID linkedAsset;
float width, height;
float textureWidth, textureHeight;
Transformations transformations = new Transformations();
}
static class Transformations {
Position defaultAnchor, defaultPosition, defaultOrientation, defaultScale;
NumberValue defaultOpacity;
List<Position> anchorKeyframes;
List<Position> positionKeyframes;
List<Position> orientationKeyframes;
List<Position> scaleKeyframes;
List<NumberValue> opacityKeyframes;
}
static class NumberValue {
double value;
}
}

View File

@@ -1,193 +0,0 @@
package com.replaymod.pathing.serialize;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.replaymod.pathing.PathingRegistry;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.property.Property;
import de.johni0702.replaystudio.replay.ReplayFile;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.util.*;
public class TimelineSerialization {
private static final String FILE_ENTRY = "timelines.json";
private final PathingRegistry registry;
private final ReplayFile replayFile;
public TimelineSerialization(PathingRegistry registry, ReplayFile replayFile) {
this.registry = registry;
this.replayFile = replayFile;
}
public void save(Map<String, Timeline> timelines) throws IOException {
String serialized = serialize(timelines);
try (OutputStream out = replayFile.write(FILE_ENTRY)) {
out.write(serialized.getBytes(Charsets.UTF_8));
}
}
public Map<String, Timeline> load() throws IOException {
Map<String, Timeline> timelines = new LinkedHashMap<>(LegacyTimelineConverter.convert(registry, replayFile));
Optional<InputStream> optionalIn = replayFile.get(FILE_ENTRY);
if (optionalIn.isPresent()) {
String serialized;
try (InputStream in = optionalIn.get()) {
serialized = IOUtils.toString(in, Charsets.UTF_8);
}
Map<String, Timeline> deserialized = deserialize(serialized);
timelines.putAll(deserialized);
}
return timelines;
}
public String serialize(Map<String, Timeline> timelines) throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new JsonWriter(stringWriter);
writer.beginObject();
for (Map.Entry<String, Timeline> entry : timelines.entrySet()) {
Timeline timeline = entry.getValue();
writer.name(entry.getKey()).beginArray();
for (Path path : timeline.getPaths()) {
writer.beginObject();
writer.name("keyframes").beginArray();
for (Keyframe keyframe : path.getKeyframes()) {
writer.beginObject();
writer.name("time").value(keyframe.getTime());
writer.name("properties").beginObject();
for (Property<?> property : keyframe.getProperties()) {
writer.name((property.getGroup() == null ? "" : property.getGroup().getId() + ":") + property.getId());
writeProperty(writer, keyframe, property);
}
writer.endObject();
writer.endObject();
}
writer.endArray();
Map<Interpolator, Integer> interpolators = new LinkedHashMap<>();
writer.name("segments").beginArray();
for (PathSegment segment : path.getSegments()) {
Interpolator interpolator = segment.getInterpolator();
if (interpolator == null) {
writer.nullValue();
} else {
Integer index = interpolators.get(interpolator);
if (index == null) {
interpolators.put(interpolator, index = interpolators.size());
}
writer.value(index);
}
}
writer.endArray();
writer.name("interpolators").beginArray();
for (Interpolator interpolator : interpolators.keySet()) {
registry.serializeInterpolator(writer, interpolator);
}
writer.endArray();
writer.endObject();
}
writer.endArray();
}
writer.endObject();
writer.flush();
return stringWriter.toString();
}
private static <T> void writeProperty(JsonWriter writer, Keyframe keyframe, Property<T> property) throws IOException {
property.toJson(writer, keyframe.getValue(property).get());
}
public Map<String, Timeline> deserialize(String serialized) throws IOException {
JsonReader reader = new JsonReader(new StringReader(serialized));
Map<String, Timeline> timelines = new LinkedHashMap<>();
reader.beginObject();
while (reader.hasNext()) {
Timeline timeline = registry.createTimeline();
timelines.put(reader.nextName(), timeline);
reader.beginArray();
while (reader.hasNext()) {
Path path = timeline.createPath();
reader.beginObject();
List<Integer> segments = new ArrayList<>();
List<Interpolator> interpolators = new ArrayList<>();
while (reader.hasNext()) {
switch (reader.nextName()) {
case "keyframes":
reader.beginArray();
while (reader.hasNext()) {
long time = 0;
Map<Property, Object> properties = new HashMap<>();
reader.beginObject();
while (reader.hasNext()) {
switch (reader.nextName()) {
case "time":
time = reader.nextLong();
break;
case "properties":
reader.beginObject();
while (reader.hasNext()) {
String id = reader.nextName();
Property property = timeline.getProperty(id);
if (property == null) {
throw new IOException("Unknown property: " + id);
}
Object value = property.fromJson(reader);
properties.put(property, value);
}
reader.endObject();
break;
}
}
reader.endObject();
Keyframe keyframe = path.insert(time);
for (Map.Entry<Property, Object> entry : properties.entrySet()) {
keyframe.setValue(entry.getKey(), entry.getValue());
}
}
reader.endArray();
break;
case "segments":
reader.beginArray();
while (reader.hasNext()) {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
segments.add(null);
} else {
segments.add(reader.nextInt());
}
}
reader.endArray();
break;
case "interpolators":
reader.beginArray();
while (reader.hasNext()) {
interpolators.add(registry.deserializeInterpolator(reader));
}
reader.endArray();
break;
}
}
Iterator<Integer> iter = segments.iterator();
for (PathSegment segment : path.getSegments()) {
Integer next = iter.next();
if (next != null) {
segment.setInterpolator(interpolators.get(next));
}
}
reader.endObject();
}
reader.endArray();
}
reader.endObject();
return timelines;
}
}

View File

@@ -4,9 +4,9 @@ import com.replaymod.core.ReplayMod;
import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import net.minecraft.client.Minecraft;

View File

@@ -3,9 +3,9 @@ package com.replaymod.recording.packet;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.replaymod.core.ReplayMod;
import de.johni0702.replaystudio.data.Marker;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

View File

@@ -5,7 +5,7 @@ import com.google.common.io.Files;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import de.johni0702.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.core.utils.Restrictions;
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
import io.netty.channel.ChannelHandlerContext;

View File

@@ -5,11 +5,11 @@ import com.google.common.util.concurrent.Futures;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.ReplayModRender;
import com.replaymod.render.rendering.VideoRenderer;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Timeline;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;

View File

@@ -1,10 +1,6 @@
package com.replaymod.render.rendering;
import com.google.common.base.Optional;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.properties.TimestampProperty;
import com.replaymod.render.RenderSettings;
import com.replaymod.render.VideoWriter;
@@ -15,6 +11,9 @@ import com.replaymod.render.hooks.ChunkLoadingRenderGlobal;
import com.replaymod.render.hooks.RenderReplayTimer;
import com.replaymod.render.metadata.MetadataInjector;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import net.minecraft.client.gui.ScaledResolution;
@@ -29,6 +28,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.FutureTask;
import static com.google.common.collect.Iterables.getLast;

View File

@@ -7,8 +7,8 @@ import com.replaymod.replay.camera.SpectatorCameraController;
import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import de.johni0702.replaystudio.data.Marker;
import de.johni0702.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import eu.crushedpixel.replaymod.registry.ReplayGuiRegistry;
import com.replaymod.core.utils.Restrictions;

View File

@@ -7,10 +7,10 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.replaymod.core.ReplayMod;
import com.replaymod.replay.camera.*;
import com.replaymod.replay.handler.GuiHandler;
import de.johni0702.replaystudio.data.Marker;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;

View File

@@ -5,7 +5,7 @@ import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.utils.Restrictions;
import com.replaymod.replay.camera.CameraEntity;
import de.johni0702.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayFile;
import eu.crushedpixel.replaymod.holders.PacketData;
import eu.crushedpixel.replaymod.settings.ReplaySettings;
import eu.crushedpixel.replaymod.utils.ReplayFileIO;

View File

@@ -10,7 +10,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.replaystudio.data.Marker;
import com.replaymod.replaystudio.data.Marker;
public class GuiEditMarkerPopup extends AbstractGuiPopup<GuiEditMarkerPopup> {
private final ReplayHandler replayHandler;

View File

@@ -6,7 +6,7 @@ import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;
import de.johni0702.minecraft.gui.function.Draggable;
import de.johni0702.replaystudio.data.Marker;
import com.replaymod.replaystudio.data.Marker;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.MathHelper;

View File

@@ -20,10 +20,10 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import eu.crushedpixel.replaymod.registry.ResourceHelper;
import eu.crushedpixel.replaymod.utils.DurationUtils;
import eu.crushedpixel.replaymod.utils.ReplayFileIO;

View File

@@ -3,17 +3,17 @@ package com.replaymod.simplepathing;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.PathingRegistry;
import com.replaymod.pathing.impl.TimelineImpl;
import com.replaymod.pathing.interpolation.AbstractInterpolator;
import com.replaymod.pathing.interpolation.CubicSplineInterpolator;
import com.replaymod.pathing.interpolation.Interpolator;
import com.replaymod.pathing.interpolation.LinearInterpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.properties.CameraProperties;
import com.replaymod.pathing.properties.TimestampProperty;
import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replaystudio.pathing.PathingRegistry;
import com.replaymod.replaystudio.pathing.impl.TimelineImpl;
import com.replaymod.replaystudio.pathing.interpolation.AbstractInterpolator;
import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator;
import com.replaymod.replaystudio.pathing.interpolation.Interpolator;
import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.simplepathing.gui.GuiPathing;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;

View File

@@ -3,10 +3,10 @@ package com.replaymod.simplepathing.gui;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.properties.CameraProperties;
import com.replaymod.pathing.properties.TimestampProperty;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline;

View File

@@ -3,15 +3,7 @@ package com.replaymod.simplepathing.gui;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.replaymod.core.ReplayMod;
import com.replaymod.pathing.change.*;
import com.replaymod.pathing.gui.GuiKeyframeRepository;
import com.replaymod.pathing.interpolation.AbstractInterpolator;
import com.replaymod.pathing.interpolation.CubicSplineInterpolator;
import com.replaymod.pathing.interpolation.LinearInterpolator;
import com.replaymod.pathing.path.Keyframe;
import com.replaymod.pathing.path.Path;
import com.replaymod.pathing.path.PathSegment;
import com.replaymod.pathing.path.Timeline;
import com.replaymod.pathing.player.RealtimeTimelinePlayer;
import com.replaymod.pathing.properties.CameraProperties;
import com.replaymod.pathing.properties.TimestampProperty;
@@ -19,6 +11,14 @@ import com.replaymod.render.gui.GuiRenderSettings;
import com.replaymod.replay.ReplayHandler;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
import com.replaymod.replaystudio.pathing.change.*;
import com.replaymod.replaystudio.pathing.interpolation.AbstractInterpolator;
import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator;
import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator;
import com.replaymod.replaystudio.pathing.path.Keyframe;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.PathSegment;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.simplepathing.ReplayModSimplePathing;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;

View File

@@ -1,249 +0,0 @@
package eu.crushedpixel.replaymod.gui;
import com.replaymod.core.ReplayMod;
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
import eu.crushedpixel.replaymod.holders.KeyframeSet;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.resources.I18n;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.lwjgl.input.Keyboard;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class GuiKeyframeRepository extends GuiScreen {
private boolean initialized = false;
private String screenTitle;
private final Minecraft mc = Minecraft.getMinecraft();
private GuiEntryList<KeyframeSet> keyframeSetList;
private KeyframeSet[] keyframeRepository;
private KeyframeSet currentKeyframeSet = null;
private GuiTextField nameInput;
private GuiButton removeButton, loadButton, saveButton;
private String message = null;
private int currentSetTimeKeyframeCount, currentSetPositionKeyframeCount, currentSetDuration;
private final List<KeyframeSet> initialKeyframeSets;
public GuiKeyframeRepository(KeyframeSet[] keyframeRepository) {
this.keyframeRepository = keyframeRepository;
this.initialKeyframeSets = new ArrayList<KeyframeSet>(Arrays.asList(keyframeRepository));
ReplayMod.replaySender.setReplaySpeed(0);
}
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
int h = (int)Math.floor(((double)this.height-(45+20+15))/14);
if(!initialized) {
screenTitle = I18n.format("replaymod.gui.keyframerepository.title");
keyframeSetList = new GuiEntryList<KeyframeSet>(mc.fontRendererObj,
0, 45, 0, h);
for(KeyframeSet set : keyframeRepository)
keyframeSetList.addElement(set);
keyframeSetList.setSelectionIndex(-1);
keyframeSetList.setEmptyMessage(I18n.format("replaymod.gui.keyframerepository.noentries"));
keyframeSetList.addSelectionListener(new SelectionListener() {
@Override
public void onSelectionChanged(int selectionIndex) {
removeButton.enabled = selectionIndex >= 0;
loadButton.enabled = selectionIndex >= 0;
nameInput.setEnabled(selectionIndex >= 0);
if(selectionIndex >= 0) {
currentKeyframeSet = keyframeSetList.getElement(selectionIndex);
nameInput.setText(currentKeyframeSet.getName());
currentSetPositionKeyframeCount = currentKeyframeSet.getPositionKeyframeCount();
currentSetTimeKeyframeCount = currentKeyframeSet.getTimeKeyframeCount();
currentSetDuration = currentKeyframeSet.getPathDuration();
} else {
nameInput.setText("");
currentKeyframeSet = null;
}
message = null;
}
});
nameInput = new GuiTextField(GuiConstants.KEYFRAME_REPOSTORY_NAME_INPUT, mc.fontRendererObj,
0, 45, 0, 20);
nameInput.setEnabled(false);
removeButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_REMOVE_BUTTON, 0, 75,
I18n.format("replaymod.gui.remove"));
loadButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_LOAD_BUTTON, 0, 75,
I18n.format("replaymod.gui.load"));
saveButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_ADD_BUTTON, 30 + (this.width / 2),
keyframeSetList.yPosition+keyframeSetList.height-20,
I18n.format("replaymod.gui.keyframerepository.savecurrent"));
removeButton.enabled = loadButton.enabled = false;
}
keyframeSetList.width = nameInput.width = saveButton.width = 150;
keyframeSetList.xPosition = width / 2 - keyframeSetList.width - 5;
keyframeSetList.setVisibleElements(h);
removeButton.width = loadButton.width = 73;
nameInput.xPosition = saveButton.xPosition = width / 2 + 5;
loadButton.xPosition = width / 2 + 5;
removeButton.xPosition = width / 2 + 5 + loadButton.width + 3 + 4;
saveButton.yPosition = keyframeSetList.yPosition+keyframeSetList.height-20;
@SuppressWarnings("unchecked")
List<GuiButton> buttonList = this.buttonList;
buttonList.add(removeButton);
buttonList.add(loadButton);
buttonList.add(saveButton);
initialized = true;
}
@Override
public void actionPerformed(GuiButton button) {
// TODO
// if(!button.enabled) return;
// switch(button.id) {
// case GuiConstants.KEYFRAME_REPOSITORY_ADD_BUTTON:
// List<Keyframe> kfs = new ArrayList<Keyframe>(ReplayHandler.getAllKeyframes());
//
// Keyframe[] keyframes = kfs.toArray(new Keyframe[ReplayHandler.getAllKeyframes().size()]);
// KeyframeSet newSet = new KeyframeSet(I18n.format("replaymod.gui.keyframerepository.preset.defaultname"), keyframes, ReplayHandler.getCustomImageObjects());
//
// try {
// CameraPathValidator.validateCameraPath(ReplayHandler.getPositionKeyframes(), ReplayHandler.getTimeKeyframes());
// } catch(CameraPathValidator.InvalidCameraPathException e) {
// message = e.getLocalizedMessage();
// break;
// }
//
// if(keyframeSetList.getCopyOfElements().contains(newSet)) {
// message = I18n.format("replaymod.gui.keyframerepository.duplicate");
// break;
// }
// message = null;
//
// keyframeSetList.addElement(newSet);
// keyframeSetList.setSelectionIndex(keyframeSetList.getEntryCount()-1);
// break;
// case GuiConstants.KEYFRAME_REPOSITORY_REMOVE_BUTTON:
// keyframeSetList.removeElement(keyframeSetList.getSelectionIndex());
// break;
// case GuiConstants.KEYFRAME_REPOSITORY_LOAD_BUTTON:
// ReplayHandler.useKeyframePreset(keyframeSetList.getElement(keyframeSetList.getSelectionIndex()));
// saveOnQuit();
// mc.displayGuiScreen(null);
// break;
// }
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
this.drawCenteredString(fontRendererObj, screenTitle, this.width / 2, 5, Color.WHITE.getRGB());
int leftBorder = 10;
int topBorder = 20;
drawGradientRect(leftBorder, topBorder, width - leftBorder, this.height - 10, -1072689136, -804253680);
this.drawCenteredString(fontRendererObj, I18n.format("replaymod.gui.keyframerepository.presets"), keyframeSetList.xPosition + (keyframeSetList.width/2), 30, Color.WHITE.getRGB());
keyframeSetList.drawTextBox();
nameInput.drawTextBox();
KeyframeSet currentSet = null;
if(keyframeSetList.getSelectionIndex() >= 0) {
currentSet = keyframeSetList.getElement(keyframeSetList.getSelectionIndex());
}
if(currentSet != null) {
this.drawString(fontRendererObj, I18n.format("replaymod.gui.keyframerepository.positionkeyframes") + ": " +currentSetPositionKeyframeCount,
loadButton.xPosition+2, removeButton.yPosition + 30, Color.WHITE.getRGB());
this.drawString(fontRendererObj, I18n.format("replaymod.gui.keyframerepository.timekeyframes") + ": " +currentSetTimeKeyframeCount,
loadButton.xPosition+2, removeButton.yPosition + 50, Color.WHITE.getRGB());
this.drawString(fontRendererObj, I18n.format("replaymod.gui.duration")+": "+ DurationFormatUtils.formatDurationHMS(currentSetDuration),
loadButton.xPosition+2, removeButton.yPosition + 70, Color.WHITE.getRGB());
this.drawString(fontRendererObj, I18n.format("replaymod.gui.objects")+": "+currentSet.getCustomObjectCount(),
loadButton.xPosition+2, removeButton.yPosition + 90, Color.WHITE.getRGB());
}
if(message != null) {
this.drawCenteredString(fontRendererObj, message, this.width/2, this.height-25, Color.RED.getRGB());
}
super.drawScreen(mouseX, mouseY, partialTicks);
}
@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
super.mouseClicked(mouseX, mouseY, mouseButton);
keyframeSetList.mouseClicked(mouseX, mouseY, mouseButton);
nameInput.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
public void updateScreen() {
nameInput.updateCursorCounter();
}
@Override
public void keyTyped(char typedChar, int keyCode) throws IOException {
nameInput.textboxKeyTyped(typedChar, keyCode);
if(keyCode == Keyboard.KEY_ESCAPE) {
saveOnQuit();
super.keyTyped(typedChar, keyCode);
}
if(currentKeyframeSet != null && !currentKeyframeSet.getName().equals(nameInput.getText().trim())) {
currentKeyframeSet.setName(nameInput.getText().trim());
}
super.keyTyped(typedChar, keyCode);
}
private void saveOnQuit() {
ArrayList<KeyframeSet> copy = new ArrayList<KeyframeSet>(keyframeSetList.getCopyOfElements());
if(initialKeyframeSets.equals(copy)) return;
this.keyframeRepository = copy.toArray(new KeyframeSet[copy.size()]);
// TODO
// ReplayHandler.setKeyframeRepository(keyframeRepository, true);
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
}

View File

@@ -1,99 +0,0 @@
package eu.crushedpixel.replaymod.holders;
import eu.crushedpixel.replaymod.assets.CustomImageObject;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class KeyframeSet implements GuiEntryListEntry {
private String name;
private Keyframe<AdvancedPosition>[] positionKeyframes;
private Keyframe<TimestampValue>[] timeKeyframes;
private CustomImageObject[] customObjects = new CustomImageObject[0];
public KeyframeSet(String name, Keyframe[] keyframes, List<CustomImageObject> customObjectRepository) {
this.name = name;
setCustomObjects(customObjectRepository.toArray(new CustomImageObject[customObjectRepository.size()]));
setKeyframes(keyframes);
}
public KeyframeSet(String name, Keyframe[] keyframes, CustomImageObject[] customObjects) {
this.name = name;
setCustomObjects(customObjects);
setKeyframes(keyframes);
}
public void setCustomObjects(CustomImageObject[] customObjects) {
this.customObjects = new CustomImageObject[customObjects.length];
int i = 0;
for(CustomImageObject object : customObjects) {
try {
this.customObjects[i] = object.copy();
} catch(IOException ioe) {
ioe.printStackTrace();
}
i++;
}
}
@SuppressWarnings("unchecked")
public void setKeyframes(Keyframe[] keyframes) {
List<Keyframe<AdvancedPosition>> posKFList = new ArrayList<Keyframe<AdvancedPosition>>();
List<Keyframe<TimestampValue>> timeKFList = new ArrayList<Keyframe<TimestampValue>>();
for(Keyframe kf : keyframes) {
if(kf.getValue() instanceof AdvancedPosition)
posKFList.add((Keyframe<AdvancedPosition>)kf);
else if(kf.getValue() instanceof TimestampValue)
timeKFList.add((Keyframe<TimestampValue>) kf);
}
positionKeyframes = posKFList.toArray(new Keyframe[posKFList.size()]);
timeKeyframes = timeKFList.toArray(new Keyframe[timeKFList.size()]);
}
public Keyframe[] getKeyframes() {
Keyframe[] keyframes = new Keyframe[positionKeyframes.length + timeKeyframes.length];
System.arraycopy(positionKeyframes, 0, keyframes, 0, positionKeyframes.length);
System.arraycopy(timeKeyframes, 0, keyframes, positionKeyframes.length, timeKeyframes.length);
return keyframes;
}
public int getTimeKeyframeCount() {
return timeKeyframes.length;
}
public int getPositionKeyframeCount() {
return positionKeyframes.length;
}
public int getPathDuration() {
int first = 0;
int last = 0;
for(Keyframe k : getKeyframes()) {
if(k.getRealTimestamp() < first) {
first = k.getRealTimestamp();
} else if(k.getRealTimestamp() > last) {
last = k.getRealTimestamp();
}
}
return last-first;
}
public int getCustomObjectCount() {
return customObjects.length;
}
@Override
public String getDisplayString() {
return name;
}
}

View File

@@ -1,10 +1,10 @@
package eu.crushedpixel.replaymod.preparation;
import de.johni0702.replaystudio.PacketData;
import de.johni0702.replaystudio.io.ReplayInputStream;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.studio.ReplayStudio;
import de.johni0702.replaystudio.util.PacketUtils;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.util.PacketUtils;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import org.apache.commons.lang3.tuple.Pair;
import org.spacehq.mc.protocol.data.game.values.entity.player.PositionElement;

View File

@@ -2,12 +2,12 @@ package eu.crushedpixel.replaymod.studio;
import com.google.common.base.Optional;
import com.google.gson.JsonObject;
import de.johni0702.replaystudio.PacketData;
import de.johni0702.replaystudio.Studio;
import de.johni0702.replaystudio.filter.StreamFilter;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import de.johni0702.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.core.ReplayMod;
import org.apache.commons.io.IOUtils;
import org.spacehq.mc.protocol.packet.ingame.server.ServerResourcePackSendPacket;

View File

@@ -1,10 +1,10 @@
package eu.crushedpixel.replaymod.studio;
import com.google.gson.JsonObject;
import de.johni0702.replaystudio.PacketData;
import de.johni0702.replaystudio.Studio;
import de.johni0702.replaystudio.filter.StreamFilter;
import de.johni0702.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.stream.PacketStream;
import eu.crushedpixel.replaymod.gui.elements.listeners.ProgressUpdateListener;
public class ProgressFilter implements StreamFilter {

View File

@@ -1,30 +1,25 @@
package eu.crushedpixel.replaymod.studio;
import com.google.common.base.Optional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.replaymod.pathing.serialize.LegacyKeyframeSetAdapter;
import de.johni0702.replaystudio.PacketData;
import de.johni0702.replaystudio.filter.ChangeTimestampFilter;
import de.johni0702.replaystudio.filter.NeutralizerFilter;
import de.johni0702.replaystudio.filter.RemoveFilter;
import de.johni0702.replaystudio.filter.SquashFilter;
import de.johni0702.replaystudio.io.ReplayOutputStream;
import de.johni0702.replaystudio.replay.ReplayFile;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import de.johni0702.replaystudio.replay.ZipReplayFile;
import de.johni0702.replaystudio.stream.PacketStream;
import de.johni0702.replaystudio.studio.ReplayStudio;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.filter.ChangeTimestampFilter;
import com.replaymod.replaystudio.filter.NeutralizerFilter;
import com.replaymod.replaystudio.filter.RemoveFilter;
import com.replaymod.replaystudio.filter.SquashFilter;
import com.replaymod.replaystudio.io.ReplayOutputStream;
import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ZipReplayFile;
import com.replaymod.replaystudio.stream.PacketStream;
import com.replaymod.replaystudio.studio.ReplayStudio;
import eu.crushedpixel.replaymod.gui.elements.listeners.ProgressUpdateListener;
import eu.crushedpixel.replaymod.holders.Keyframe;
import eu.crushedpixel.replaymod.holders.KeyframeSet;
import eu.crushedpixel.replaymod.holders.TimestampValue;
import net.minecraft.client.resources.I18n;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -98,6 +93,7 @@ public class StudioImplementation {
}
}
/*
KeyframeSet[] keyframeSets = new GsonBuilder()
.registerTypeAdapter(KeyframeSet[].class, new LegacyKeyframeSetAdapter())
.create().fromJson(new InputStreamReader(in.get()), KeyframeSet[].class);
@@ -130,6 +126,7 @@ public class StudioImplementation {
new Gson().toJson(resultSets.toArray(new KeyframeSet[resultSets.size()]), out);
out.flush();
out.close();
*/
}
public static void connectReplayFiles(List<File> filesToConnect, File outputFile, ProgressUpdateListener updateListener) throws IOException {

View File

@@ -2,10 +2,9 @@ package eu.crushedpixel.replaymod.utils;
import com.google.gson.Gson;
import com.replaymod.recording.packet.PacketSerializer;
import de.johni0702.replaystudio.replay.ReplayMetaData;
import com.replaymod.replaystudio.replay.ReplayMetaData;
import com.replaymod.core.ReplayMod;
import eu.crushedpixel.replaymod.assets.CustomObjectRepository;
import eu.crushedpixel.replaymod.holders.KeyframeSet;
import eu.crushedpixel.replaymod.holders.PacketData;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -118,9 +117,6 @@ public class ReplayFileIO {
FileUtils.write(file, json);
}
public static void write(KeyframeSet[] keyframeRegistry, File file) throws IOException {
write((Object) keyframeRegistry, file);
}
public static void write(ReplayMetaData metaData, File file) throws IOException {
write((Object) metaData, file);