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:
CrushedPixel
2015-06-09 16:08:06 +02:00
parent ea968ca418
commit d30ef19c89
13 changed files with 235 additions and 20 deletions

View File

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

View File

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

View File

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

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

View File

@@ -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();

View File

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

View File

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

View File

@@ -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"));

View File

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

View File

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

View File

@@ -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.

View File

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