diff --git a/jGui b/jGui index e9387ad0..7713f298 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit e9387ad0035ff2fb2f6eac3ddd9c469e3deccec8 +Subproject commit 7713f2985a7c4f536ce1e677faae2e317edaf981 diff --git a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java index 072e1f4c..377e9c14 100644 --- a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java +++ b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java @@ -14,6 +14,7 @@ import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.stream.IteratorStream; import com.replaymod.replaystudio.studio.ReplayStudio; +import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import com.replaymod.replaystudio.util.Utils; import org.apache.commons.io.FilenameUtils; @@ -106,9 +107,13 @@ public class MarkerProcessor { return suffixes; } - public static void apply(Path path, Consumer progress) throws IOException { + public static List> apply(Path path, Consumer progress) throws IOException { if (!hasWork(path)) { - return; + ReplayMetaData metaData; + try (ZipReplayFile inputReplayFile = new ZipReplayFile(new ReplayStudio(), path.toFile())) { + metaData = inputReplayFile.getMetaData(); + } + return Collections.singletonList(new Pair<>(path, metaData)); } String replayName = FilenameUtils.getBaseName(path.getFileName().toString()); @@ -119,6 +124,8 @@ public class MarkerProcessor { SquashFilter squashFilter = new SquashFilter(); squashFilter.init(studio, null); + List> outputPaths = new ArrayList<>(); + Path inputPath = path.resolveSibling("raw").resolve(path.getFileName()); Files.createDirectories(inputPath.getParent()); Files.move(path, inputPath); @@ -139,7 +146,8 @@ public class MarkerProcessor { Marker nextMarker = markerIterator.next(); while (nextPacket != null && outputFileSuffixes.hasNext()) { - File outputFile = path.resolveSibling(replayName + outputFileSuffixes.next() + ".mcpr").toFile(); + Path outputPath = path.resolveSibling(replayName + outputFileSuffixes.next() + ".mcpr"); + File outputFile = outputPath.toFile(); try (ZipReplayFile outputReplayFile = new ZipReplayFile(studio, null, outputFile)) { long duration = 0; Set outputMarkers = new HashSet<>(); @@ -237,6 +245,8 @@ public class MarkerProcessor { } outputReplayFile.save(); + + outputPaths.add(new Pair(outputPath, metaData)); } } @@ -245,5 +255,7 @@ public class MarkerProcessor { cutFilter.release(); } } + + return outputPaths; } } diff --git a/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java b/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java new file mode 100644 index 00000000..a704a388 --- /dev/null +++ b/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java @@ -0,0 +1,161 @@ +package com.replaymod.recording.gui; + +import com.replaymod.core.ReplayMod; +import com.replaymod.core.utils.Utils; +import com.replaymod.replay.gui.screen.GuiReplayViewer; +import com.replaymod.replaystudio.replay.ReplayMetaData; +import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; +import de.johni0702.minecraft.gui.container.GuiContainer; +import de.johni0702.minecraft.gui.container.GuiPanel; +import de.johni0702.minecraft.gui.container.VanillaGuiScreen; +import de.johni0702.minecraft.gui.element.GuiButton; +import de.johni0702.minecraft.gui.element.GuiLabel; +import de.johni0702.minecraft.gui.element.GuiTextField; +import de.johni0702.minecraft.gui.element.GuiTooltip; +import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; +import de.johni0702.minecraft.gui.function.Focusable; +import de.johni0702.minecraft.gui.layout.CustomLayout; +import de.johni0702.minecraft.gui.layout.HorizontalLayout; +import de.johni0702.minecraft.gui.layout.VerticalLayout; +import de.johni0702.minecraft.gui.utils.Colors; +import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; +import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.crash.CrashReport; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static com.replaymod.core.versions.MCVer.getMinecraft; +import static de.johni0702.minecraft.gui.utils.Utils.link; + +public class GuiSavingReplay { + + private static final MinecraftClient mc = getMinecraft(); + private static final Logger logger = LogManager.getLogger(); + + private final GuiLabel label = new GuiLabel() + .setI18nText("replaymod.gui.replaysaving.title") + .setColor(Colors.BLACK); + + private final GuiProgressBar progressBar = new GuiProgressBar() + .setHeight(14); + + private final GuiPanel panel = new GuiPanel() + .setLayout(new VerticalLayout().setSpacing(2)) + .addElements(new VerticalLayout.Data(0.5), label, progressBar); + + private final ReplayMod core; + private final List apply = new ArrayList<>(); + + public GuiSavingReplay(ReplayMod core) { + this.core = core; + } + + public void open() { + core.getBackgroundProcesses().addProcess(panel); + } + + public void close() { + core.getBackgroundProcesses().removeProcess(panel); + } + + public GuiProgressBar getProgressBar() { + return progressBar; + } + + public void presentRenameDialog(List> outputPaths) { + panel.removeElement(progressBar); + + link(outputPaths.stream().map(it -> addOutput(it.getKey(), it.getValue())).toArray(Focusable[]::new)); + + GuiButton applyButton = new GuiButton() + .setSize(150, 20) + .setI18nLabel("replaymod.gui.done") + .onClick(() -> { + apply.forEach(Runnable::run); + close(); + }); + + panel.addElements(new VerticalLayout.Data(0.5), applyButton); + } + + private GuiTextField addOutput(Path path, ReplayMetaData metaData) { + String originalName = Utils.fileNameToReplayName(path.getFileName().toString()); + GuiTextField textField = new GuiTextField() + .setSize(130, 20) + .setText(originalName) + .setI18nHint("replaymod.gui.delete") + .setTextColorDisabled(Colors.RED) + .setTooltip(createTooltip(path, metaData)); + GuiButton clearButton = new GuiButton() + .setSize(20, 20) + .setLabel("X") + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.delete")) + .onClick(() -> textField.setText("")); + GuiPanel row = new GuiPanel() + .setLayout(new HorizontalLayout()) + .addElements(null, textField, clearButton); + panel.addElements(new VerticalLayout.Data(0.5), row); + + apply.add(() -> { + String newName = textField.getText(); + if (newName.equals(originalName)) { + return; + } + applyOutput(path, newName); + }); + + return textField; + } + + private GuiPanel createTooltip(Path path, ReplayMetaData metaData) { + GuiTooltip tooltip = new GuiTooltip(); + GuiReplayViewer.GuiReplayEntry entry = new GuiReplayViewer.GuiReplayEntry(path.toFile(), metaData, null); + return new GuiPanel().setLayout(new CustomLayout() { + @Override + protected void layout(GuiPanel container, int width, int height) { + pos(entry, 4, 4); + size(entry, width - 8, height - 8); + size(tooltip, width, height); + } + + @Override + public ReadableDimension calcMinSize(GuiContainer container) { + ReadableDimension size = entry.calcMinSize(); + return new Dimension(size.getWidth() + 8, size.getHeight() + 8); + } + }).addElements(null, tooltip, entry); + } + + private void applyOutput(Path path, String newName) { + if (newName.isEmpty()) { + try { + Files.delete(path); + } catch (IOException e) { + logger.error("Deleting replay file:", e); + CrashReport crashReport = CrashReport.create(e, "Deleting replay file"); + core.runLater(() -> Utils.error(logger, VanillaGuiScreen.setup(mc.currentScreen), crashReport, () -> {})); + } + return; + } + + Path newPath = path.resolveSibling(Utils.replayNameToFileName(newName)); + if (Files.exists(newPath)) { + return; + } + + try { + Files.move(path, newPath); + } catch (IOException e) { + logger.error("Renaming replay file:", e); + CrashReport crashReport = CrashReport.create(e, "Renaming replay file"); + core.runLater(() -> Utils.error(logger, VanillaGuiScreen.setup(mc.currentScreen), crashReport, () -> {})); + } + } +} diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index e28289b2..dc46c768 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -11,6 +11,7 @@ import com.replaymod.core.versions.MCVer; import com.replaymod.editor.gui.MarkerProcessor; import com.replaymod.recording.ReplayModRecording; import com.replaymod.recording.Setting; +import com.replaymod.recording.gui.GuiSavingReplay; import com.replaymod.recording.handler.ConnectionEventHandler; import com.replaymod.recording.mixin.SPacketSpawnMobAccessor; import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor; @@ -19,12 +20,8 @@ import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.io.ReplayOutputStream; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; -import de.johni0702.minecraft.gui.container.GuiPanel; +import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import de.johni0702.minecraft.gui.container.VanillaGuiScreen; -import de.johni0702.minecraft.gui.element.GuiLabel; -import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; -import de.johni0702.minecraft.gui.layout.VerticalLayout; -import de.johni0702.minecraft.gui.utils.Colors; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; @@ -70,7 +67,9 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -248,13 +247,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } }); - GuiLabel label = new GuiLabel().setI18nText("replaymod.gui.replaysaving.title").setColor(Colors.BLACK); - GuiProgressBar progressBar = new GuiProgressBar().setHeight(14); - GuiPanel savingProcess = new GuiPanel() - .setLayout(new VerticalLayout()) - .addElements(new VerticalLayout.Data(0.5), label, progressBar); + GuiSavingReplay guiSavingReplay = new GuiSavingReplay(core); new Thread(() -> { - core.runLater(() -> core.getBackgroundProcesses().addProcess(savingProcess)); + core.runLater(guiSavingReplay::open); saveService.shutdown(); try { @@ -268,22 +263,26 @@ public class PacketListener extends ChannelInboundHandlerAdapter { logger.error("Failed to close packet output stream:", e); } + List> outputPaths; synchronized (replayFile) { try { replayFile.save(); replayFile.close(); if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS) && !ReplayMod.isMinimalMode()) { - MarkerProcessor.apply(outputPath, progressBar::setProgress); + outputPaths = MarkerProcessor.apply(outputPath, guiSavingReplay.getProgressBar()::setProgress); + } else { + outputPaths = Collections.singletonList(new Pair<>(outputPath, metaData)); } } catch (Exception e) { logger.error("Saving replay file:", e); CrashReport crashReport = CrashReport.create(e, "Saving replay file"); - core.runLater(() -> Utils.error(logger, VanillaGuiScreen.setup(mc.currentScreen), crashReport, () -> {})); + core.runLater(() -> Utils.error(logger, VanillaGuiScreen.setup(mc.currentScreen), crashReport, guiSavingReplay::close)); + return; } } - core.runLater(() -> core.getBackgroundProcesses().removeProcess(savingProcess)); + core.runLater(() -> guiSavingReplay.presentRenameDialog(outputPaths)); }).start(); }