Add start/stop/pause/resume recording functionality
Using automatically applied, ReplayStudio-based post processing.
This commit is contained in:
@@ -60,7 +60,7 @@ public class ReplayModRecording implements Module {
|
||||
public void run() {
|
||||
PacketListener packetListener = connectionEventHandler.getPacketListener();
|
||||
if (packetListener != null) {
|
||||
packetListener.addMarker();
|
||||
packetListener.addMarker(null);
|
||||
core.printInfoToChat("replaymod.chat.addedmarker");
|
||||
}
|
||||
}
|
||||
@@ -92,4 +92,8 @@ public class ReplayModRecording implements Module {
|
||||
//#endif
|
||||
connectionEventHandler.onConnectedToServerEvent(networkManager);
|
||||
}
|
||||
|
||||
public ConnectionEventHandler getConnectionEventHandler() {
|
||||
return connectionEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_SERVER = make("recordServer", "recordserver", 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) {
|
||||
return new Setting<>(key, displayName, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,12 @@ import static com.replaymod.core.versions.MCVer.*;
|
||||
public class GuiRecordingOverlay {
|
||||
private final Minecraft mc;
|
||||
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.settingsRegistry = settingsRegistry;
|
||||
this.guiControls = guiControls;
|
||||
}
|
||||
|
||||
public void register() {
|
||||
@@ -52,9 +54,11 @@ public class GuiRecordingOverlay {
|
||||
@SubscribeEvent
|
||||
public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) {
|
||||
if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return;
|
||||
if (guiControls.isStopped()) return;
|
||||
if (settingsRegistry.get(Setting.INDICATOR)) {
|
||||
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);
|
||||
resetColor();
|
||||
enableAlpha();
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.replaymod.recording.handler;
|
||||
import com.replaymod.core.ReplayMod;
|
||||
import com.replaymod.core.utils.ModCompat;
|
||||
import com.replaymod.core.utils.Utils;
|
||||
import com.replaymod.editor.gui.MarkerProcessor;
|
||||
import com.replaymod.recording.Setting;
|
||||
import com.replaymod.recording.gui.GuiRecordingControls;
|
||||
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
||||
import com.replaymod.recording.packet.PacketListener;
|
||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||
@@ -17,16 +19,9 @@ import org.apache.logging.log4j.Logger;
|
||||
//#if MC>=10800
|
||||
//#if MC>=11300
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
import java.io.File;
|
||||
@@ -51,6 +46,7 @@ public class ConnectionEventHandler {
|
||||
private RecordingEventHandler recordingEventHandler;
|
||||
private PacketListener packetListener;
|
||||
private GuiRecordingOverlay guiOverlay;
|
||||
private GuiRecordingControls guiControls;
|
||||
|
||||
public ConnectionEventHandler(Logger logger, ReplayMod core) {
|
||||
this.logger = logger;
|
||||
@@ -111,26 +107,33 @@ public class ConnectionEventHandler {
|
||||
metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion());
|
||||
metaData.setDate(System.currentTimeMillis());
|
||||
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);
|
||||
|
||||
recordingEventHandler = new RecordingEventHandler(packetListener);
|
||||
recordingEventHandler.register();
|
||||
|
||||
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry());
|
||||
guiControls = new GuiRecordingControls(core, packetListener);
|
||||
guiControls.register();
|
||||
|
||||
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
|
||||
guiOverlay.register();
|
||||
|
||||
core.printInfoToChat("replaymod.chat.recordingstarted");
|
||||
if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING)) {
|
||||
core.printInfoToChat("replaymod.chat.recordingstarted");
|
||||
} else {
|
||||
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
core.printWarningToChat("replaymod.chat.recordingfailed");
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME event not (yet?) in 1.13
|
||||
@SubscribeEvent
|
||||
public void onDisconnectedFromServerEvent(ClientDisconnectionFromServerEvent event) {
|
||||
public void reset() {
|
||||
if (packetListener != null) {
|
||||
guiControls.unregister();
|
||||
guiControls = null;
|
||||
guiOverlay.unregister();
|
||||
guiOverlay = null;
|
||||
recordingEventHandler.unregister();
|
||||
@@ -138,7 +141,6 @@ public class ConnectionEventHandler {
|
||||
packetListener = null;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public PacketListener getPacketListener() {
|
||||
return packetListener;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
package com.replaymod.recording.packet;
|
||||
|
||||
import com.replaymod.core.ReplayMod;
|
||||
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.replay.ReplayFile;
|
||||
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.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -42,6 +49,7 @@ import net.minecraft.network.EnumPacketDirection;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -58,6 +66,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
private static final Minecraft mc = getMinecraft();
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
|
||||
private final ReplayMod core;
|
||||
private final Path outputPath;
|
||||
private final ReplayFile replayFile;
|
||||
|
||||
private final ResourcePackRecorder resourcePackRecorder;
|
||||
@@ -85,7 +95,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
*/
|
||||
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.metaData = metaData;
|
||||
this.resourcePackRecorder = new ResourcePackRecorder(replayFile);
|
||||
@@ -174,21 +186,39 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
metaData.setDuration((int) lastSentPacket);
|
||||
saveMetaData();
|
||||
|
||||
saveService.shutdown();
|
||||
try {
|
||||
saveService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Waiting for save service termination:", e);
|
||||
}
|
||||
|
||||
synchronized (replayFile) {
|
||||
try {
|
||||
replayFile.save();
|
||||
replayFile.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("Saving replay file:", e);
|
||||
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();
|
||||
try {
|
||||
saveService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Waiting for save service termination:", e);
|
||||
}
|
||||
|
||||
synchronized (replayFile) {
|
||||
try {
|
||||
replayFile.save();
|
||||
replayFile.close();
|
||||
|
||||
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) {
|
||||
MarkerProcessor.apply(outputPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Saving replay file:", e);
|
||||
}
|
||||
}
|
||||
|
||||
core.runLater(() -> core.getBackgroundProcesses().removeProcess(savingLabel));
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -376,11 +406,15 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
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);
|
||||
int timestamp = (int) (System.currentTimeMillis() - startTime);
|
||||
|
||||
Marker marker = new Marker();
|
||||
marker.setName(name);
|
||||
marker.setTime(timestamp);
|
||||
marker.setX(view.posX);
|
||||
marker.setY(view.posY);
|
||||
@@ -401,6 +435,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
public long getCurrentDuration() {
|
||||
return lastSentPacket;
|
||||
}
|
||||
|
||||
public void setServerWasPaused() {
|
||||
this.serverWasPaused = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user