Add start/stop/pause/resume recording functionality

Using automatically applied, ReplayStudio-based post processing.
This commit is contained in:
Jonas Herzig
2019-03-12 13:25:05 +01:00
parent fe68f76bd1
commit eb6e7b79d0
11 changed files with 513 additions and 37 deletions

2
jGui

Submodule jGui updated: 31fecfa0d6...63aec9fd9a

View File

@@ -2,6 +2,7 @@ package com.replaymod.core;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask; import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.core.gui.GuiBackgroundProcesses;
import com.replaymod.core.gui.GuiReplaySettings; import com.replaymod.core.gui.GuiReplaySettings;
import com.replaymod.core.gui.RestoreReplayGui; import com.replaymod.core.gui.RestoreReplayGui;
import com.replaymod.core.handler.MainMenuHandler; import com.replaymod.core.handler.MainMenuHandler;
@@ -152,6 +153,8 @@ public class ReplayMod implements Module {
private final List<Module> modules = new ArrayList<>(); private final List<Module> modules = new ArrayList<>();
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
public ReplayMod() { public ReplayMod() {
I18n.setI18n(net.minecraft.client.resources.I18n::format); I18n.setI18n(net.minecraft.client.resources.I18n::format);
@@ -285,6 +288,7 @@ public class ReplayMod implements Module {
//#endif //#endif
FML_BUS.register(keyBindingRegistry); FML_BUS.register(keyBindingRegistry);
FORGE_BUS.register(backgroundProcesses);
// 1.7.10 crashes when render distance > 16 // 1.7.10 crashes when render distance > 16
//#if MC>=10800 //#if MC>=10800
@@ -455,4 +459,8 @@ public class ReplayMod implements Module {
mc.ingameGUI.getChatGUI().printChatMessage(text); mc.ingameGUI.getChatGUI().printChatMessage(text);
} }
} }
public GuiBackgroundProcesses getBackgroundProcesses() {
return backgroundProcesses;
}
} }

View File

@@ -0,0 +1,135 @@
package com.replaymod.core.gui;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
import static com.replaymod.core.versions.MCVer.getMinecraft;
public class GuiBackgroundProcesses {
private GuiPanel panel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(10));
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) != getMinecraft().currentScreen) return; // people tend to construct GuiScreens without opening them
VanillaGuiScreen.setup(getGui(event)).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width - 5 - width(panel), 5);
}
}).addElements(null, panel);
}
public void addProcess(GuiElement<?> element) {
panel.addElements(new VerticalLayout.Data(1.0),
new Element(element));
}
public void removeProcess(GuiElement<?> element) {
for (GuiElement child : panel.getChildren()) {
if (((Element) child).inner == element) {
panel.removeElement(child);
return;
}
}
}
private static class Element extends AbstractGuiContainer<Element> {
private GuiElement inner;
Element(GuiElement inner) {
this.inner = inner;
addElements(null, inner);
setLayout(new CustomLayout<Element>() {
@Override
protected void layout(Element container, int width, int height) {
pos(inner, 10, 10);
size(inner, width - 20, height - 20);
}
});
}
@Override
public ReadableDimension getMinSize() {
ReadableDimension minSize = inner.getMinSize();
return new Dimension(minSize.getWidth() + 20, minSize.getHeight() + 20);
}
@Override
public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
final int u0 = 0;
final int v0 = 39;
renderer.bindTexture(TEXTURE);
int w = size.getWidth();
int h = size.getHeight();
// Corners
renderer.drawTexturedRect(0, 0, u0, v0, 5, 5); // Top left
renderer.drawTexturedRect(w - 5, 0, u0 + 12, v0, 5, 5); // Top right
renderer.drawTexturedRect(0, h - 5, u0, v0 + 12, 5, 5); // Bottom left
renderer.drawTexturedRect(w - 5, h - 5, u0 + 12, v0 + 12, 5, 5); // Bottom right
// Top and bottom edge
for (int x = 5; x < w - 5; x += 5) {
int rx = Math.min(5, w - 5 - x);
renderer.drawTexturedRect(x, 0, u0 + 6, v0, rx, 5); // Top
renderer.drawTexturedRect(x, h - 5, u0 + 6, v0 + 12, rx, 5); // Bottom
}
// Left and right edge
for (int y = 5; y < h - 5; y += 5) {
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(0, y, u0, v0 + 6, 5, ry); // Left
renderer.drawTexturedRect(w - 5, y, u0 + 12, v0 + 6, 5, ry); // Right
}
// Center
for (int x = 5; x < w - 5; x += 5) {
for (int y = 5; y < h - 5; y += 5) {
int rx = Math.min(5, w - 5 - x);
int ry = Math.min(5, h - 5 - y);
renderer.drawTexturedRect(x, y, u0 + 6, v0 + 6, rx, ry);
}
}
super.draw(renderer, size, renderInfo);
}
@Override
protected Element getThis() {
return this;
}
}
}

View File

@@ -0,0 +1,173 @@
package com.replaymod.editor.gui;
import com.replaymod.replaystudio.PacketData;
import com.replaymod.replaystudio.Studio;
import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.filter.SquashFilter;
import com.replaymod.replaystudio.filter.StreamFilter;
import com.replaymod.replaystudio.io.ReplayInputStream;
import com.replaymod.replaystudio.io.ReplayOutputStream;
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.util.Utils;
import org.apache.commons.io.FilenameUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class MarkerProcessor {
/**
* Signals the start of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_START_CUT = "_RM_START_CUT";
/**
* Signals the end of a section which is to be cut from the replay.
*/
public static final String MARKER_NAME_END_CUT = "_RM_END_CUT";
/**
* Signals the point at which a replay is to be split into multiple replays.
* If it is found in a section which is to be cut, that section will be cut from both replays.
*/
public static final String MARKER_NAME_SPLIT = "_RM_SPLIT";
private static boolean hasWork(Path path) throws IOException {
try (ZipReplayFile inputReplayFile = new ZipReplayFile(new ReplayStudio(), path.toFile())) {
return inputReplayFile.getMarkers().or(HashSet::new).stream().anyMatch(m -> m.getName().startsWith("_RM_"));
}
}
public static void apply(Path path) throws IOException {
if (!hasWork(path)) {
return;
}
String replayName = FilenameUtils.getBaseName(path.getFileName().toString());
int splitCounter = 0;
Studio studio = new ReplayStudio();
SquashFilter squashFilter = new SquashFilter();
squashFilter.init(studio, null);
Path inputPath = path.resolveSibling("raw").resolve(path.getFileName());
Files.createDirectories(inputPath.getParent());
Files.move(path, inputPath);
try (ZipReplayFile inputReplayFile = new ZipReplayFile(studio, inputPath.toFile())) {
List<Marker> markers = inputReplayFile.getMarkers().or(HashSet::new)
.stream().sorted(Comparator.comparing(Marker::getTime)).collect(Collectors.toList());
Iterator<Marker> markerIterator = markers.iterator();
boolean anySplit = markers.stream().anyMatch(m -> m.getName().equals(MARKER_NAME_SPLIT));
ReplayInputStream replayInputStream = inputReplayFile.getPacketData(studio, true);
int timeOffset = 0;
SquashFilter cutFilter = null;
int startCutOffset = 0;
PacketData nextPacket = replayInputStream.readPacket();
Marker nextMarker = markerIterator.next();
while (nextPacket != null) {
try (ZipReplayFile outputReplayFile = new ZipReplayFile(studio, null,
(anySplit ? path.resolveSibling(replayName + "_" + splitCounter + ".mcpr") : path).toFile())) {
long duration = 0;
Set<Marker> outputMarkers = new HashSet<>();
ReplayMetaData metaData = inputReplayFile.getMetaData();
metaData.setDate(metaData.getDate() + timeOffset);
try (ReplayOutputStream replayOutputStream = outputReplayFile.writePacketData(true)) {
if (cutFilter != null) {
cutFilter = squashFilter;
} else if (splitCounter > 0) {
List<PacketData> packets = new ArrayList<>();
squashFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
timeOffset
);
for (PacketData packet : packets) {
replayOutputStream.write(packet.getTime() - timeOffset, packet.getPacket());
}
}
while (nextPacket != null) {
if (nextMarker != null && nextPacket.getTime() > nextMarker.getTime()) {
if (MARKER_NAME_START_CUT.equals(nextMarker.getName())) {
startCutOffset = nextMarker.getTime();
cutFilter = new SquashFilter();
} else if (MARKER_NAME_END_CUT.equals(nextMarker.getName())) {
timeOffset += nextMarker.getTime() - startCutOffset;
if (cutFilter != null) {
List<PacketData> packets = new ArrayList<>();
cutFilter.onEnd(
new IteratorStream(packets.listIterator(), (StreamFilter) null),
nextMarker.getTime()
);
for (PacketData packet : packets) {
replayOutputStream.write(packet.getTime() - timeOffset, packet.getPacket());
}
cutFilter = null;
}
} else if (MARKER_NAME_SPLIT.equals(nextMarker.getName())) {
splitCounter++;
timeOffset = nextMarker.getTime();
startCutOffset = timeOffset;
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
break;
} else {
nextMarker.setTime(nextMarker.getTime() - timeOffset);
outputMarkers.add(nextMarker);
}
nextMarker = markerIterator.hasNext() ? markerIterator.next() : null;
continue;
}
squashFilter.onPacket(null, nextPacket);
if (cutFilter != null) {
if (cutFilter != squashFilter) {
cutFilter.onPacket(null, nextPacket);
}
} else {
replayOutputStream.write(nextPacket.getTime() - timeOffset, nextPacket.getPacket());
duration = nextPacket.getTime() - timeOffset;
}
nextPacket = replayInputStream.readPacket();
}
}
metaData.setDuration((int) duration);
outputReplayFile.writeMetaData(metaData);
outputReplayFile.writeMarkers(outputMarkers);
outputReplayFile.writeModInfo(inputReplayFile.getModInfo());
// We could filter these but ReplayStudio doesn't yet provide a nice method for determining
// which ones are used.
Map<Integer, String> resourcePackIndex = inputReplayFile.getResourcePackIndex();
if (resourcePackIndex != null) {
outputReplayFile.writeResourcePackIndex(resourcePackIndex);
for (String hash : resourcePackIndex.values()) {
try (InputStream in = inputReplayFile.getResourcePack(hash).get();
OutputStream out = outputReplayFile.writeResourcePack(hash)) {
Utils.copy(in, out);
}
}
}
outputReplayFile.save();
}
}
}
}
}

View File

@@ -60,7 +60,7 @@ public class ReplayModRecording implements Module {
public void run() { public void run() {
PacketListener packetListener = connectionEventHandler.getPacketListener(); PacketListener packetListener = connectionEventHandler.getPacketListener();
if (packetListener != null) { if (packetListener != null) {
packetListener.addMarker(); packetListener.addMarker(null);
core.printInfoToChat("replaymod.chat.addedmarker"); core.printInfoToChat("replaymod.chat.addedmarker");
} }
} }
@@ -92,4 +92,8 @@ public class ReplayModRecording implements Module {
//#endif //#endif
connectionEventHandler.onConnectedToServerEvent(networkManager); connectionEventHandler.onConnectedToServerEvent(networkManager);
} }
public ConnectionEventHandler getConnectionEventHandler() {
return connectionEventHandler;
}
} }

View File

@@ -6,12 +6,14 @@ public final class Setting<T> extends SettingsRegistry.SettingKeys<T> {
public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true); public static final Setting<Boolean> RECORD_SINGLEPLAYER = make("recordSingleplayer", "recordsingleplayer", true);
public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true); public static final Setting<Boolean> RECORD_SERVER = make("recordServer", "recordserver", true);
public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true); public static final Setting<Boolean> INDICATOR = make("indicator", "indicator", true);
public static final Setting<Boolean> AUTO_START_RECORDING = make("autoStartRecording", "autostartrecording", true);
public static final Setting<Boolean> AUTO_POST_PROCESS = make("autoPostProcess", null, true);
private static <T> Setting<T> make(String key, String displayName, T defaultValue) { private static <T> Setting<T> make(String key, String displayName, T defaultValue) {
return new Setting<>(key, displayName, defaultValue); return new Setting<>(key, displayName, defaultValue);
} }
public Setting(String key, String displayString, T defaultValue) { public Setting(String key, String displayString, T defaultValue) {
super("recording", key, "replaymod.gui.settings." + displayString, defaultValue); super("recording", key, displayString == null ? null : "replaymod.gui.settings." + displayString, defaultValue);
} }
} }

View File

@@ -0,0 +1,110 @@
package com.replaymod.recording.gui;
import com.replaymod.core.ReplayMod;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting;
import com.replaymod.recording.packet.PacketListener;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.VanillaGuiScreen;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import net.minecraft.client.gui.GuiIngameMenu;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.common.MinecraftForge;
//#if MC>=10800
//#if MC>=11300
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//#endif
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//#endif
import static com.replaymod.core.versions.MCVer.getGui;
public class GuiRecordingControls {
private ReplayMod core;
private PacketListener packetListener;
private boolean paused;
private boolean stopped;
private GuiPanel panel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4));
private GuiButton buttonPauseResume = new GuiButton(panel).onClick(() -> {
if (paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT);
}
paused = !paused;
updateState();
}).setSize(98, 20);
private GuiButton buttonStartStop = new GuiButton(panel).onClick(() -> {
if (stopped) {
paused = false;
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
int timestamp = (int) packetListener.getCurrentDuration();
if (!paused) {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, timestamp);
}
packetListener.addMarker(MarkerProcessor.MARKER_NAME_SPLIT, timestamp + 1);
}
stopped = !stopped;
updateState();
}).setSize(98, 20);
public GuiRecordingControls(ReplayMod core, PacketListener packetListener) {
this.core = core;
this.packetListener = packetListener;
paused = stopped = !core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING);
updateState();
}
public void register() {
MinecraftForge.EVENT_BUS.register(this);
}
public void unregister() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@SubscribeEvent
public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) {
if (getGui(event) instanceof GuiIngameMenu) {
show((GuiIngameMenu) getGui(event));
}
}
private void updateState() {
buttonPauseResume.setI18nLabel("replaymod.gui.recording." + (paused ? "resume" : "pause"));
buttonStartStop.setI18nLabel("replaymod.gui.recording." + (stopped ? "start" : "stop"));
buttonPauseResume.setEnabled(!stopped);
}
public void show(GuiIngameMenu gui) {
VanillaGuiScreen.setup(gui).setLayout(new CustomLayout<GuiScreen>() {
@Override
protected void layout(GuiScreen container, int width, int height) {
pos(panel, width / 2 - 100, height / 4 + 128);
}
}).addElements(null, panel);
}
public boolean isPaused() {
return paused;
}
public boolean isStopped() {
return stopped;
}
}

View File

@@ -31,10 +31,12 @@ import static com.replaymod.core.versions.MCVer.*;
public class GuiRecordingOverlay { public class GuiRecordingOverlay {
private final Minecraft mc; private final Minecraft mc;
private final SettingsRegistry settingsRegistry; private final SettingsRegistry settingsRegistry;
private final GuiRecordingControls guiControls;
public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry) { public GuiRecordingOverlay(Minecraft mc, SettingsRegistry settingsRegistry, GuiRecordingControls guiControls) {
this.mc = mc; this.mc = mc;
this.settingsRegistry = settingsRegistry; this.settingsRegistry = settingsRegistry;
this.guiControls = guiControls;
} }
public void register() { public void register() {
@@ -52,9 +54,11 @@ public class GuiRecordingOverlay {
@SubscribeEvent @SubscribeEvent
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) { public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return; if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return;
if (guiControls.isStopped()) return;
if (settingsRegistry.get(Setting.INDICATOR)) { if (settingsRegistry.get(Setting.INDICATOR)) {
FontRenderer fontRenderer = getFontRenderer(mc); FontRenderer fontRenderer = getFontRenderer(mc);
fontRenderer.drawString(I18n.format("replaymod.gui.recording").toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff); String text = guiControls.isPaused() ? I18n.format("replaymod.gui.paused") : I18n.format("replaymod.gui.recording");
fontRenderer.drawString(text.toUpperCase(), 30, 18 - (fontRenderer.FONT_HEIGHT / 2), 0xffffffff);
bindTexture(TEXTURE); bindTexture(TEXTURE);
resetColor(); resetColor();
enableAlpha(); enableAlpha();

View File

@@ -3,7 +3,9 @@ package com.replaymod.recording.handler;
import com.replaymod.core.ReplayMod; import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.ModCompat; import com.replaymod.core.utils.ModCompat;
import com.replaymod.core.utils.Utils; import com.replaymod.core.utils.Utils;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.Setting; import com.replaymod.recording.Setting;
import com.replaymod.recording.gui.GuiRecordingControls;
import com.replaymod.recording.gui.GuiRecordingOverlay; import com.replaymod.recording.gui.GuiRecordingOverlay;
import com.replaymod.recording.packet.PacketListener; import com.replaymod.recording.packet.PacketListener;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
@@ -17,16 +19,9 @@ import org.apache.logging.log4j.Logger;
//#if MC>=10800 //#if MC>=10800
//#if MC>=11300 //#if MC>=11300
import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.eventbus.api.SubscribeEvent;
//#else
//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
//$$ import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
//#endif //#endif
import static com.replaymod.core.versions.MCVer.WorldType_DEBUG_ALL_BLOCK_STATES; import static com.replaymod.core.versions.MCVer.WorldType_DEBUG_ALL_BLOCK_STATES;
//#else
//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent;
//#endif //#endif
import java.io.File; import java.io.File;
@@ -51,6 +46,7 @@ public class ConnectionEventHandler {
private RecordingEventHandler recordingEventHandler; private RecordingEventHandler recordingEventHandler;
private PacketListener packetListener; private PacketListener packetListener;
private GuiRecordingOverlay guiOverlay; private GuiRecordingOverlay guiOverlay;
private GuiRecordingControls guiControls;
public ConnectionEventHandler(Logger logger, ReplayMod core) { public ConnectionEventHandler(Logger logger, ReplayMod core) {
this.logger = logger; this.logger = logger;
@@ -111,26 +107,33 @@ public class ConnectionEventHandler {
metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion()); metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion());
metaData.setDate(System.currentTimeMillis()); metaData.setDate(System.currentTimeMillis());
metaData.setMcVersion(ReplayMod.getMinecraftVersion()); metaData.setMcVersion(ReplayMod.getMinecraftVersion());
packetListener = new PacketListener(replayFile, metaData); packetListener = new PacketListener(core, currentFile.toPath(), replayFile, metaData);
networkManager.channel().pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener); networkManager.channel().pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener);
recordingEventHandler = new RecordingEventHandler(packetListener); recordingEventHandler = new RecordingEventHandler(packetListener);
recordingEventHandler.register(); recordingEventHandler.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry()); guiControls = new GuiRecordingControls(core, packetListener);
guiControls.register();
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
guiOverlay.register(); guiOverlay.register();
if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING)) {
core.printInfoToChat("replaymod.chat.recordingstarted"); core.printInfoToChat("replaymod.chat.recordingstarted");
} else {
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
core.printWarningToChat("replaymod.chat.recordingfailed"); core.printWarningToChat("replaymod.chat.recordingfailed");
} }
} }
/* FIXME event not (yet?) in 1.13 public void reset() {
@SubscribeEvent
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
if (packetListener != null) { if (packetListener != null) {
guiControls.unregister();
guiControls = null;
guiOverlay.unregister(); guiOverlay.unregister();
guiOverlay = null; guiOverlay = null;
recordingEventHandler.unregister(); recordingEventHandler.unregister();
@@ -138,7 +141,6 @@ public class ConnectionEventHandler {
packetListener = null; packetListener = null;
} }
} }
*/
public PacketListener getPacketListener() { public PacketListener getPacketListener() {
return packetListener; return packetListener;

View File

@@ -1,9 +1,16 @@
package com.replaymod.recording.packet; package com.replaymod.recording.packet;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Restrictions; import com.replaymod.core.utils.Restrictions;
import com.replaymod.editor.gui.MarkerProcessor;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.recording.Setting;
import com.replaymod.recording.handler.ConnectionEventHandler;
import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.data.Marker;
import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayFile;
import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.replay.ReplayMetaData;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.utils.Colors;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@@ -42,6 +49,7 @@ import net.minecraft.network.EnumPacketDirection;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -58,6 +66,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private static final Minecraft mc = getMinecraft(); private static final Minecraft mc = getMinecraft();
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private final ReplayMod core;
private final Path outputPath;
private final ReplayFile replayFile; private final ReplayFile replayFile;
private final ResourcePackRecorder resourcePackRecorder; private final ResourcePackRecorder resourcePackRecorder;
@@ -85,7 +95,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
*/ */
private final AtomicInteger lastSaveMetaDataId = new AtomicInteger(); private final AtomicInteger lastSaveMetaDataId = new AtomicInteger();
public PacketListener(ReplayFile replayFile, ReplayMetaData metaData) throws IOException { public PacketListener(ReplayMod core, Path outputPath, ReplayFile replayFile, ReplayMetaData metaData) throws IOException {
this.core = core;
this.outputPath = outputPath;
this.replayFile = replayFile; this.replayFile = replayFile;
this.metaData = metaData; this.metaData = metaData;
this.resourcePackRecorder = new ResourcePackRecorder(replayFile); this.resourcePackRecorder = new ResourcePackRecorder(replayFile);
@@ -174,6 +186,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
metaData.setDuration((int) lastSentPacket); metaData.setDuration((int) lastSentPacket);
saveMetaData(); saveMetaData();
core.runLater(() -> {
ConnectionEventHandler connectionEventHandler = ReplayModRecording.instance.getConnectionEventHandler();
if (connectionEventHandler.getPacketListener() == this) {
connectionEventHandler.reset();
}
});
GuiLabel savingLabel = new GuiLabel().setI18nText("replaymod.gui.replaysaving.title").setColor(Colors.BLACK);
new Thread(() -> {
core.runLater(() -> core.getBackgroundProcesses().addProcess(savingLabel));
saveService.shutdown(); saveService.shutdown();
try { try {
saveService.awaitTermination(10, TimeUnit.SECONDS); saveService.awaitTermination(10, TimeUnit.SECONDS);
@@ -185,10 +208,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
try { try {
replayFile.save(); replayFile.save();
replayFile.close(); replayFile.close();
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) {
MarkerProcessor.apply(outputPath);
}
} catch (IOException e) { } catch (IOException e) {
logger.error("Saving replay file:", e); logger.error("Saving replay file:", e);
} }
} }
core.runLater(() -> core.getBackgroundProcesses().removeProcess(savingLabel));
}).start();
} }
@Override @Override
@@ -376,11 +406,15 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
return array; return array;
} }
public void addMarker() { public void addMarker(String name) {
addMarker(name, (int) getCurrentDuration());
}
public void addMarker(String name, int timestamp) {
Entity view = getRenderViewEntity(mc); Entity view = getRenderViewEntity(mc);
int timestamp = (int) (System.currentTimeMillis() - startTime);
Marker marker = new Marker(); Marker marker = new Marker();
marker.setName(name);
marker.setTime(timestamp); marker.setTime(timestamp);
marker.setX(view.posX); marker.setX(view.posX);
marker.setY(view.posY); marker.setY(view.posY);
@@ -401,6 +435,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
}); });
} }
public long getCurrentDuration() {
return lastSentPacket;
}
public void setServerWasPaused() { public void setServerWasPaused() {
this.serverWasPaused = true; this.serverWasPaused = true;
} }