Add start/stop/pause/resume recording functionality
Using automatically applied, ReplayStudio-based post processing.
This commit is contained in:
@@ -2,6 +2,7 @@ package com.replaymod.core;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.replaymod.core.gui.GuiBackgroundProcesses;
|
||||
import com.replaymod.core.gui.GuiReplaySettings;
|
||||
import com.replaymod.core.gui.RestoreReplayGui;
|
||||
import com.replaymod.core.handler.MainMenuHandler;
|
||||
@@ -152,6 +153,8 @@ public class ReplayMod implements Module {
|
||||
|
||||
private final List<Module> modules = new ArrayList<>();
|
||||
|
||||
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
|
||||
|
||||
public ReplayMod() {
|
||||
I18n.setI18n(net.minecraft.client.resources.I18n::format);
|
||||
|
||||
@@ -285,6 +288,7 @@ public class ReplayMod implements Module {
|
||||
//#endif
|
||||
|
||||
FML_BUS.register(keyBindingRegistry);
|
||||
FORGE_BUS.register(backgroundProcesses);
|
||||
|
||||
// 1.7.10 crashes when render distance > 16
|
||||
//#if MC>=10800
|
||||
@@ -455,4 +459,8 @@ public class ReplayMod implements Module {
|
||||
mc.ingameGUI.getChatGUI().printChatMessage(text);
|
||||
}
|
||||
}
|
||||
|
||||
public GuiBackgroundProcesses getBackgroundProcesses() {
|
||||
return backgroundProcesses;
|
||||
}
|
||||
}
|
||||
|
||||
135
src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java
Normal file
135
src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
173
src/main/java/com/replaymod/editor/gui/MarkerProcessor.java
Normal file
173
src/main/java/com/replaymod/editor/gui/MarkerProcessor.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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