Added Event Markers to Replay, which allow players to quickly mark important events while recording (M key by default)
Added Event Marker Indicators to be displayed on GuiTimeline
This commit is contained in:
@@ -4,6 +4,7 @@ import eu.crushedpixel.replaymod.ReplayMod;
|
|||||||
import eu.crushedpixel.replaymod.entities.CameraEntity.MoveDirection;
|
import eu.crushedpixel.replaymod.entities.CameraEntity.MoveDirection;
|
||||||
import eu.crushedpixel.replaymod.gui.GuiKeyframeRepository;
|
import eu.crushedpixel.replaymod.gui.GuiKeyframeRepository;
|
||||||
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
|
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
|
||||||
|
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||||
import eu.crushedpixel.replaymod.registry.KeybindRegistry;
|
import eu.crushedpixel.replaymod.registry.KeybindRegistry;
|
||||||
import eu.crushedpixel.replaymod.registry.PlayerHandler;
|
import eu.crushedpixel.replaymod.registry.PlayerHandler;
|
||||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||||
@@ -21,6 +22,8 @@ public class KeyInputHandler {
|
|||||||
private long prevKeysDown = Sys.getTime();
|
private long prevKeysDown = Sys.getTime();
|
||||||
|
|
||||||
public void onKeyInput() throws Exception {
|
public void onKeyInput() throws Exception {
|
||||||
|
if(!ReplayHandler.isInReplay()) return;
|
||||||
|
|
||||||
if(!Keyboard.isCreated()) Keyboard.create();
|
if(!Keyboard.isCreated()) Keyboard.create();
|
||||||
|
|
||||||
Keyboard.poll();
|
Keyboard.poll();
|
||||||
@@ -96,8 +99,6 @@ public class KeyInputHandler {
|
|||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void keyInput(InputEvent.KeyInputEvent event) {
|
public void keyInput(InputEvent.KeyInputEvent event) {
|
||||||
if (!ReplayHandler.isInReplay()) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
onKeyInput();
|
onKeyInput();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
@@ -124,6 +125,16 @@ public class KeyInputHandler {
|
|||||||
|
|
||||||
public void handleCustomKeybindings(KeyBinding kb, boolean found, int keyCode) {
|
public void handleCustomKeybindings(KeyBinding kb, boolean found, int keyCode) {
|
||||||
//Custom registered handlers
|
//Custom registered handlers
|
||||||
|
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_ADD_MARKER) && (kb.isPressed() || kb.getKeyCode() == keyCode)) {
|
||||||
|
if(ReplayHandler.isInReplay()) {
|
||||||
|
ReplayHandler.addMarker();
|
||||||
|
} else if(ConnectionEventHandler.isRecording()) {
|
||||||
|
ConnectionEventHandler.addMarker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ReplayHandler.isInReplay()) return;
|
||||||
|
|
||||||
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_PLAY_PAUSE) && (kb.isPressed() || kb.getKeyCode() == keyCode) && !ReplayHandler.isInPath()) {
|
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_PLAY_PAUSE) && (kb.isPressed() || kb.getKeyCode() == keyCode) && !ReplayHandler.isInPath()) {
|
||||||
ReplayMod.overlay.togglePlayPause();
|
ReplayMod.overlay.togglePlayPause();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public class GuiKeyframeTimeline extends GuiTimeline {
|
|||||||
public GuiKeyframeTimeline(int positionX, int positionY, int width) {
|
public GuiKeyframeTimeline(int positionX, int positionY, int width) {
|
||||||
super(positionX, positionY, width);
|
super(positionX, positionY, width);
|
||||||
showMarkers = true;
|
showMarkers = true;
|
||||||
|
showMarkerIndicators = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,8 +131,8 @@ public class GuiKeyframeTimeline extends GuiTimeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(nextSpectatorKeyframeRealTime != -1) {
|
if(nextSpectatorKeyframeRealTime != -1) {
|
||||||
int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, rightTime, bodyWidth, segmentLength);
|
int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, bodyWidth, segmentLength);
|
||||||
int nextX = getKeyframeX(nextSpectatorKeyframeRealTime, leftTime, rightTime, bodyWidth, segmentLength);
|
int nextX = getKeyframeX(nextSpectatorKeyframeRealTime, leftTime, bodyWidth, segmentLength);
|
||||||
|
|
||||||
drawGradientRect(keyframeX + 2, positionY + BORDER_TOP + 1, nextX - 2, positionY + BORDER_TOP + 4, 0xFF0080FF, 0xFF0080FF);
|
drawGradientRect(keyframeX + 2, positionY + BORDER_TOP + 1, nextX - 2, positionY + BORDER_TOP + 4, 0xFF0080FF, 0xFF0080FF);
|
||||||
}
|
}
|
||||||
@@ -154,7 +155,7 @@ public class GuiKeyframeTimeline extends GuiTimeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getKeyframeX(int timestamp, long leftTime, long rightTime, int bodyWidth, double segmentLength) {
|
private int getKeyframeX(int timestamp, long leftTime, int bodyWidth, double segmentLength) {
|
||||||
long positionInSegment = timestamp - leftTime;
|
long positionInSegment = timestamp - leftTime;
|
||||||
double fractionOfSegment = positionInSegment / segmentLength;
|
double fractionOfSegment = positionInSegment / segmentLength;
|
||||||
return (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
|
return (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
|
||||||
@@ -166,7 +167,7 @@ public class GuiKeyframeTimeline extends GuiTimeline {
|
|||||||
int textureY;
|
int textureY;
|
||||||
int y = positionY;
|
int y = positionY;
|
||||||
|
|
||||||
int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, rightTime, bodyWidth, segmentLength);
|
int keyframeX = getKeyframeX(kf.getRealTimestamp(), leftTime, bodyWidth, segmentLength);
|
||||||
|
|
||||||
if (kf instanceof PositionKeyframe) {
|
if (kf instanceof PositionKeyframe) {
|
||||||
textureX = KEYFRAME_PLACE_X;
|
textureX = KEYFRAME_PLACE_X;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package eu.crushedpixel.replaymod.gui.elements;
|
package eu.crushedpixel.replaymod.gui.elements;
|
||||||
|
|
||||||
import eu.crushedpixel.replaymod.ReplayMod;
|
import eu.crushedpixel.replaymod.ReplayMod;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Marker;
|
||||||
|
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.Gui;
|
import net.minecraft.client.gui.Gui;
|
||||||
|
|
||||||
@@ -25,6 +27,11 @@ public class GuiTimeline extends Gui implements GuiElement {
|
|||||||
protected static final int BORDER_RIGHT = 4;
|
protected static final int BORDER_RIGHT = 4;
|
||||||
protected static final int BODY_WIDTH = TEXTURE_WIDTH - BORDER_LEFT - BORDER_RIGHT;
|
protected static final int BODY_WIDTH = TEXTURE_WIDTH - BORDER_LEFT - BORDER_RIGHT;
|
||||||
|
|
||||||
|
protected static final int MARKER_TEXTURE_X = 40;
|
||||||
|
protected static final int MARKER_TEXTURE_Y = 39;
|
||||||
|
protected static final int MARKER_TEXTURE_WIDTH = 5;
|
||||||
|
protected static final int MARKER_TEXTURE_HEIGHT = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current position of the cursor. Should normally be between 0 and {@link #timelineLength}.
|
* Current position of the cursor. Should normally be between 0 and {@link #timelineLength}.
|
||||||
*/
|
*/
|
||||||
@@ -52,6 +59,11 @@ public class GuiTimeline extends Gui implements GuiElement {
|
|||||||
*/
|
*/
|
||||||
public boolean showMarkers;
|
public boolean showMarkers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to draw indicators for Replay Markers.
|
||||||
|
*/
|
||||||
|
public boolean showMarkerIndicators = true;
|
||||||
|
|
||||||
protected final int positionX;
|
protected final int positionX;
|
||||||
protected final int positionY;
|
protected final int positionY;
|
||||||
protected final int width;
|
protected final int width;
|
||||||
@@ -138,9 +150,35 @@ public class GuiTimeline extends Gui implements GuiElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(showMarkerIndicators) {
|
||||||
|
double segmentLength = timelineLength * zoom;
|
||||||
|
|
||||||
|
for(Marker marker : ReplayHandler.getMarkers()) {
|
||||||
|
if(marker.getTimestamp() <= rightTime && marker.getTimestamp() >= leftTime) {
|
||||||
|
int textureX = MARKER_TEXTURE_X;
|
||||||
|
int textureY = MARKER_TEXTURE_Y;
|
||||||
|
int y = positionY;
|
||||||
|
|
||||||
|
int markerX = getMarkerX(marker.getTimestamp(), leftTime, bodyWidth, segmentLength);
|
||||||
|
|
||||||
|
if(ReplayHandler.isSelected(marker)) {
|
||||||
|
textureX += MARKER_TEXTURE_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect(markerX - 2, y + HEIGHT - 3 - MARKER_TEXTURE_HEIGHT, textureX, textureY, MARKER_TEXTURE_WIDTH, MARKER_TEXTURE_HEIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drawTimelineCursor(leftTime, rightTime, bodyWidth);
|
drawTimelineCursor(leftTime, rightTime, bodyWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getMarkerX(int timestamp, long leftTime, int bodyWidth, double segmentLength) {
|
||||||
|
long positionInSegment = timestamp - leftTime;
|
||||||
|
double fractionOfSegment = positionInSegment / segmentLength;
|
||||||
|
return (int) (positionX + BORDER_LEFT + fractionOfSegment * bodyWidth);
|
||||||
|
}
|
||||||
|
|
||||||
protected void drawTimelineCursor(long leftTime, long rightTime, int bodyWidth) {
|
protected void drawTimelineCursor(long leftTime, long rightTime, int bodyWidth) {
|
||||||
// Draw the cursor if it's on the current timeline segment
|
// Draw the cursor if it's on the current timeline segment
|
||||||
if (cursorPosition >= leftTime && cursorPosition <= rightTime) {
|
if (cursorPosition >= leftTime && cursorPosition <= rightTime) {
|
||||||
|
|||||||
45
src/main/java/eu/crushedpixel/replaymod/holders/Marker.java
Normal file
45
src/main/java/eu/crushedpixel/replaymod/holders/Marker.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package eu.crushedpixel.replaymod.holders;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
|
||||||
|
public class Marker {
|
||||||
|
|
||||||
|
public Marker(Position position, int timestamp, String name) {
|
||||||
|
this.position = position;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Position position;
|
||||||
|
private int timestamp;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Position getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o2) {
|
||||||
|
if(o2 == null) return false;
|
||||||
|
if(!(o2 instanceof Marker)) return false;
|
||||||
|
Marker m2 = (Marker)o2;
|
||||||
|
return hashCode() == m2.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(position)
|
||||||
|
.append(timestamp)
|
||||||
|
.append(name)
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,19 @@ public class ConnectionEventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addMarker() {
|
||||||
|
if(!isRecording || packetListener == null) {
|
||||||
|
String reason = isRecording ? " (recording)" : " (null)";
|
||||||
|
logger.error("Invalid attempt to insert Marker!" + reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
packetListener.addMarker();
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onConnectedToServerEvent(ClientConnectedToServerEvent event) {
|
public void onConnectedToServerEvent(ClientConnectedToServerEvent event) {
|
||||||
ReplayMod.chatMessageHandler.initialize();
|
ReplayMod.chatMessageHandler.initialize();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.google.common.hash.Hashing;
|
|||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import eu.crushedpixel.replaymod.ReplayMod;
|
import eu.crushedpixel.replaymod.ReplayMod;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Marker;
|
||||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||||
import eu.crushedpixel.replaymod.utils.ReplayFile;
|
import eu.crushedpixel.replaymod.utils.ReplayFile;
|
||||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||||
@@ -34,6 +35,7 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
|||||||
protected boolean alive = true;
|
protected boolean alive = true;
|
||||||
protected DataWriter dataWriter;
|
protected DataWriter dataWriter;
|
||||||
protected Set<String> players = new HashSet<String>();
|
protected Set<String> players = new HashSet<String>();
|
||||||
|
protected Set<Marker> markers = new HashSet<Marker>();
|
||||||
private boolean singleplayer;
|
private boolean singleplayer;
|
||||||
private Gson gson = new Gson();
|
private Gson gson = new Gson();
|
||||||
|
|
||||||
@@ -43,7 +45,6 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
|||||||
private final Map<Integer, String> requestToHash = new ConcurrentHashMap<Integer, String>();
|
private final Map<Integer, String> requestToHash = new ConcurrentHashMap<Integer, String>();
|
||||||
private final Map<String, File> resourcePacks = new HashMap<String, File>();
|
private final Map<String, File> resourcePacks = new HashMap<String, File>();
|
||||||
|
|
||||||
|
|
||||||
public DataListener(File file, String name, String worldName, long startTime, boolean singleplayer) throws FileNotFoundException {
|
public DataListener(File file, String name, String worldName, long startTime, boolean singleplayer) throws FileNotFoundException {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
@@ -77,7 +78,7 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelInactive(ChannelHandlerContext ctx) {
|
public void channelInactive(ChannelHandlerContext ctx) {
|
||||||
dataWriter.requestFinish(players);
|
dataWriter.requestFinish(players, markers);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void recordResourcePack(File file, int requestId) {
|
protected void recordResourcePack(File file, int requestId) {
|
||||||
@@ -153,7 +154,7 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
|||||||
queue.add(data);
|
queue.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestFinish(Set<String> players) {
|
public void requestFinish(Set<String> players, Set<Marker> markers) {
|
||||||
active = false;
|
active = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -177,7 +178,7 @@ public abstract class DataListener extends ChannelInboundHandlerAdapter {
|
|||||||
File archive = new File(folder, name + ReplayFile.ZIP_FILE_EXTENSION);
|
File archive = new File(folder, name + ReplayFile.ZIP_FILE_EXTENSION);
|
||||||
archive.createNewFile();
|
archive.createNewFile();
|
||||||
|
|
||||||
ReplayFileIO.writeReplayFile(archive, file, metaData, resourcePacks, requestToHash);
|
ReplayFileIO.writeReplayFile(archive, file, metaData, markers, resourcePacks, requestToHash);
|
||||||
|
|
||||||
file.delete();
|
file.delete();
|
||||||
FileUtils.deleteDirectory(tempResourcePacksFolder);
|
FileUtils.deleteDirectory(tempResourcePacksFolder);
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import com.google.common.io.Files;
|
|||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import eu.crushedpixel.replaymod.ReplayMod;
|
||||||
|
import eu.crushedpixel.replaymod.chat.ChatMessageHandler;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Marker;
|
||||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Position;
|
||||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
@@ -215,6 +219,17 @@ public class PacketListener extends DataListener {
|
|||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMarker() {
|
||||||
|
Position pos = new Position(Minecraft.getMinecraft().getRenderViewEntity());
|
||||||
|
int timestamp = (int) (System.currentTimeMillis() - startTime);
|
||||||
|
|
||||||
|
Marker marker = new Marker(pos, timestamp, null);
|
||||||
|
|
||||||
|
markers.add(marker);
|
||||||
|
|
||||||
|
ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.addedmarker", ChatMessageHandler.ChatMessageType.INFORMATION);
|
||||||
|
}
|
||||||
|
|
||||||
private void downloadResourcePackFuture(int requestId, String url, final String hash) {
|
private void downloadResourcePackFuture(int requestId, String url, final String hash) {
|
||||||
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
|
Futures.addCallback(downloadResourcePack(requestId, url, hash), new FutureCallback() {
|
||||||
public void onSuccess(Object result) {
|
public void onSuccess(Object result) {
|
||||||
|
|||||||
@@ -20,17 +20,19 @@ public class KeybindRegistry {
|
|||||||
public static final String KEY_ROLL_COUNTERCLOCKWISE = "replaymod.input.rollcounterclockwise";
|
public static final String KEY_ROLL_COUNTERCLOCKWISE = "replaymod.input.rollcounterclockwise";
|
||||||
public static final String KEY_RESET_TILT = "replaymod.input.resettilt";
|
public static final String KEY_RESET_TILT = "replaymod.input.resettilt";
|
||||||
public static final String KEY_PLAY_PAUSE = "replaymod.input.playpause";
|
public static final String KEY_PLAY_PAUSE = "replaymod.input.playpause";
|
||||||
|
public static final String KEY_ADD_MARKER = "replaymod.input.marker";
|
||||||
private static Minecraft mc = Minecraft.getMinecraft();
|
private static Minecraft mc = Minecraft.getMinecraft();
|
||||||
|
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
List<KeyBinding> bindings = new ArrayList<KeyBinding>(Arrays.asList(mc.gameSettings.keyBindings));
|
List<KeyBinding> bindings = new ArrayList<KeyBinding>(Arrays.asList(mc.gameSettings.keyBindings));
|
||||||
|
|
||||||
bindings.add(new KeyBinding(KEY_LIGHTING, Keyboard.KEY_M, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_ADD_MARKER, Keyboard.KEY_M, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_THUMBNAIL, Keyboard.KEY_N, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_THUMBNAIL, Keyboard.KEY_N, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_PLAYER_OVERVIEW, Keyboard.KEY_B, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_PLAYER_OVERVIEW, Keyboard.KEY_B, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_SYNC_TIMELINE, Keyboard.KEY_V, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_SYNC_TIMELINE, Keyboard.KEY_V, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_CLEAR_KEYFRAMES, Keyboard.KEY_C, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_CLEAR_KEYFRAMES, Keyboard.KEY_C, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_KEYFRAME_PRESETS, Keyboard.KEY_X, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_KEYFRAME_PRESETS, Keyboard.KEY_X, "replaymod.title"));
|
||||||
|
bindings.add(new KeyBinding(KEY_LIGHTING, Keyboard.KEY_Z, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_ROLL_CLOCKWISE, Keyboard.KEY_L, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_ROLL_CLOCKWISE, Keyboard.KEY_L, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_RESET_TILT, Keyboard.KEY_K, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_RESET_TILT, Keyboard.KEY_K, "replaymod.title"));
|
||||||
bindings.add(new KeyBinding(KEY_ROLL_COUNTERCLOCKWISE, Keyboard.KEY_J, "replaymod.title"));
|
bindings.add(new KeyBinding(KEY_ROLL_COUNTERCLOCKWISE, Keyboard.KEY_J, "replaymod.title"));
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class ReplayHandler {
|
|||||||
private static float cameraTilt = 0;
|
private static float cameraTilt = 0;
|
||||||
|
|
||||||
private static KeyframeSet[] keyframeRepository = new KeyframeSet[]{};
|
private static KeyframeSet[] keyframeRepository = new KeyframeSet[]{};
|
||||||
|
private static Marker[] markers = new Marker[]{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The file currently being played.
|
* The file currently being played.
|
||||||
@@ -55,7 +56,6 @@ public class ReplayHandler {
|
|||||||
if(write) {
|
if(write) {
|
||||||
try {
|
try {
|
||||||
File tempFile = File.createTempFile(ReplayFile.ENTRY_PATHS, "json");
|
File tempFile = File.createTempFile(ReplayFile.ENTRY_PATHS, "json");
|
||||||
tempFile.deleteOnExit();
|
|
||||||
|
|
||||||
ReplayFileIO.writeKeyframeRegistryToFile(repo, tempFile);
|
ReplayFileIO.writeKeyframeRegistryToFile(repo, tempFile);
|
||||||
|
|
||||||
@@ -66,6 +66,23 @@ public class ReplayHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Marker[] getMarkers() { return markers; }
|
||||||
|
|
||||||
|
public static void setMarkers(Marker[] m, boolean write) {
|
||||||
|
markers = m;
|
||||||
|
if(write) {
|
||||||
|
try {
|
||||||
|
File tempFile = File.createTempFile(ReplayFile.ENTRY_MARKERS, "json");
|
||||||
|
|
||||||
|
ReplayFileIO.writeMarkersToFile(m, tempFile);
|
||||||
|
|
||||||
|
ReplayMod.replayFileAppender.registerModifiedFile(tempFile, ReplayFile.ENTRY_MARKERS, getReplayFile());
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void useKeyframePresetFromRepository(int index) {
|
public static void useKeyframePresetFromRepository(int index) {
|
||||||
keyframes = new ArrayList<Keyframe>(Arrays.asList(keyframeRepository[index].getKeyframes()));
|
keyframes = new ArrayList<Keyframe>(Arrays.asList(keyframeRepository[index].getKeyframes()));
|
||||||
}
|
}
|
||||||
@@ -145,6 +162,24 @@ public class ReplayHandler {
|
|||||||
Collections.sort(keyframes, new KeyframeComparator());
|
Collections.sort(keyframes, new KeyframeComparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addMarker() {
|
||||||
|
Position pos = new Position(mc.getRenderViewEntity());
|
||||||
|
int timestamp = ReplayMod.replaySender.currentTimeStamp();
|
||||||
|
addMarker(new Marker(pos, timestamp, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addMarker(Marker marker) {
|
||||||
|
List<Marker> markerList = new ArrayList<Marker>(Arrays.asList(markers));
|
||||||
|
markerList.add(marker);
|
||||||
|
markers = markerList.toArray(new Marker[markerList.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeMarker(Marker marker) {
|
||||||
|
List<Marker> markerList = new ArrayList<Marker>(Arrays.asList(markers));
|
||||||
|
markerList.remove(marker);
|
||||||
|
markers = markerList.toArray(new Marker[markerList.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
public static void addKeyframe(Keyframe keyframe) {
|
public static void addKeyframe(Keyframe keyframe) {
|
||||||
keyframes.add(keyframe);
|
keyframes.add(keyframe);
|
||||||
selectKeyframe(keyframe);
|
selectKeyframe(keyframe);
|
||||||
@@ -330,6 +365,11 @@ public class ReplayHandler {
|
|||||||
return kf == selectedKeyframe;
|
return kf == selectedKeyframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isSelected(Marker marker) {
|
||||||
|
//TODO: Make marker selectable
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void selectKeyframe(Keyframe kf) {
|
public static void selectKeyframe(Keyframe kf) {
|
||||||
selectedKeyframe = kf;
|
selectedKeyframe = kf;
|
||||||
sortKeyframes();
|
sortKeyframes();
|
||||||
@@ -381,6 +421,9 @@ public class ReplayHandler {
|
|||||||
KeyframeSet[] paths = currentReplayFile.paths().get();
|
KeyframeSet[] paths = currentReplayFile.paths().get();
|
||||||
ReplayHandler.setKeyframeRepository(paths == null ? new KeyframeSet[0] : paths, false);
|
ReplayHandler.setKeyframeRepository(paths == null ? new KeyframeSet[0] : paths, false);
|
||||||
|
|
||||||
|
Marker[] markers = currentReplayFile.markers().get();
|
||||||
|
ReplayHandler.setMarkers(markers == null ? new Marker[0] : markers, false);
|
||||||
|
|
||||||
PlayerVisibility visibility = currentReplayFile.visibility().get();
|
PlayerVisibility visibility = currentReplayFile.visibility().get();
|
||||||
PlayerHandler.loadPlayerVisibilityConfiguration(visibility);
|
PlayerHandler.loadPlayerVisibilityConfiguration(visibility);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Marker;
|
||||||
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
|
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
|
||||||
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
|
||||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||||
@@ -30,6 +31,7 @@ public class ReplayFile extends ZipFile {
|
|||||||
public static final String ENTRY_RESOURCE_PACK_INDEX = "resourcepack/index.json";
|
public static final String ENTRY_RESOURCE_PACK_INDEX = "resourcepack/index.json";
|
||||||
public static final String ENTRY_VISIBILITY_OLD = "visibility";
|
public static final String ENTRY_VISIBILITY_OLD = "visibility";
|
||||||
public static final String ENTRY_VISIBILITY = "visibility" + JSON_FILE_EXTENSION;
|
public static final String ENTRY_VISIBILITY = "visibility" + JSON_FILE_EXTENSION;
|
||||||
|
public static final String ENTRY_MARKERS = "markers" + JSON_FILE_EXTENSION;
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
|
|
||||||
@@ -82,11 +84,6 @@ public class ReplayFile extends ZipFile {
|
|||||||
return newEntry != null ? newEntry : getEntry(ENTRY_PATHS_OLD);
|
return newEntry != null ? newEntry : getEntry(ENTRY_PATHS_OLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZipArchiveEntry visibilityEntry() {
|
|
||||||
ZipArchiveEntry newEntry = getEntry(ENTRY_VISIBILITY);
|
|
||||||
return newEntry != null ? newEntry : getEntry(ENTRY_VISIBILITY_OLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<KeyframeSet[]> paths() {
|
public Supplier<KeyframeSet[]> paths() {
|
||||||
return new Supplier<KeyframeSet[]>() {
|
return new Supplier<KeyframeSet[]>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -105,6 +102,11 @@ public class ReplayFile extends ZipFile {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ZipArchiveEntry visibilityEntry() {
|
||||||
|
ZipArchiveEntry newEntry = getEntry(ENTRY_VISIBILITY);
|
||||||
|
return newEntry != null ? newEntry : getEntry(ENTRY_VISIBILITY_OLD);
|
||||||
|
}
|
||||||
|
|
||||||
public Supplier<PlayerVisibility> visibility() {
|
public Supplier<PlayerVisibility> visibility() {
|
||||||
return new Supplier<PlayerVisibility>() {
|
return new Supplier<PlayerVisibility>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -123,6 +125,28 @@ public class ReplayFile extends ZipFile {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ZipArchiveEntry markersEntry() {
|
||||||
|
return getEntry(ENTRY_MARKERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Marker[]> markers() {
|
||||||
|
return new Supplier<Marker[]>() {
|
||||||
|
@Override
|
||||||
|
public Marker[] get() {
|
||||||
|
try {
|
||||||
|
ZipArchiveEntry entry = markersEntry();
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(getInputStream(entry)));
|
||||||
|
return new Gson().fromJson(reader, Marker[].class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public ZipArchiveEntry thumbEntry() {
|
public ZipArchiveEntry thumbEntry() {
|
||||||
return getEntry(ENTRY_THUMB);
|
return getEntry(ENTRY_THUMB);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.utils;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import eu.crushedpixel.replaymod.ReplayMod;
|
import eu.crushedpixel.replaymod.ReplayMod;
|
||||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||||
|
import eu.crushedpixel.replaymod.holders.Marker;
|
||||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||||
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
|
import eu.crushedpixel.replaymod.holders.PlayerVisibility;
|
||||||
import eu.crushedpixel.replaymod.recording.PacketSerializer;
|
import eu.crushedpixel.replaymod.recording.PacketSerializer;
|
||||||
@@ -57,7 +58,7 @@ public class ReplayFileIO {
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeReplayFile(File replayFile, File tempFile, ReplayMetaData metaData,
|
public static void writeReplayFile(File replayFile, File tempFile, ReplayMetaData metaData, Set<Marker> markers,
|
||||||
Map<String, File> resourcePacks, Map<Integer, String> resourcePackRequests) throws IOException {
|
Map<String, File> resourcePacks, Map<Integer, String> resourcePackRequests) throws IOException {
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
|
|
||||||
@@ -86,14 +87,23 @@ public class ReplayFileIO {
|
|||||||
fis.close();
|
fis.close();
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
|
|
||||||
if (!resourcePackRequests.isEmpty()) {
|
if(!markers.isEmpty()) {
|
||||||
|
zos.putNextEntry(new ZipEntry(ReplayFile.ENTRY_MARKERS));
|
||||||
|
|
||||||
|
pw = new PrintWriter(zos);
|
||||||
|
pw.write(new Gson().toJson(markers.toArray(new Marker[markers.size()])));
|
||||||
|
pw.flush();
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!resourcePackRequests.isEmpty()) {
|
||||||
zos.putNextEntry(new ZipEntry(ReplayFile.ENTRY_RESOURCE_PACK_INDEX));
|
zos.putNextEntry(new ZipEntry(ReplayFile.ENTRY_RESOURCE_PACK_INDEX));
|
||||||
pw = new PrintWriter(zos);
|
pw = new PrintWriter(zos);
|
||||||
pw.write(new Gson().toJson(resourcePackRequests));
|
pw.write(new Gson().toJson(resourcePackRequests));
|
||||||
pw.flush();
|
pw.flush();
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
|
|
||||||
for (Map.Entry<String, File> entry : resourcePacks.entrySet()) {
|
for(Map.Entry<String, File> entry : resourcePacks.entrySet()) {
|
||||||
zos.putNextEntry(new ZipEntry(String.format(ReplayFile.ENTRY_RESOURCE_PACK, entry.getKey())));
|
zos.putNextEntry(new ZipEntry(String.format(ReplayFile.ENTRY_RESOURCE_PACK, entry.getKey())));
|
||||||
IOUtils.copy(new FileInputStream(entry.getValue()), zos);
|
IOUtils.copy(new FileInputStream(entry.getValue()), zos);
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
@@ -322,6 +332,14 @@ public class ReplayFileIO {
|
|||||||
FileUtils.write(file, json);
|
FileUtils.write(file, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void writeMarkersToFile(Marker[] markers, File file) throws IOException {
|
||||||
|
file.mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
|
||||||
|
String json = gson.toJson(markers);
|
||||||
|
FileUtils.write(file, json);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds Files as entries to a ZIP File.
|
* Adds Files as entries to a ZIP File.
|
||||||
* @param zipFile The ZIP File to add the files to.
|
* @param zipFile The ZIP File to add the files to.
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ replaymod.chat.savingthumb=Saving Thumbnail...
|
|||||||
replaymod.chat.savedthumb=Thumbnail has been successfully saved
|
replaymod.chat.savedthumb=Thumbnail has been successfully saved
|
||||||
replaymod.chat.failedthumb=Thumbnail could not be saved
|
replaymod.chat.failedthumb=Thumbnail could not be saved
|
||||||
|
|
||||||
|
replaymod.chat.addedmarker=Event Marker has been added
|
||||||
|
|
||||||
#Chat messages displayed in Replay Viewer
|
#Chat messages displayed in Replay Viewer
|
||||||
replaymod.chat.notenoughkeyframes=At least 2 position or time keyframes required
|
replaymod.chat.notenoughkeyframes=At least 2 position or time keyframes required
|
||||||
replaymod.chat.pathstarted=Camera Path started
|
replaymod.chat.pathstarted=Camera Path started
|
||||||
@@ -156,6 +158,7 @@ replaymod.gui.upload.start=Start Upload
|
|||||||
replaymod.gui.upload.cancel=Cancel Upload
|
replaymod.gui.upload.cancel=Cancel Upload
|
||||||
replaymod.gui.upload.tagshint=Tags separated by comma
|
replaymod.gui.upload.tagshint=Tags separated by comma
|
||||||
replaymod.gui.upload.namehint=Replay Title
|
replaymod.gui.upload.namehint=Replay Title
|
||||||
|
replaymod.gui.upload.descriptionhint=Description
|
||||||
replaymod.gui.upload.uploading=Uploading...
|
replaymod.gui.upload.uploading=Uploading...
|
||||||
replaymod.gui.upload.success=File has been successfully uploaded
|
replaymod.gui.upload.success=File has been successfully uploaded
|
||||||
replaymod.gui.upload.canceled=Upload has been canceled
|
replaymod.gui.upload.canceled=Upload has been canceled
|
||||||
@@ -226,6 +229,7 @@ replaymod.input.rollclockwise=Roll Clockwise
|
|||||||
replaymod.input.rollcounterclockwise=Roll Counterclockwise
|
replaymod.input.rollcounterclockwise=Roll Counterclockwise
|
||||||
replaymod.input.resettilt=Reset Camera Tilt
|
replaymod.input.resettilt=Reset Camera Tilt
|
||||||
replaymod.input.playpause=Play/Pause Replay
|
replaymod.input.playpause=Play/Pause Replay
|
||||||
|
replaymod.input.marker=Add Event Marker
|
||||||
|
|
||||||
#Keyframe Presets GUI
|
#Keyframe Presets GUI
|
||||||
replaymod.gui.keyframerepository.title=Keyframe Repository
|
replaymod.gui.keyframerepository.title=Keyframe Repository
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Reference in New Issue
Block a user