Introduce minimal mode (allows RM to run on unsupported MC versions)
by disabling various version-specific features (i.e. everything which requires the ReplayStudio).
This commit is contained in:
@@ -14,6 +14,7 @@ import com.replaymod.online.ReplayModOnline;
|
|||||||
import com.replaymod.recording.ReplayModRecording;
|
import com.replaymod.recording.ReplayModRecording;
|
||||||
import com.replaymod.render.ReplayModRender;
|
import com.replaymod.render.ReplayModRender;
|
||||||
import com.replaymod.replay.ReplayModReplay;
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
|
import com.replaymod.replaystudio.studio.ReplayStudio;
|
||||||
import com.replaymod.replaystudio.util.I18n;
|
import com.replaymod.replaystudio.util.I18n;
|
||||||
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
import com.replaymod.simplepathing.ReplayModSimplePathing;
|
||||||
import de.johni0702.minecraft.gui.container.GuiScreen;
|
import de.johni0702.minecraft.gui.container.GuiScreen;
|
||||||
@@ -157,6 +158,16 @@ public class ReplayMod implements
|
|||||||
|
|
||||||
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
|
private final GuiBackgroundProcesses backgroundProcesses = new GuiBackgroundProcesses();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the current MC version is supported by the embedded ReplayStudio version.
|
||||||
|
* If this is not the case (i.e. if this is variable true), any feature of the RM which depends on the ReplayStudio
|
||||||
|
* lib will be disabled.
|
||||||
|
*
|
||||||
|
* Only supported on Fabric builds, i.e. will always be false / crash the game with Forge/pre-1.14 builds.
|
||||||
|
* (specifically the code below and MCVer#getProtocolVersion make this assumption)
|
||||||
|
*/
|
||||||
|
private boolean minimalMode;
|
||||||
|
|
||||||
public ReplayMod() {
|
public ReplayMod() {
|
||||||
I18n.setI18n(net.minecraft.client.resource.language.I18n::translate);
|
I18n.setI18n(net.minecraft.client.resource.language.I18n::translate);
|
||||||
|
|
||||||
@@ -164,12 +175,17 @@ public class ReplayMod implements
|
|||||||
// Check Minecraft protocol version for compatibility
|
// Check Minecraft protocol version for compatibility
|
||||||
int supportedProtocol = MinecraftConstants.PROTOCOL_VERSION;
|
int supportedProtocol = MinecraftConstants.PROTOCOL_VERSION;
|
||||||
int actualProtocol = SharedConstants.getGameVersion().getProtocolVersion();
|
int actualProtocol = SharedConstants.getGameVersion().getProtocolVersion();
|
||||||
if (supportedProtocol != actualProtocol) {
|
if (supportedProtocol != actualProtocol && !Boolean.parseBoolean(System.getProperty("replaymod.skipversioncheck", "false"))) {
|
||||||
throw new UnsupportedOperationException(String.format(
|
minimalMode = true;
|
||||||
"Unsupported Minecraft version, supporting protocol version %s (%s) but actual version is %s (%s).",
|
// Only allow use of older RM on newer (e.g. snapshot, pre-release) versions.
|
||||||
supportedProtocol, MinecraftConstants.GAME_VERSION,
|
// The other way around is almost certainly user error.
|
||||||
actualProtocol, SharedConstants.getGameVersion().getName()
|
if (actualProtocol < supportedProtocol && !Boolean.parseBoolean(System.getProperty("replaymod.allowoldsnapshot", "false"))) {
|
||||||
));
|
throw new UnsupportedOperationException(String.format(
|
||||||
|
"Unsupported Minecraft version, supporting protocol version %s (%s) but actual version is %s (%s).",
|
||||||
|
supportedProtocol, MinecraftConstants.GAME_VERSION,
|
||||||
|
actualProtocol, SharedConstants.getGameVersion().getName()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
@@ -602,4 +618,17 @@ public class ReplayMod implements
|
|||||||
public GuiBackgroundProcesses getBackgroundProcesses() {
|
public GuiBackgroundProcesses getBackgroundProcesses() {
|
||||||
return backgroundProcesses;
|
return backgroundProcesses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This method is static because it depends solely on the environment, not on the actual RM instance.
|
||||||
|
public static boolean isMinimalMode() {
|
||||||
|
return ReplayMod.instance.minimalMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCompatible(int fileFormatVersion, int protocolVersion) {
|
||||||
|
if (isMinimalMode()) {
|
||||||
|
return protocolVersion == MCVer.getProtocolVersion();
|
||||||
|
} else {
|
||||||
|
return new ReplayStudio().isCompatible(fileFormatVersion, protocolVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.replaymod.core.gui;
|
package com.replaymod.core.gui;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.replaymod.replaystudio.PacketData;
|
import com.google.gson.Gson;
|
||||||
import com.replaymod.replaystudio.io.ReplayInputStream;
|
import com.replaymod.replaystudio.io.ReplayInputStream;
|
||||||
import com.replaymod.replaystudio.io.ReplayOutputStream;
|
import com.replaymod.replaystudio.io.ReplayOutputStream;
|
||||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||||
@@ -17,9 +17,14 @@ import de.johni0702.minecraft.gui.layout.CustomLayout;
|
|||||||
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import static com.replaymod.replaystudio.util.Utils.readInt;
|
||||||
|
import static com.replaymod.replaystudio.util.Utils.writeInt;
|
||||||
|
|
||||||
public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
|
public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
|
||||||
|
|
||||||
@@ -53,16 +58,34 @@ public class RestoreReplayGui extends AbstractGuiScreen<RestoreReplayGui> {
|
|||||||
// We need to re-write the packet data in case there are any incomplete packets dangling at the end
|
// We need to re-write the packet data in case there are any incomplete packets dangling at the end
|
||||||
try (ReplayInputStream in = replayFile.getPacketData(studio, true);
|
try (ReplayInputStream in = replayFile.getPacketData(studio, true);
|
||||||
ReplayOutputStream out = replayFile.writePacketData(true)) {
|
ReplayOutputStream out = replayFile.writePacketData(true)) {
|
||||||
PacketData last = null;
|
while (true) {
|
||||||
while ((last = in.readPacket()) != null) {
|
// To prevent failing at un-parsable packets and to support recovery in minimal mode,
|
||||||
metaData.setDuration((int) last.getTime());
|
// we do not use the ReplayIn/OutputStream methods but instead parse the packets ourselves.
|
||||||
out.write(last);
|
int time = readInt(in);
|
||||||
|
int length = readInt(in);
|
||||||
|
if (time == -1 || length == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byte[] buf = new byte[length];
|
||||||
|
IOUtils.readFully(in, buf);
|
||||||
|
|
||||||
|
// Fully read, update replay duration
|
||||||
|
metaData.setDuration(time);
|
||||||
|
|
||||||
|
// Write packet back into recovered replay
|
||||||
|
writeInt(out, time);
|
||||||
|
writeInt(out, length);
|
||||||
|
out.write(buf);
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
// Write back the actual duration
|
// Write back the actual duration
|
||||||
replayFile.writeMetaData(metaData);
|
try (OutputStream out = replayFile.write("metaData.json")) {
|
||||||
|
metaData.setGenerator(metaData.getGenerator() + "(+ ReplayMod Replay Recovery)");
|
||||||
|
String json = (new Gson()).toJson(metaData);
|
||||||
|
out.write(json.getBytes());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
replayFile.save();
|
replayFile.save();
|
||||||
replayFile.close();
|
replayFile.close();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.google.common.net.PercentEscaper;
|
|||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.replaymod.core.ReplayMod;
|
||||||
import de.johni0702.minecraft.gui.GuiRenderer;
|
import de.johni0702.minecraft.gui.GuiRenderer;
|
||||||
import de.johni0702.minecraft.gui.RenderInfo;
|
import de.johni0702.minecraft.gui.RenderInfo;
|
||||||
import de.johni0702.minecraft.gui.container.AbstractGuiScrollable;
|
import de.johni0702.minecraft.gui.container.AbstractGuiScrollable;
|
||||||
@@ -26,6 +27,7 @@ import net.minecraft.util.crash.CrashReport;
|
|||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import org.apache.commons.io.Charsets;
|
import org.apache.commons.io.Charsets;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
//#if MC>=11300
|
//#if MC>=11300
|
||||||
@@ -34,10 +36,11 @@ import org.apache.logging.log4j.Logger;
|
|||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
//#if MC>=10800
|
//#if MC>=10800
|
||||||
|
import com.github.steveice10.mc.protocol.MinecraftConstants;
|
||||||
import net.minecraft.client.network.PlayerListEntry;
|
import net.minecraft.client.network.PlayerListEntry;
|
||||||
import net.minecraft.client.util.DefaultSkinHelper;
|
import net.minecraft.client.util.DefaultSkinHelper;
|
||||||
|
|
||||||
//#else
|
//#else
|
||||||
|
//$$ import com.github.steveice10.mc.protocol.ProtocolConstants;
|
||||||
//$$ import net.minecraft.client.Minecraft;
|
//$$ import net.minecraft.client.Minecraft;
|
||||||
//$$ import net.minecraft.client.entity.AbstractClientPlayer;
|
//$$ import net.minecraft.client.entity.AbstractClientPlayer;
|
||||||
//$$ import net.minecraft.entity.player.EntityPlayer;
|
//$$ import net.minecraft.entity.player.EntityPlayer;
|
||||||
@@ -71,6 +74,7 @@ import java.util.function.Consumer;
|
|||||||
import static com.replaymod.core.versions.MCVer.getMinecraft;
|
import static com.replaymod.core.versions.MCVer.getMinecraft;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
private static Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
private static InputStream getResourceAsStream(String path) {
|
private static InputStream getResourceAsStream(String path) {
|
||||||
// FIXME this seems broken in 1.13, hence the workaround. probably want to open an issue with modlauncher (or forge?)
|
// FIXME this seems broken in 1.13, hence the workaround. probably want to open an issue with modlauncher (or forge?)
|
||||||
@@ -315,4 +319,62 @@ public class Utils {
|
|||||||
throw (Error) t;
|
throw (Error) t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void denyIfMinimalMode(GuiContainer container, Runnable onPopupClosed, Runnable orElseRun) {
|
||||||
|
if (isNotMinimalModeElsePopup(container, onPopupClosed)) {
|
||||||
|
orElseRun.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean ifMinimalModeDoPopup(GuiContainer container, Runnable onPopupClosed) {
|
||||||
|
return !isNotMinimalModeElsePopup(container, onPopupClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNotMinimalModeElsePopup(GuiContainer container, Runnable onPopupClosed) {
|
||||||
|
if (!ReplayMod.isMinimalMode()) {
|
||||||
|
LOGGER.trace("Minimal mode not active, continuing");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOGGER.trace("Minimal mode active, denying action, opening popup");
|
||||||
|
|
||||||
|
MinimalModeUnsupportedPopup popup = new MinimalModeUnsupportedPopup(container);
|
||||||
|
Futures.addCallback(popup.getFuture(), new FutureCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@Nullable Void result) {
|
||||||
|
LOGGER.trace("Minimal mode popup closed");
|
||||||
|
if (onPopupClosed != null) {
|
||||||
|
onPopupClosed.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
LOGGER.error("During minimal mode popup:", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MinimalModeUnsupportedPopup extends GuiInfoPopup {
|
||||||
|
private MinimalModeUnsupportedPopup(GuiContainer container) {
|
||||||
|
super(container);
|
||||||
|
setBackgroundColor(Colors.DARK_TRANSPARENT);
|
||||||
|
|
||||||
|
getInfo().addElements(new VerticalLayout.Data(0.5),
|
||||||
|
new GuiLabel()
|
||||||
|
.setColor(Colors.BLACK)
|
||||||
|
.setI18nText("replaymod.gui.minimalmode.unsupported"),
|
||||||
|
new GuiLabel()
|
||||||
|
.setColor(Colors.BLACK)
|
||||||
|
.setI18nText("replaymod.gui.minimalmode.supportedversion",
|
||||||
|
//#if MC>=10800
|
||||||
|
MinecraftConstants.GAME_VERSION
|
||||||
|
//#else
|
||||||
|
//$$ ProtocolConstants.GAME_VERSION
|
||||||
|
//#endif
|
||||||
|
));
|
||||||
|
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ import net.minecraft.client.render.VertexFormatElement;
|
|||||||
|
|
||||||
//#if MC>=11400
|
//#if MC>=11400
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
//#else
|
//#else
|
||||||
//#if MC>=11300
|
//#if MC>=11300
|
||||||
//$$ import net.minecraftforge.fml.ModList;
|
//$$ import net.minecraftforge.fml.ModList;
|
||||||
@@ -117,6 +118,14 @@ public class MCVer {
|
|||||||
//#endif
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getProtocolVersion() {
|
||||||
|
//#if MC>=11400
|
||||||
|
return SharedConstants.getGameVersion().getProtocolVersion();
|
||||||
|
//#else
|
||||||
|
//$$ throw new UnsupportedOperationException("Minimal mode not supported pre-1.14");
|
||||||
|
//#endif
|
||||||
|
}
|
||||||
|
|
||||||
public static void addDetail(CrashReportSection category, String name, Callable<String> callable) {
|
public static void addDetail(CrashReportSection category, String name, Callable<String> callable) {
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
//#if MC>=11200
|
//#if MC>=11200
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class GuiHandler extends EventRegistrations {
|
|||||||
// Inject Edit button
|
// Inject Edit button
|
||||||
if (!replayViewer.editorButton.getChildren().isEmpty()) return;
|
if (!replayViewer.editorButton.getChildren().isEmpty()) return;
|
||||||
replayViewer.replaySpecificButtons.add(new GuiButton(replayViewer.editorButton).onClick(() -> {
|
replayViewer.replaySpecificButtons.add(new GuiButton(replayViewer.editorButton).onClick(() -> {
|
||||||
|
if (Utils.ifMinimalModeDoPopup(replayViewer, () -> {})) return;
|
||||||
try {
|
try {
|
||||||
new GuiEditReplay(replayViewer, replayViewer.list.getSelected().file.toPath()) {
|
new GuiEditReplay(replayViewer, replayViewer.list.getSelected().file.toPath()) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.google.common.base.Supplier;
|
|||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.replaymod.core.ReplayMod;
|
||||||
import com.replaymod.core.utils.Utils;
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.online.ReplayModOnline;
|
import com.replaymod.online.ReplayModOnline;
|
||||||
import com.replaymod.online.api.ApiClient;
|
import com.replaymod.online.api.ApiClient;
|
||||||
@@ -425,7 +426,7 @@ public class GuiReplayCenter extends GuiScreen {
|
|||||||
} else {
|
} else {
|
||||||
server.setText(metaData.getServerName());
|
server.setText(metaData.getServerName());
|
||||||
}
|
}
|
||||||
incompatible = !new ReplayStudio().isCompatible(fileInfo.getMetadata().getFileFormatVersion());
|
incompatible = !ReplayMod.isCompatible(fileInfo.getMetadata().getFileFormatVersion(), -1); // TODO protocol version support on website
|
||||||
if (incompatible) {
|
if (incompatible) {
|
||||||
version.setText("Minecraft " + fileInfo.getMetadata().getMcVersion());
|
version.setText("Minecraft " + fileInfo.getMetadata().getMcVersion());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.replaymod.recording.gui;
|
package com.replaymod.recording.gui;
|
||||||
|
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.Utils;
|
||||||
import com.replaymod.editor.gui.MarkerProcessor;
|
import com.replaymod.editor.gui.MarkerProcessor;
|
||||||
import com.replaymod.recording.Setting;
|
import com.replaymod.recording.Setting;
|
||||||
import com.replaymod.recording.packet.PacketListener;
|
import com.replaymod.recording.packet.PacketListener;
|
||||||
@@ -32,6 +33,7 @@ public class GuiRecordingControls extends EventRegistrations {
|
|||||||
private GuiPanel panel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4));
|
private GuiPanel panel = new GuiPanel().setLayout(new HorizontalLayout().setSpacing(4));
|
||||||
|
|
||||||
private GuiButton buttonPauseResume = new GuiButton(panel).onClick(() -> {
|
private GuiButton buttonPauseResume = new GuiButton(panel).onClick(() -> {
|
||||||
|
if (Utils.ifMinimalModeDoPopup(panel, () -> {})) return;
|
||||||
if (paused) {
|
if (paused) {
|
||||||
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
|
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
|
||||||
} else {
|
} else {
|
||||||
@@ -42,6 +44,7 @@ public class GuiRecordingControls extends EventRegistrations {
|
|||||||
}).setSize(98, 20);
|
}).setSize(98, 20);
|
||||||
|
|
||||||
private GuiButton buttonStartStop = new GuiButton(panel).onClick(() -> {
|
private GuiButton buttonStartStop = new GuiButton(panel).onClick(() -> {
|
||||||
|
if (Utils.ifMinimalModeDoPopup(panel, () -> {})) return;
|
||||||
if (stopped) {
|
if (stopped) {
|
||||||
paused = false;
|
paused = false;
|
||||||
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
|
packetListener.addMarker(MarkerProcessor.MARKER_NAME_END_CUT);
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public class ConnectionEventHandler {
|
|||||||
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
|
guiOverlay = new GuiRecordingOverlay(mc, core.getSettingsRegistry(), guiControls);
|
||||||
guiOverlay.register();
|
guiOverlay.register();
|
||||||
|
|
||||||
if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING)) {
|
if (core.getSettingsRegistry().get(Setting.AUTO_START_RECORDING) || ReplayMod.isMinimalMode()) {
|
||||||
core.printInfoToChat("replaymod.chat.recordingstarted");
|
core.printInfoToChat("replaymod.chat.recordingstarted");
|
||||||
} else {
|
} else {
|
||||||
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
|
packetListener.addMarker(MarkerProcessor.MARKER_NAME_START_CUT, 0);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.replaymod.recording.packet;
|
package com.replaymod.recording.packet;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
import com.replaymod.core.utils.Restrictions;
|
import com.replaymod.core.utils.Restrictions;
|
||||||
|
import com.replaymod.core.versions.MCVer;
|
||||||
import com.replaymod.editor.gui.MarkerProcessor;
|
import com.replaymod.editor.gui.MarkerProcessor;
|
||||||
import com.replaymod.recording.ReplayModRecording;
|
import com.replaymod.recording.ReplayModRecording;
|
||||||
import com.replaymod.recording.Setting;
|
import com.replaymod.recording.Setting;
|
||||||
@@ -45,6 +47,7 @@ import net.minecraft.network.NetworkSide;
|
|||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -112,7 +115,19 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
synchronized (replayFile) {
|
synchronized (replayFile) {
|
||||||
replayFile.writeMetaData(metaData);
|
if (ReplayMod.isMinimalMode()) {
|
||||||
|
metaData.setFileFormat("MCPR");
|
||||||
|
metaData.setFileFormatVersion(ReplayMetaData.CURRENT_FILE_FORMAT_VERSION);
|
||||||
|
metaData.setProtocolVersion(MCVer.getProtocolVersion());
|
||||||
|
metaData.setGenerator("ReplayMod in Minimal Mode");
|
||||||
|
|
||||||
|
try (OutputStream out = replayFile.write("metaData.json")) {
|
||||||
|
String json = (new Gson()).toJson(metaData);
|
||||||
|
out.write(json.getBytes());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replayFile.writeMetaData(metaData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Writing metadata:", e);
|
logger.error("Writing metadata:", e);
|
||||||
@@ -197,7 +212,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
|
|||||||
replayFile.save();
|
replayFile.save();
|
||||||
replayFile.close();
|
replayFile.close();
|
||||||
|
|
||||||
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS)) {
|
if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS) && !ReplayMod.isMinimalMode()) {
|
||||||
MarkerProcessor.apply(outputPath, progress -> {});
|
MarkerProcessor.apply(outputPath, progress -> {});
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.replaymod.core.versions.MCVer.*;
|
import static com.replaymod.core.versions.MCVer.*;
|
||||||
|
import static com.replaymod.replaystudio.util.Utils.readInt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends replay packets to netty channels.
|
* Sends replay packets to netty channels.
|
||||||
@@ -1102,26 +1103,37 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend
|
|||||||
private final byte[] bytes;
|
private final byte[] bytes;
|
||||||
|
|
||||||
PacketData(ReplayInputStream in, boolean loginPhase) throws IOException {
|
PacketData(ReplayInputStream in, boolean loginPhase) throws IOException {
|
||||||
com.replaymod.replaystudio.PacketData data = in.readPacket();
|
if (ReplayMod.isMinimalMode()) {
|
||||||
timestamp = (int) data.getTime();
|
// Minimal mode, we can only read our exact protocol version and cannot use ReplayStudio
|
||||||
// We need to re-encode MCProtocolLib packets, so we can later decode them as NMS packets
|
timestamp = readInt(in);
|
||||||
// The main reason we aren't reading them as NMS packets is that we want ReplayStudio to be able
|
int length = readInt(in);
|
||||||
// to apply ViaVersion (and potentially other magic) to it.
|
if (timestamp == -1 || length == -1) {
|
||||||
synchronized (byteBuf) {
|
throw new EOFException();
|
||||||
byteBuf.markReaderIndex(); // Mark the current reader and writer index (should be at start)
|
|
||||||
byteBuf.markWriterIndex();
|
|
||||||
|
|
||||||
try {
|
|
||||||
(loginPhase ? codecLoginPhase : codecPlayPhase).encode(null, data.getPacket(), byteBuf);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
|
bytes = new byte[length];
|
||||||
|
IOUtils.readFully(in, bytes);
|
||||||
|
} else {
|
||||||
|
com.replaymod.replaystudio.PacketData data = in.readPacket();
|
||||||
|
timestamp = (int) data.getTime();
|
||||||
|
// We need to re-encode MCProtocolLib packets, so we can later decode them as NMS packets
|
||||||
|
// The main reason we aren't reading them as NMS packets is that we want ReplayStudio to be able
|
||||||
|
// to apply ViaVersion (and potentially other magic) to it.
|
||||||
|
synchronized (byteBuf) {
|
||||||
|
byteBuf.markReaderIndex(); // Mark the current reader and writer index (should be at start)
|
||||||
|
byteBuf.markWriterIndex();
|
||||||
|
|
||||||
bytes = new byte[byteBuf.readableBytes()]; // Create bytes array of sufficient size
|
try {
|
||||||
byteBuf.readBytes(bytes); // Read all data into bytes
|
(loginPhase ? codecLoginPhase : codecPlayPhase).encode(null, data.getPacket(), byteBuf);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
byteBuf.resetReaderIndex(); // Reset reader & writer index for next use
|
bytes = new byte[byteBuf.readableBytes()]; // Create bytes array of sufficient size
|
||||||
byteBuf.resetWriterIndex();
|
byteBuf.readBytes(bytes); // Read all data into bytes
|
||||||
|
|
||||||
|
byteBuf.resetReaderIndex(); // Reset reader & writer index for next use
|
||||||
|
byteBuf.resetWriterIndex();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ public class ReplayHandler {
|
|||||||
|
|
||||||
//#if MC>=10904
|
//#if MC>=10904
|
||||||
public void ensureQuickModeInitialized(Runnable andThen) {
|
public void ensureQuickModeInitialized(Runnable andThen) {
|
||||||
|
if (Utils.ifMinimalModeDoPopup(overlay, () -> {})) return;
|
||||||
ListenableFuture<Void> future = quickReplaySender.getInitializationPromise();
|
ListenableFuture<Void> future = quickReplaySender.getInitializationPromise();
|
||||||
if (future == null) {
|
if (future == null) {
|
||||||
InitializingQuickModePopup popup = new InitializingQuickModePopup(overlay);
|
InitializingQuickModePopup popup = new InitializingQuickModePopup(overlay);
|
||||||
@@ -365,6 +366,9 @@ public class ReplayHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setQuickMode(boolean quickMode) {
|
public void setQuickMode(boolean quickMode) {
|
||||||
|
if (ReplayMod.isMinimalMode()) {
|
||||||
|
throw new UnsupportedOperationException("Quick Mode not supported in minimal mode.");
|
||||||
|
}
|
||||||
if (quickMode == this.quickMode) return;
|
if (quickMode == this.quickMode) return;
|
||||||
if (quickMode && fullReplaySender.isAsyncMode()) {
|
if (quickMode && fullReplaySender.isAsyncMode()) {
|
||||||
// If this method is called via runLater, then it cannot switch to sync mode by itself as there might be
|
// If this method is called via runLater, then it cannot switch to sync mode by itself as there might be
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ public class GuiReplayViewer extends GuiScreen {
|
|||||||
} else {
|
} else {
|
||||||
server.setText(metaData.getServerName());
|
server.setText(metaData.getServerName());
|
||||||
}
|
}
|
||||||
incompatible = !new ReplayStudio().isCompatible(metaData.getFileFormatVersion(), metaData.getProtocolVersion());
|
incompatible = !ReplayMod.isCompatible(metaData.getFileFormatVersion(), metaData.getProtocolVersion());
|
||||||
if (incompatible) {
|
if (incompatible) {
|
||||||
version.setText("Minecraft " + metaData.getMcVersion());
|
version.setText("Minecraft " + metaData.getMcVersion());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user