Added Keyframe Repository, which is stored in Replay File itself and allows Camera Path Presets to be saved and loaded
This commit is contained in:
@@ -6,7 +6,7 @@ import eu.crushedpixel.replaymod.events.*;
|
||||
import eu.crushedpixel.replaymod.localization.LocalizedResourcePack;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import eu.crushedpixel.replaymod.registry.FileCopyHandler;
|
||||
import eu.crushedpixel.replaymod.registry.ReplayFileAppender;
|
||||
import eu.crushedpixel.replaymod.registry.KeybindRegistry;
|
||||
import eu.crushedpixel.replaymod.renderer.SafeEntityRenderer;
|
||||
import eu.crushedpixel.replaymod.replay.ReplaySender;
|
||||
@@ -62,7 +62,7 @@ public class ReplayMod {
|
||||
public static KeyInputHandler keyInputHandler = new KeyInputHandler();
|
||||
public static ReplaySender replaySender;
|
||||
public static int TP_DISTANCE_LIMIT = 128;
|
||||
public static FileCopyHandler fileCopyHandler;
|
||||
public static ReplayFileAppender replayFileAppender;
|
||||
|
||||
private static Field defaultResourcePacksField;
|
||||
static {
|
||||
@@ -86,8 +86,8 @@ public class ReplayMod {
|
||||
replaySettings = new ReplaySettings();
|
||||
replaySettings.readValues();
|
||||
|
||||
fileCopyHandler = new FileCopyHandler();
|
||||
fileCopyHandler.start();
|
||||
replayFileAppender = new ReplayFileAppender();
|
||||
replayFileAppender.start();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
||||
@@ -2,10 +2,7 @@ package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.gui.GuiCancelRender;
|
||||
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
|
||||
import eu.crushedpixel.replaymod.gui.GuiReplaySpeedSlider;
|
||||
import eu.crushedpixel.replaymod.gui.GuiSpectateSelection;
|
||||
import eu.crushedpixel.replaymod.gui.*;
|
||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.holders.PositionKeyframe;
|
||||
@@ -120,7 +117,8 @@ public class GuiReplayOverlay extends Gui {
|
||||
@SubscribeEvent
|
||||
public void onRenderGui(RenderGameOverlayEvent.Post event) throws IllegalArgumentException, IllegalAccessException {
|
||||
|
||||
if(!ReplayHandler.isInReplay() || FMLClientHandler.instance().isGUIOpen(GuiSpectateSelection.class) || VideoWriter.isRecording()) {
|
||||
if(!ReplayHandler.isInReplay() || FMLClientHandler.instance().isGUIOpen(GuiSpectateSelection.class)
|
||||
|| VideoWriter.isRecording() || FMLClientHandler.instance().isGUIOpen(GuiKeyframeRepository.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.events;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity.MoveDirection;
|
||||
import eu.crushedpixel.replaymod.gui.GuiCancelRender;
|
||||
import eu.crushedpixel.replaymod.gui.GuiKeyframeRepository;
|
||||
import eu.crushedpixel.replaymod.gui.GuiMouseInput;
|
||||
import eu.crushedpixel.replaymod.registry.KeybindRegistry;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
@@ -87,12 +88,12 @@ public class KeyInputHandler {
|
||||
ReplayHandler.getCameraEntity().speedUp();
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals("key.chat")) {
|
||||
if(kb.getKeyDescription().equals("key.chat") && kb.isPressed()) {
|
||||
mc.displayGuiScreen(new GuiMouseInput());
|
||||
break;
|
||||
}
|
||||
|
||||
handleCustomKeybindings(kb, found, null);
|
||||
handleCustomKeybindings(kb, found, -1);
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -101,11 +102,10 @@ public class KeyInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCustomKeybindings(KeyBinding kb, boolean found, Integer keyCode) {
|
||||
public void handleCustomKeybindings(KeyBinding kb, boolean found, int keyCode) {
|
||||
//Custom registered handlers
|
||||
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_THUMBNAIL) && (kb.isPressed() || kb.getKeyCode() == keyCode) && !found) {
|
||||
TickAndRenderListener.requestScreenshot();
|
||||
//TODO: Make this properly work
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_SPECTATE) && (kb.isPressed() || kb.getKeyCode() == keyCode) && !found) {
|
||||
@@ -123,5 +123,9 @@ public class KeyInputHandler {
|
||||
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_SYNC_TIMELINE) && (kb.isPressed() || kb.getKeyCode() == keyCode)) {
|
||||
ReplayHandler.syncTimeCursor(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT));
|
||||
}
|
||||
|
||||
if(kb.getKeyDescription().equals(KeybindRegistry.KEY_KEYFRAME_PRESETS) && (kb.isPressed() || kb.getKeyCode() == keyCode)) {
|
||||
mc.displayGuiScreen(new GuiKeyframeRepository(ReplayHandler.getKeyframeRepository()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ package eu.crushedpixel.replaymod.events;
|
||||
|
||||
import eu.crushedpixel.replaymod.reflection.MCPNames;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.inventory.GuiInventory;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.network.play.client.C16PacketClientStatus;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.ReportedException;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
@@ -260,31 +256,6 @@ public class MinecraftTicker {
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag = mc.gameSettings.chatVisibility != EntityPlayer.EnumChatVisibility.HIDDEN;
|
||||
|
||||
while(mc.gameSettings.keyBindInventory.isPressed()) {
|
||||
if(mc.playerController.isRidingHorse()) {
|
||||
mc.thePlayer.sendHorseInventory();
|
||||
} else {
|
||||
mc.getNetHandler().addToSendQueue(new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT));
|
||||
mc.displayGuiScreen(new GuiInventory(mc.thePlayer));
|
||||
}
|
||||
}
|
||||
|
||||
while(mc.gameSettings.keyBindDrop.isPressed()) {
|
||||
if(!mc.thePlayer.isSpectator()) {
|
||||
mc.thePlayer.dropOneItem(GuiScreen.isCtrlKeyDown());
|
||||
}
|
||||
}
|
||||
|
||||
while(mc.gameSettings.keyBindChat.isPressed() && flag) {
|
||||
mc.displayGuiScreen(new GuiChat());
|
||||
}
|
||||
|
||||
if(mc.currentScreen == null && mc.gameSettings.keyBindCommand.isPressed() && flag) {
|
||||
mc.displayGuiScreen(new GuiChat("/"));
|
||||
}
|
||||
|
||||
if(mc.thePlayer != null && mc.thePlayer.isUsingItem()) {
|
||||
if(!mc.gameSettings.keyBindUseItem.isKeyDown()) {
|
||||
mc.playerController.onStoppedUsingItem(mc.thePlayer);
|
||||
|
||||
@@ -45,4 +45,10 @@ public class GuiConstants {
|
||||
|
||||
public static final int REPLAY_EDITOR_REMOVE_BUTTON = 5008;
|
||||
public static final int REPLAY_EDITOR_ADD_BUTTON = 5009;
|
||||
|
||||
public static final int KEYFRAME_REPOSITORY_LIST = 1234;
|
||||
public static final int KEYFRAME_REPOSTORY_NAME_INPUT = 2345;
|
||||
public static final int KEYFRAME_REPOSITORY_ADD_BUTTON = 3456;
|
||||
public static final int KEYFRAME_REPOSITORY_REMOVE_BUTTON = 4567;
|
||||
public static final int KEYFRAME_REPOSITORY_LOAD_BUTTON = 5678;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package eu.crushedpixel.replaymod.gui;
|
||||
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.gui.elements.GuiEntryList;
|
||||
import eu.crushedpixel.replaymod.gui.elements.listeners.SelectionListener;
|
||||
import eu.crushedpixel.replaymod.holders.Keyframe;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
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;
|
||||
|
||||
public class GuiKeyframeRepository extends GuiScreen {
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private final String screenTitle = I18n.format("replaymod.gui.keyframerepository.title");
|
||||
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 double prevSpeed;
|
||||
|
||||
public GuiKeyframeRepository(KeyframeSet[] keyframeRepository) {
|
||||
this.keyframeRepository = keyframeRepository;
|
||||
prevSpeed = ReplayMod.replaySender.getReplaySpeed();
|
||||
ReplayMod.replaySender.setReplaySpeed(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
int h = (int)Math.floor(((double)this.height-(45+20+15))/14);
|
||||
|
||||
if(!initialized) {
|
||||
keyframeSetList = new GuiEntryList<KeyframeSet>(GuiConstants.KEYFRAME_REPOSITORY_LIST, mc.fontRendererObj,
|
||||
20, 45, this.width / 2, 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,
|
||||
30 + (this.width / 2), 45, this.width - (30 + this.width / 2 + 20), 20);
|
||||
nameInput.setEnabled(false);
|
||||
|
||||
removeButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_REMOVE_BUTTON, 30 + (this.width / 2), 75,
|
||||
I18n.format("replaymod.gui.remove"));
|
||||
removeButton.width = (nameInput.width / 2) - 3;
|
||||
removeButton.enabled = false;
|
||||
|
||||
loadButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_LOAD_BUTTON, 30 + (this.width / 2) + 5 + removeButton.width, 75,
|
||||
I18n.format("replaymod.gui.load"));
|
||||
loadButton.width = removeButton.width + 1;
|
||||
loadButton.enabled = false;
|
||||
|
||||
saveButton = new GuiButton(GuiConstants.KEYFRAME_REPOSITORY_ADD_BUTTON, 30 + (this.width / 2),
|
||||
keyframeSetList.yPosition+keyframeSetList.height-20,
|
||||
I18n.format("replaymod.gui.keyframerepository.savecurrent"));
|
||||
saveButton.width = nameInput.width;
|
||||
} else {
|
||||
keyframeSetList.width = this.width / 2;
|
||||
keyframeSetList.setVisibleElements(h);
|
||||
|
||||
nameInput.xPosition = 30 + (this.width / 2);
|
||||
nameInput.width = this.width - (30 + this.width / 2 + 20);
|
||||
|
||||
removeButton.xPosition = 30 + (this.width / 2);
|
||||
removeButton.width = (nameInput.width / 2) - 3;
|
||||
|
||||
loadButton.xPosition = 30 + (this.width / 2) + 5 + removeButton.width;
|
||||
loadButton.width = removeButton.width + 1;
|
||||
|
||||
saveButton.xPosition = 30 + (this.width / 2);
|
||||
saveButton.width = nameInput.width;
|
||||
saveButton.yPosition = keyframeSetList.yPosition+keyframeSetList.height-20;
|
||||
}
|
||||
|
||||
buttonList.add(removeButton);
|
||||
buttonList.add(loadButton);
|
||||
buttonList.add(saveButton);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(GuiButton button) {
|
||||
if(!button.enabled) return;
|
||||
switch(button.id) {
|
||||
case GuiConstants.KEYFRAME_REPOSITORY_ADD_BUTTON:
|
||||
Keyframe[] keyframes = ReplayHandler.getKeyframes().toArray(new Keyframe[ReplayHandler.getKeyframes().size()]);
|
||||
KeyframeSet newSet = new KeyframeSet(I18n.format("replaymod.gui.keyframerepository.preset.defaultname"), keyframes);
|
||||
if(newSet.getPositionKeyframeCount() < 2 && newSet.getTimeKeyframeCount() < 2) {
|
||||
message = I18n.format("replaymod.chat.notenoughkeyframes");
|
||||
break;
|
||||
} else 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.useKeyframePresetFromRepository(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"), 20 + (this.width / 4), 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,
|
||||
30 + (this.width / 2), removeButton.yPosition + 30, Color.WHITE.getRGB());
|
||||
|
||||
this.drawString(fontRendererObj, I18n.format("replaymod.gui.keyframerepository.timekeyframes") + ": " +currentSetTimeKeyframeCount,
|
||||
30 + (this.width / 2), removeButton.yPosition + 50, Color.WHITE.getRGB());
|
||||
|
||||
this.drawString(fontRendererObj, I18n.format("replaymod.gui.duration")+": "+ DurationFormatUtils.formatDurationHMS(currentSetDuration),
|
||||
30 + (this.width / 2), removeButton.yPosition + 70, 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.setName(nameInput.getText());
|
||||
keyframeSetList.replaceElement(keyframeSetList.getSelectionIndex(), currentKeyframeSet);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOnQuit() {
|
||||
ArrayList<KeyframeSet> copy = new ArrayList<KeyframeSet>(keyframeSetList.getCopyOfElements());
|
||||
this.keyframeRepository = copy.toArray(new KeyframeSet[copy.size()]);
|
||||
ReplayHandler.setKeyframeRepository(keyframeRepository, true);
|
||||
ReplayMod.replaySender.setReplaySpeed(prevSpeed);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ public class GuiEntryList<T> extends GuiTextField {
|
||||
private int visibleElements;
|
||||
private int upperIndex = 0;
|
||||
|
||||
private String emptyMessage = null;
|
||||
|
||||
private List<SelectionListener> selectionListeners = new ArrayList<SelectionListener>();
|
||||
private List<T> elements = new ArrayList<T>();
|
||||
|
||||
@@ -32,10 +34,20 @@ public class GuiEntryList<T> extends GuiTextField {
|
||||
this.height = elementHeight * visibleElements - 1;
|
||||
}
|
||||
|
||||
public void setEmptyMessage(String emptyMessage) {
|
||||
this.emptyMessage = emptyMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTextBox() {
|
||||
try {
|
||||
super.drawTextBox();
|
||||
|
||||
if(elements.size() == 0 && emptyMessage != null) {
|
||||
drawCenteredString(mc.fontRendererObj, emptyMessage,
|
||||
xPosition+(width/2), yPosition+(height/2)-(mc.fontRendererObj.FONT_HEIGHT/2), Color.RED.getRGB());
|
||||
}
|
||||
|
||||
//drawing the entries
|
||||
for(int i = 0; i - upperIndex < visibleElements; i++) {
|
||||
if(i < upperIndex) continue;
|
||||
@@ -125,6 +137,14 @@ public class GuiEntryList<T> extends GuiTextField {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeElement(int index) {
|
||||
elements.remove(index);
|
||||
if(selectionIndex >= elements.size()) {
|
||||
selectionIndex = elements.size()-1;
|
||||
}
|
||||
fireSelectionChangeEvent();
|
||||
}
|
||||
|
||||
public int getSelectionIndex() {
|
||||
return selectionIndex;
|
||||
}
|
||||
@@ -135,6 +155,19 @@ public class GuiEntryList<T> extends GuiTextField {
|
||||
fireSelectionChangeEvent();
|
||||
}
|
||||
|
||||
public List<T> getCopyOfElements() {
|
||||
return new ArrayList<T>(elements);
|
||||
}
|
||||
|
||||
public void replaceElement(int index, T replace) {
|
||||
elements.set(index, replace);
|
||||
fireSelectionChangeEvent();
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
return elements.size();
|
||||
}
|
||||
|
||||
public void addSelectionListener(SelectionListener listener) {
|
||||
this.selectionListeners.add(listener);
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ public class GuiConnectPart extends GuiStudioPart {
|
||||
|
||||
@Override
|
||||
public void applyFilters(File replayFile, File outputFile) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package eu.crushedpixel.replaymod.holders;
|
||||
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KeyframeSet {
|
||||
private String name;
|
||||
private PositionKeyframe[] positionKeyframes;
|
||||
private TimeKeyframe[] timeKeyframes;
|
||||
|
||||
public KeyframeSet(String name, Keyframe[] keyframes) {
|
||||
this.name = name;
|
||||
setKeyframes(keyframes);
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setKeyframes(Keyframe[] keyframes) {
|
||||
List<PositionKeyframe> posKFList = new ArrayList<PositionKeyframe>();
|
||||
List<TimeKeyframe> timeKFList = new ArrayList<TimeKeyframe>();
|
||||
|
||||
for(Keyframe kf : keyframes) {
|
||||
if(kf instanceof PositionKeyframe)
|
||||
posKFList.add((PositionKeyframe)kf);
|
||||
else if(kf instanceof TimeKeyframe)
|
||||
timeKFList.add((TimeKeyframe) kf);
|
||||
}
|
||||
|
||||
positionKeyframes = posKFList.toArray(new PositionKeyframe[posKFList.size()]);
|
||||
timeKeyframes = timeKFList.toArray(new TimeKeyframe[timeKFList.size()]);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Keyframe[] getKeyframes() {
|
||||
List<Keyframe> kfList = new ArrayList<Keyframe>();
|
||||
for(Keyframe kf : positionKeyframes) kfList.add(kf);
|
||||
for(Keyframe kf : timeKeyframes) kfList.add(kf);
|
||||
return kfList.toArray(new Keyframe[kfList.size()]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o2) {
|
||||
if(o2 == null) return false;
|
||||
if(!(o2 instanceof KeyframeSet)) return false;
|
||||
KeyframeSet set2 = (KeyframeSet)o2;
|
||||
return hashCode() == set2.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(getKeyframes())
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package eu.crushedpixel.replaymod.registry;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class FileCopyHandler extends Thread {
|
||||
|
||||
private Queue<Pair<File, File>> filesToMove = new ConcurrentLinkedQueue<Pair<File, File>>();
|
||||
private boolean shutdown = false;
|
||||
|
||||
public FileCopyHandler() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FileCopyHandler.this.shutdown();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void registerModifiedFile(File tempFile, File destination) {
|
||||
filesToMove.add(Pair.of(tempFile, destination));
|
||||
|
||||
for(Pair<File, File> p : new ArrayList<Pair<File, File>>(filesToMove)) {
|
||||
if(p.equals(tempFile)) {
|
||||
filesToMove.remove(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(!shutdown || !filesToMove.isEmpty()) {
|
||||
Pair<File, File> mv = filesToMove.poll();
|
||||
if(mv != null) {
|
||||
try {
|
||||
Files.move(mv.getLeft().toPath(), mv.getRight().toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch(IOException e) {
|
||||
filesToMove.add(mv);
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch(Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public class KeybindRegistry {
|
||||
public static final String KEY_SPECTATE = "replaymod.input.spectate";
|
||||
public static final String KEY_CLEAR_KEYFRAMES = "replaymod.input.clearkeyframes";
|
||||
public static final String KEY_SYNC_TIMELINE = "replaymod.input.synctimeline";
|
||||
public static final String KEY_KEYFRAME_PRESETS = "replaymod.input.keyframerepository";
|
||||
private static Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
public static void initialize() {
|
||||
@@ -25,6 +26,7 @@ public class KeybindRegistry {
|
||||
bindings.add(new KeyBinding(KEY_SPECTATE, Keyboard.KEY_B, "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_KEYFRAME_PRESETS, Keyboard.KEY_X, "replaymod.title"));
|
||||
|
||||
mc.gameSettings.keyBindings = bindings.toArray(new KeyBinding[bindings.size()]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package eu.crushedpixel.replaymod.registry;
|
||||
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class ReplayFileAppender extends Thread {
|
||||
|
||||
private Queue<Pair<Pair<File, String>, File>> filesToMove = new ConcurrentLinkedQueue<Pair<Pair<File, String>, File>>();
|
||||
private boolean shutdown = false;
|
||||
|
||||
public ReplayFileAppender() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ReplayFileAppender.this.shutdown();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void registerModifiedFile(File toAdd, String name, File replayFile) {
|
||||
for(Pair<Pair<File, String>, File> p : new ArrayList<Pair<Pair<File, String>, File>>(filesToMove)) {
|
||||
if(p.getLeft().getRight().equals(name) && p.getRight().equals(replayFile)) {
|
||||
filesToMove.remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
filesToMove.add(Pair.of(Pair.of(toAdd, name), replayFile));
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(!shutdown || !filesToMove.isEmpty()) {
|
||||
Pair<Pair<File, String>, File> mv = filesToMove.poll();
|
||||
if(mv != null) {
|
||||
if(mv.getRight().canWrite()) {
|
||||
try {
|
||||
ReplayFileIO.addFileToZip(mv.getRight(), mv.getLeft().getLeft(), mv.getLeft().getRight());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
filesToMove.add(mv);
|
||||
}
|
||||
} else {
|
||||
filesToMove.add(mv);
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch(Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.holders.*;
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
@@ -13,10 +14,7 @@ import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.play.INetHandlerPlayClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public class ReplayHandler {
|
||||
|
||||
@@ -34,6 +32,32 @@ public class ReplayHandler {
|
||||
private static Entity currentEntity = null;
|
||||
private static Position lastPosition = null;
|
||||
|
||||
private static KeyframeSet[] keyframeRepository = new KeyframeSet[]{};
|
||||
|
||||
public static KeyframeSet[] getKeyframeRepository() {
|
||||
return keyframeRepository;
|
||||
}
|
||||
|
||||
public static void setKeyframeRepository(KeyframeSet[] repo, boolean write) {
|
||||
keyframeRepository = repo;
|
||||
if(write) {
|
||||
try {
|
||||
File tempFile = File.createTempFile("paths", "json");
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
ReplayFileIO.writeKeyframeRegistryToFile(repo, tempFile);
|
||||
|
||||
ReplayMod.replayFileAppender.registerModifiedFile(tempFile, "paths", ReplayMod.replaySender.getReplayFile());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void useKeyframePresetFromRepository(int index) {
|
||||
keyframes = Arrays.asList(keyframeRepository[index].getKeyframes());
|
||||
}
|
||||
|
||||
public static void spectateEntity(Entity e) {
|
||||
currentEntity = e;
|
||||
mc.setRenderViewEntity(currentEntity);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.gson.Gson;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.entities.CameraEntity;
|
||||
import eu.crushedpixel.replaymod.events.RecordingHandler;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||
import eu.crushedpixel.replaymod.holders.Position;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
@@ -15,7 +16,6 @@ import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.client.resources.ResourcePackRepository;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.EnumConnectionState;
|
||||
@@ -76,14 +76,9 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
joinPacketDimension, joinPacketDifficulty, joinPacketMaxPlayers;
|
||||
private Field effectPacketEntityId;
|
||||
private Field metadataPacketEntityId, metadataPacketList;
|
||||
private Field animationPacketEntityId;
|
||||
private Field entityDataWatcher;
|
||||
private Field chatPacketPosition;
|
||||
private Minecraft mc = Minecraft.getMinecraft();
|
||||
private long now = System.currentTimeMillis();
|
||||
private int replayLength = 0;
|
||||
private int actualID = -1;
|
||||
private EffectRenderer old = mc.effectRenderer;
|
||||
private ZipArchiveEntry replayEntry;
|
||||
private ArrayList<Class> badPackets = new ArrayList<Class>() {
|
||||
{
|
||||
@@ -187,7 +182,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
} catch(EOFException eof) {
|
||||
System.out.println("End of File encountered!");
|
||||
dis = new DataInputStream(archive.getInputStream(replayEntry));
|
||||
setReplaySpeed(0);
|
||||
} catch(IOException e) {
|
||||
@@ -226,16 +220,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
metadataPacketList = S1CPacketEntityMetadata.class.getDeclaredField(MCPNames.field("field_149378_b"));
|
||||
metadataPacketList.setAccessible(true);
|
||||
|
||||
animationPacketEntityId = S0BPacketAnimation.class.getDeclaredField(MCPNames.field("field_148981_a"));
|
||||
animationPacketEntityId.setAccessible(true);
|
||||
|
||||
entityDataWatcher = Entity.class.getDeclaredField(MCPNames.field("field_70180_af"));
|
||||
entityDataWatcher.setAccessible(true);
|
||||
|
||||
chatPacketPosition = S02PacketChat.class.getDeclaredField(MCPNames.field("field_179842_b"));
|
||||
chatPacketPosition.setAccessible(true);
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -253,10 +237,24 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
|
||||
String json = br.readLine();
|
||||
|
||||
ReplayMetaData metaData = new Gson().fromJson(json, ReplayMetaData.class);
|
||||
Gson gson = new Gson();
|
||||
ReplayMetaData metaData = gson.fromJson(json, ReplayMetaData.class);
|
||||
|
||||
this.replayLength = metaData.getDuration();
|
||||
|
||||
ZipArchiveEntry paths = archive.getEntry("paths");
|
||||
if(paths != null) {
|
||||
InputStream is2 = archive.getInputStream(paths);
|
||||
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
|
||||
|
||||
String json2 = br2.readLine();
|
||||
KeyframeSet[] repo = gson.fromJson(json2, KeyframeSet[].class);
|
||||
|
||||
ReplayHandler.setKeyframeRepository(repo, false);
|
||||
} else {
|
||||
ReplayHandler.setKeyframeRepository(new KeyframeSet[]{}, false);
|
||||
}
|
||||
|
||||
sender.start();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -364,13 +362,6 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
if(p instanceof S02PacketChat) {
|
||||
byte pos = (Byte) chatPacketPosition.get(p);
|
||||
if(pos == 1) { //Ignores command block output sent
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(badPackets.contains(p.getClass())) return;
|
||||
|
||||
/*
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.crushedpixel.replaymod.utils;
|
||||
import akka.japi.Pair;
|
||||
import com.google.gson.Gson;
|
||||
import eu.crushedpixel.replaymod.ReplayMod;
|
||||
import eu.crushedpixel.replaymod.holders.KeyframeSet;
|
||||
import eu.crushedpixel.replaymod.holders.PacketData;
|
||||
import eu.crushedpixel.replaymod.recording.ConnectionEventHandler;
|
||||
import eu.crushedpixel.replaymod.recording.PacketSerializer;
|
||||
@@ -20,6 +21,7 @@ import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -305,7 +307,21 @@ public class ReplayFileIO {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void addThumbToZip(File zipFile, File thumb) throws IOException {
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public static void writeKeyframeRegistryToFile(KeyframeSet[] keyframeRegistry, File file) throws IOException {
|
||||
file.mkdirs();
|
||||
file.createNewFile();
|
||||
|
||||
String json = gson.toJson(keyframeRegistry);
|
||||
Files.write(file.toPath(), json.getBytes());
|
||||
}
|
||||
|
||||
public static KeyframeSet[] getKeyframeRegistryFromFile(File file) throws Exception {
|
||||
return gson.fromJson(Files.readAllLines(file.toPath()).get(0), KeyframeSet[].class);
|
||||
}
|
||||
|
||||
public static void addFileToZip(File zipFile, File toAdd, String fileName) throws IOException {
|
||||
// get a temp file
|
||||
File tempFile = File.createTempFile(zipFile.getName(), null, zipFile.getParentFile());
|
||||
// delete it, otherwise you cannot rename your existing zip to it.
|
||||
@@ -318,8 +334,8 @@ public class ReplayFileIO {
|
||||
ZipEntry entry = zin.getNextEntry();
|
||||
while(entry != null) {
|
||||
String name = entry.getName();
|
||||
boolean isThumb = name.contains("thumb");
|
||||
if(!isThumb) {
|
||||
boolean isFile = name.equalsIgnoreCase(fileName);
|
||||
if(!isFile) {
|
||||
// Add ZIP entry to output stream.
|
||||
out.putNextEntry(new ZipEntry(name));
|
||||
// Transfer bytes from the ZIP file to the output file
|
||||
@@ -334,13 +350,13 @@ public class ReplayFileIO {
|
||||
zin.close();
|
||||
// Compress the files
|
||||
|
||||
InputStream in = new FileInputStream(thumb);
|
||||
InputStream in = new FileInputStream(toAdd);
|
||||
// Add ZIP entry to output stream.
|
||||
out.putNextEntry(new ZipEntry("thumb"));
|
||||
out.putNextEntry(new ZipEntry(fileName));
|
||||
// Transfer bytes from the file to the ZIP file
|
||||
int len;
|
||||
|
||||
out.write(uniqueBytes);
|
||||
int len;
|
||||
if(fileName.equals("thumb")) out.write(uniqueBytes);
|
||||
|
||||
while((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
@@ -352,6 +368,9 @@ public class ReplayFileIO {
|
||||
// Complete the ZIP file
|
||||
out.close();
|
||||
|
||||
ReplayMod.fileCopyHandler.registerModifiedFile(tempFile, zipFile);
|
||||
zipFile.delete();
|
||||
tempFile.renameTo(zipFile);
|
||||
|
||||
toAdd.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import eu.crushedpixel.replaymod.events.TickAndRenderListener;
|
||||
import eu.crushedpixel.replaymod.gui.GuiReplaySaving;
|
||||
import eu.crushedpixel.replaymod.replay.ReplayHandler;
|
||||
import eu.crushedpixel.replaymod.utils.ImageUtils;
|
||||
import eu.crushedpixel.replaymod.utils.ReplayFileIO;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
@@ -84,9 +83,9 @@ public class ReplayScreenshot {
|
||||
|
||||
ImageIO.write(img, "jpg", tempImage);
|
||||
|
||||
ReplayFileIO.addThumbToZip(replayFile, tempImage);
|
||||
ReplayMod.replayFileAppender.registerModifiedFile(tempImage, "thumb", replayFile);
|
||||
|
||||
tempImage.delete();
|
||||
tempImage.deleteOnExit();
|
||||
|
||||
ReplayMod.chatMessageHandler.addLocalizedChatMessage("replaymod.chat.savedthumb", ChatMessageType.INFORMATION);
|
||||
} catch(Exception e) {
|
||||
|
||||
@@ -144,4 +144,18 @@ replaymod.input.lighting=Toggle Lighting
|
||||
replaymod.input.thumbnail=Capture Thumbnail
|
||||
replaymod.input.spectate=Spectate Players
|
||||
replaymod.input.clearkeyframes=Clear Keyframes
|
||||
replaymod.input.synctimeline=Synchronize Timeline
|
||||
replaymod.input.synctimeline=Synchronize Timeline
|
||||
replaymod.input.keyframerepository=Open Keyframe Presets
|
||||
|
||||
#Keyframe Presets GUI
|
||||
replaymod.gui.keyframerepository.title=Keyframe Repository
|
||||
replaymod.gui.keyframerepository.presets=Keyframe Presets
|
||||
replaymod.gui.keyframerepository.positionkeyframes=Position Keyframes
|
||||
replaymod.gui.keyframerepository.timekeyframes=Time Keyframes
|
||||
|
||||
replaymod.gui.keyframerepository.savecurrent=Save current Path
|
||||
|
||||
replaymod.gui.keyframerepository.noentries=No Presets available
|
||||
|
||||
replaymod.gui.keyframerepository.preset.defaultname=New Keyframe Preset
|
||||
replaymod.gui.keyframerepository.duplicate=This Preset already exists
|
||||
Reference in New Issue
Block a user