diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/GuiConstants.java b/src/main/java/eu/crushedpixel/replaymod/gui/GuiConstants.java index c664f047..3bae2a01 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/GuiConstants.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/GuiConstants.java @@ -101,4 +101,6 @@ public class GuiConstants { public static final int REPLAY_SETTINGS_RESOURCEPACK_ID = 9010; public static final int REPLAY_SETTINGS_INDICATOR_ID = 9012; public static final int REPLAY_SETTINGS_PATHPREVIEW_ID = 9013; + + public static final int REPLAY_EDITING_CANCEL_BUTTON = 1234; } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiProgressBar.java b/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiProgressBar.java index bc75873b..2fcac190 100644 --- a/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiProgressBar.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/elements/GuiProgressBar.java @@ -17,6 +17,10 @@ public class GuiProgressBar extends Gui { private String progressString = null; + public GuiProgressBar() { + this(0, 0, 0, 0); + } + public GuiProgressBar(int xPosition, int yPosition, int width, int height) { this(xPosition, yPosition, width, height, 0); } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/elements/listeners/ProgressUpdateListener.java b/src/main/java/eu/crushedpixel/replaymod/gui/elements/listeners/ProgressUpdateListener.java new file mode 100644 index 00000000..2a23c278 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/gui/elements/listeners/ProgressUpdateListener.java @@ -0,0 +1,9 @@ +package eu.crushedpixel.replaymod.gui.elements.listeners; + +public interface ProgressUpdateListener { + + void onProgressChanged(float progress); + + void onProgressChanged(float progress, String progressString); + +} diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiConnectPart.java b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiConnectPart.java index 958d4087..90e82bec 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiConnectPart.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiConnectPart.java @@ -10,6 +10,7 @@ import eu.crushedpixel.replaymod.utils.ReplayFileIO; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.resources.I18n; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import java.awt.*; @@ -34,31 +35,50 @@ public class GuiConnectPart extends GuiStudioPart { private List replayFiles; private List filesToConcat; + private Thread filterThread; + private File outputFile; + public GuiConnectPart(int yPos) { super(yPos); this.mc = Minecraft.getMinecraft(); fontRendererObj = mc.fontRendererObj; } - @Override public void applyFilters(File replayFile, File outputFile) { - try { - List inputFiles = new ArrayList(); - OUTER: - for (String fileName : filesToConcat) { - for (File file : replayFiles) { - if (fileName.equals(FilenameUtils.getBaseName(file.getAbsolutePath()))) { - inputFiles.add(file); - continue OUTER; + this.outputFile = outputFile; + filterThread = new Thread(new Runnable() { + @Override + public void run() { + try { + List inputFiles = new ArrayList(); + OUTER: + for (String fileName : filesToConcat) { + for (File file : replayFiles) { + if (fileName.equals(FilenameUtils.getBaseName(file.getAbsolutePath()))) { + inputFiles.add(file); + continue OUTER; + } + } + throw new RuntimeException(fileName); } + StudioImplementation.connectReplayFiles(inputFiles, outputFile, GuiConnectPart.this); + } catch (Exception e) { + e.printStackTrace(); } - throw new RuntimeException(fileName); } - StudioImplementation.connectReplayFiles(inputFiles, outputFile); - } catch (Exception e) { - e.printStackTrace(); - } + }, "replay-editor-connect"); + + filterThread.start(); + + mc.displayGuiScreen(new GuiReplayEditingProcess(this)); + } + + @Override + @SuppressWarnings("deprecation") + public void cancelFilters() { + filterThread.stop(); + FileUtils.deleteQuietly(outputFile); } @Override diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiReplayEditingProcess.java b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiReplayEditingProcess.java new file mode 100644 index 00000000..ef202658 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiReplayEditingProcess.java @@ -0,0 +1,91 @@ +package eu.crushedpixel.replaymod.gui.replayeditor; + +import eu.crushedpixel.replaymod.ReplayMod; +import eu.crushedpixel.replaymod.gui.GuiConstants; +import eu.crushedpixel.replaymod.gui.elements.GuiProgressBar; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +import java.awt.*; +import java.io.IOException; + +public class GuiReplayEditingProcess extends GuiScreen { + + private String title; + private String pleaseWait; + private String cancelCallback; + + private boolean finished = false; + + private boolean callback = false; + + private boolean initialized = false; + + private GuiProgressBar progressBar; + private GuiButton cancelButton; + + private GuiStudioPart studioPart; + + public GuiReplayEditingProcess(GuiStudioPart studioPart) { + this.studioPart = studioPart; + } + + @Override + public void initGui() { + if(!initialized) { + title = I18n.format("replaymod.gui.editor.progress.title"); + pleaseWait = I18n.format("replaymod.gui.editor.progress.pleasewait"); + cancelCallback = I18n.format("replaymod.gui.rendering.cancel.callback"); + + progressBar = new GuiProgressBar(); + cancelButton = new GuiButton(GuiConstants.REPLAY_EDITING_CANCEL_BUTTON, 0, 0, I18n.format("gui.cancel")); + } + + progressBar.setBounds(10, this.height-30-20, this.width-20, 20); + + cancelButton.xPosition = this.width - 5 - 150; + cancelButton.width = 150; + cancelButton.yPosition = this.height - 5 - 20; + + buttonList.add(cancelButton); + + initialized = true; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + this.drawDefaultBackground(); + super.drawScreen(mouseX, mouseY, partialTicks); + this.drawCenteredString(mc.fontRendererObj, title, this.width / 2, 20, Color.WHITE.getRGB()); + this.drawCenteredString(mc.fontRendererObj, pleaseWait, this.width / 2, 40, Color.WHITE.getRGB()); + + progressBar.setProgress(studioPart.getProgress()); + progressBar.setProgressString(studioPart.getFilterProgressString()); + progressBar.drawProgressBar(); + + if(studioPart.getProgress() >= 1f && !finished) { + finished = true; + cancelButton.displayString = I18n.format("gui.done"); + ReplayMod.soundHandler.playRenderSuccessSound(); + } + } + + @Override + protected void actionPerformed(GuiButton button) throws IOException { + if(!button.enabled) return; + if(button.id == GuiConstants.REPLAY_EDITING_CANCEL_BUTTON) { + if(finished) { + mc.displayGuiScreen(null); + } else { + if(!callback) { + callback = true; + button.displayString = cancelCallback; + } else { + studioPart.cancelFilters(); + mc.displayGuiScreen(null); + } + } + } + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiStudioPart.java b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiStudioPart.java index 997c5fae..31f24e2e 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiStudioPart.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiStudioPart.java @@ -1,11 +1,12 @@ package eu.crushedpixel.replaymod.gui.replayeditor; +import eu.crushedpixel.replaymod.gui.elements.listeners.ProgressUpdateListener; import net.minecraft.client.gui.GuiScreen; import java.io.File; import java.io.IOException; -public abstract class GuiStudioPart extends GuiScreen { +public abstract class GuiStudioPart extends GuiScreen implements ProgressUpdateListener { protected int yPos = 0; @@ -15,6 +16,8 @@ public abstract class GuiStudioPart extends GuiScreen { public abstract void applyFilters(File replayFile, File outputFile); + public abstract void cancelFilters(); + public abstract String getDescription(); public abstract String getTitle(); @@ -26,4 +29,27 @@ public abstract class GuiStudioPart extends GuiScreen { public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { super.mouseClicked(mouseX, mouseY, mouseButton); } + + private float progress; + private String progressString; + + public float getProgress() { + return progress; + } + + public String getFilterProgressString() { + return progressString; + } + + @Override + public void onProgressChanged(float progress, String progressString) { + this.progress = progress; + this.progressString = progressString; + } + + @Override + public void onProgressChanged(float progress) { + this.progress = progress; + } + } diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiTrimPart.java b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiTrimPart.java index 9f563d34..721a00cb 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiTrimPart.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/replayeditor/GuiTrimPart.java @@ -5,6 +5,7 @@ import eu.crushedpixel.replaymod.studio.StudioImplementation; import eu.crushedpixel.replaymod.utils.TimestampUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.I18n; +import org.apache.commons.io.FileUtils; import org.lwjgl.input.Keyboard; import java.awt.*; @@ -24,6 +25,9 @@ public class GuiTrimPart extends GuiStudioPart { private List inputOrder = new ArrayList(); + private Thread filterThread; + private File outputFile; + public GuiTrimPart(int yPos) { super(yPos); fontRendererObj = mc.fontRendererObj; @@ -31,11 +35,28 @@ public class GuiTrimPart extends GuiStudioPart { @Override public void applyFilters(File replayFile, File outputFile) { - try { - StudioImplementation.trimReplay(replayFile, getStartTimestamp(), getEndTimestamp(), outputFile); - } catch(Exception e) { - e.printStackTrace(); - } + this.outputFile = outputFile; + filterThread = new Thread(new Runnable() { + @Override + public void run() { + try { + StudioImplementation.trimReplay(replayFile, getStartTimestamp(), getEndTimestamp(), outputFile, GuiTrimPart.this); + } catch(Exception e) { + e.printStackTrace(); + } + } + }, "replay-editor-trim"); + + filterThread.start(); + + mc.displayGuiScreen(new GuiReplayEditingProcess(this)); + } + + @Override + @SuppressWarnings("deprecation") + public void cancelFilters() { + filterThread.stop(); + FileUtils.deleteQuietly(outputFile); } private int valueOf(String text) { diff --git a/src/main/java/eu/crushedpixel/replaymod/studio/StudioImplementation.java b/src/main/java/eu/crushedpixel/replaymod/studio/StudioImplementation.java index ff1b8706..dc520cd9 100755 --- a/src/main/java/eu/crushedpixel/replaymod/studio/StudioImplementation.java +++ b/src/main/java/eu/crushedpixel/replaymod/studio/StudioImplementation.java @@ -12,6 +12,8 @@ import de.johni0702.replaystudio.replay.ReplayMetaData; import de.johni0702.replaystudio.replay.ZipReplayFile; import de.johni0702.replaystudio.stream.PacketStream; import de.johni0702.replaystudio.studio.ReplayStudio; +import eu.crushedpixel.replaymod.gui.elements.listeners.ProgressUpdateListener; +import net.minecraft.client.resources.I18n; import org.apache.commons.lang3.Validate; import java.io.File; @@ -20,7 +22,9 @@ import java.util.List; public class StudioImplementation { - public static void trimReplay(File file, int beginning, int ending, File outputFile) throws IOException { + public static void trimReplay(File file, int beginning, int ending, File outputFile, ProgressUpdateListener updateListener) throws IOException { + updateListener.onProgressChanged(0f, I18n.format("replaymod.gui.editor.progress.status.initializing")); + ReplayStudio studio = new ReplayStudio(); studio.setWrappingEnabled(false); ReplayFile replayFile = new ZipReplayFile(studio, file); @@ -39,24 +43,37 @@ public class StudioImplementation { ReplayOutputStream out = replayFile.writePacketData(); PacketData packetData; + updateListener.onProgressChanged(1/4f, I18n.format("replaymod.gui.editor.progress.status.writing.raw")); + while((packetData = stream.next()) != null) { out.write(packetData); } - for(PacketData data : stream.end()) { + + List endList = stream.end(); + + for(PacketData data : endList) { out.write(data); } out.close(); + updateListener.onProgressChanged(2/4f, I18n.format("replaymod.gui.editor.progress.status.writing.zip")); + ReplayMetaData metaData = replayFile.getMetaData(); ending = Math.min(metaData.getDuration(), ending); metaData.setDuration(ending - beginning); replayFile.writeMetaData(metaData); + updateListener.onProgressChanged(3/4f, I18n.format("replaymod.gui.editor.progress.status.writing.final")); + replayFile.saveTo(outputFile); + + updateListener.onProgressChanged(1f, I18n.format("replaymod.gui.editor.progress.status.finished")); } - public static void connectReplayFiles(List filesToConnect, File outputFile) throws IOException { + public static void connectReplayFiles(List filesToConnect, File outputFile, ProgressUpdateListener updateListener) throws IOException { + updateListener.onProgressChanged(0f, I18n.format("replaymod.gui.editor.progress.status.initializing")); + Validate.notEmpty(filesToConnect); ReplayStudio studio = new ReplayStudio(); studio.setWrappingEnabled(false); @@ -66,6 +83,12 @@ public class StudioImplementation { ConnectMetadataFilter metaDataFilter = new ConnectMetadataFilter(); int startTime = 0; + + updateListener.onProgressChanged(1/4f, I18n.format("replaymod.gui.editor.progress.status.writing.raw")); + + float i = 0; + float size = filesToConnect.size(); + for (File file : filesToConnect) { ReplayFile replayFile = new ZipReplayFile(studio, file); PacketStream stream = studio.createReplayStream(replayFile.getPacketData(), true); @@ -93,11 +116,20 @@ public class StudioImplementation { } startTime += metaData.getDuration(); + + updateListener.onProgressChanged((1/4f)+((1/4f)*(i/size))); } out.close(); + updateListener.onProgressChanged(2/4f, I18n.format("replaymod.gui.editor.progress.status.writing.zip")); + metaDataFilter.writeTo(outputReplayFile); + + updateListener.onProgressChanged(3/4f, I18n.format("replaymod.gui.editor.progress.status.writing.final")); + outputReplayFile.saveTo(outputFile); + + updateListener.onProgressChanged(1f, I18n.format("replaymod.gui.editor.progress.status.finished")); } } diff --git a/src/main/resources/assets/replaymod/lang/en_US.lang b/src/main/resources/assets/replaymod/lang/en_US.lang index 5c27105d..510334fd 100644 --- a/src/main/resources/assets/replaymod/lang/en_US.lang +++ b/src/main/resources/assets/replaymod/lang/en_US.lang @@ -180,6 +180,15 @@ replaymod.gui.editor.trim.description=Removes the beginning and end of a Replay replaymod.gui.editor.connect.description=Connects multiple Replays in the specified order replaymod.gui.editor.modify.description=Provides several filters to modify Replay Files +replaymod.gui.editor.progress.title=Editing Replay File... +replaymod.gui.editor.progress.pleasewait=Please wait while the Replay is being edited. + +replaymod.gui.editor.progress.status.initializing=Initializing +replaymod.gui.editor.progress.status.writing.raw=Rewriting Replay... +replaymod.gui.editor.progress.status.writing.zip=Wrapping Replay File... +replaymod.gui.editor.progress.status.writing.final=Writing File to disk... +replaymod.gui.editor.progress.status.finished=Finished Editing! + #Cancel Replay GUI replaymod.gui.cancelrender.title=Cancel Rendering replaymod.gui.cancelrender.message=Are you sure that you want to cancel the current rendering process?