Store networking mods in replay file and compare installed versions on replay
This commit is contained in:
Submodule ReplayStudio updated: eaeb19666e...040e61d71d
61
src/main/java/com/replaymod/core/utils/ModCompat.java
Normal file
61
src/main/java/com/replaymod/core/utils/ModCompat.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package com.replaymod.core.utils;
|
||||||
|
|
||||||
|
import com.replaymod.replaystudio.data.ModInfo;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.fml.common.Loader;
|
||||||
|
import net.minecraftforge.fml.common.ModContainer;
|
||||||
|
import net.minecraftforge.fml.common.registry.GameData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ModCompat {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Collection<ModInfo> getInstalledNetworkMods() {
|
||||||
|
Map<String, ModContainer> ignoreCaseMap = Loader.instance().getModList().stream()
|
||||||
|
.collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity()));
|
||||||
|
return Stream.concat(
|
||||||
|
((Set<ResourceLocation>) GameData.getBlockRegistry().getKeys()).stream(),
|
||||||
|
((Set<ResourceLocation>) GameData.getItemRegistry().getKeys()).stream()
|
||||||
|
).map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct()
|
||||||
|
.map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null)
|
||||||
|
.map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ModInfoDifference {
|
||||||
|
private final Set<ModInfo> missing = new HashSet<>();
|
||||||
|
private final Map<ModInfo, String> differing = new HashMap<>();
|
||||||
|
public ModInfoDifference(Collection<ModInfo> requiredList) {
|
||||||
|
Collection<ModInfo> installedList = getInstalledNetworkMods();
|
||||||
|
REQUIRED:
|
||||||
|
for (ModInfo required : requiredList) {
|
||||||
|
for (ModInfo installed : installedList) {
|
||||||
|
if (required.getId().equals(installed.getId())) {
|
||||||
|
// Mod is installed, check if versions match
|
||||||
|
if (Objects.equals(required.getVersion(), installed.getVersion())) {
|
||||||
|
// Mod found and version match
|
||||||
|
continue REQUIRED;
|
||||||
|
} else {
|
||||||
|
// Mod found but versions don't match
|
||||||
|
differing.put(required, installed.getVersion());
|
||||||
|
continue REQUIRED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mod no longer installed
|
||||||
|
missing.add(required);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ModInfo> getMissing() {
|
||||||
|
return Collections.unmodifiableSet(missing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ModInfo, String> getDiffering() {
|
||||||
|
return Collections.unmodifiableMap(differing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.replaymod.recording.handler;
|
package com.replaymod.recording.handler;
|
||||||
|
|
||||||
import com.replaymod.core.ReplayMod;
|
import com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.ModCompat;
|
||||||
import com.replaymod.recording.Setting;
|
import com.replaymod.recording.Setting;
|
||||||
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
import com.replaymod.recording.gui.GuiRecordingOverlay;
|
||||||
import com.replaymod.recording.packet.PacketListener;
|
import com.replaymod.recording.packet.PacketListener;
|
||||||
@@ -83,6 +84,8 @@ public class ConnectionEventHandler {
|
|||||||
File currentFile = new File(folder, name + ".mcpr");
|
File currentFile = new File(folder, name + ".mcpr");
|
||||||
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
|
ReplayFile replayFile = new ZipReplayFile(new ReplayStudio(), currentFile);
|
||||||
|
|
||||||
|
replayFile.writeModInfo(ModCompat.getInstalledNetworkMods());
|
||||||
|
|
||||||
ReplayMetaData metaData = new ReplayMetaData();
|
ReplayMetaData metaData = new ReplayMetaData();
|
||||||
metaData.setSingleplayer(event.isLocal);
|
metaData.setSingleplayer(event.isLocal);
|
||||||
metaData.setServerName(worldName);
|
metaData.setServerName(worldName);
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ 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 com.replaymod.core.ReplayMod;
|
||||||
|
import com.replaymod.core.utils.ModCompat;
|
||||||
import com.replaymod.replay.camera.*;
|
import com.replaymod.replay.camera.*;
|
||||||
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
|
import com.replaymod.replay.gui.overlay.GuiMarkerTimeline;
|
||||||
|
import com.replaymod.replay.gui.screen.GuiModCompatWarning;
|
||||||
import com.replaymod.replay.handler.GuiHandler;
|
import com.replaymod.replay.handler.GuiHandler;
|
||||||
import com.replaymod.replaystudio.data.Marker;
|
import com.replaymod.replaystudio.data.Marker;
|
||||||
import com.replaymod.replaystudio.replay.ReplayFile;
|
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||||
@@ -165,6 +167,17 @@ public class ReplayModReplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startReplay(ReplayFile replayFile) throws IOException {
|
public void startReplay(ReplayFile replayFile) throws IOException {
|
||||||
|
startReplay(replayFile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startReplay(ReplayFile replayFile, boolean checkModCompat) throws IOException {
|
||||||
|
if (checkModCompat) {
|
||||||
|
ModCompat.ModInfoDifference modDifference = new ModCompat.ModInfoDifference(replayFile.getModInfo());
|
||||||
|
if (!modDifference.getMissing().isEmpty() || !modDifference.getDiffering().isEmpty()) {
|
||||||
|
new GuiModCompatWarning(this, replayFile, modDifference).display();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
replayHandler = new ReplayHandler(replayFile, true);
|
replayHandler = new ReplayHandler(replayFile, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.replaymod.replay.gui.screen;
|
||||||
|
|
||||||
|
import com.replaymod.core.utils.ModCompat;
|
||||||
|
import com.replaymod.replay.ReplayModReplay;
|
||||||
|
import com.replaymod.replaystudio.data.ModInfo;
|
||||||
|
import com.replaymod.replaystudio.replay.ReplayFile;
|
||||||
|
import com.replaymod.replaystudio.util.I18n;
|
||||||
|
import de.johni0702.minecraft.gui.container.AbstractGuiScreen;
|
||||||
|
import de.johni0702.minecraft.gui.container.GuiPanel;
|
||||||
|
import de.johni0702.minecraft.gui.container.GuiVerticalList;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiButton;
|
||||||
|
import de.johni0702.minecraft.gui.element.GuiLabel;
|
||||||
|
import de.johni0702.minecraft.gui.layout.CustomLayout;
|
||||||
|
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
|
||||||
|
import de.johni0702.minecraft.gui.layout.VerticalLayout;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class GuiModCompatWarning extends AbstractGuiScreen<GuiModCompatWarning> {
|
||||||
|
public final GuiVerticalList content = new GuiVerticalList(this).setDrawShadow(true).setDrawSlider(true);
|
||||||
|
public final GuiButton loadButton = new GuiButton().setI18nLabel("replaymod.gui.load").setSize(200, 20);
|
||||||
|
public final GuiButton cancelButton = new GuiButton().setI18nLabel("gui.cancel").setSize(200, 20);
|
||||||
|
public final GuiPanel closeButtons = new GuiPanel(this).setLayout(new HorizontalLayout().setSpacing(5))
|
||||||
|
.addElements(null, loadButton, cancelButton);
|
||||||
|
|
||||||
|
{
|
||||||
|
setTitle(new GuiLabel().setI18nText("replaymod.gui.modwarning.title"));
|
||||||
|
setLayout(new CustomLayout<GuiModCompatWarning>() {
|
||||||
|
@Override
|
||||||
|
protected void layout(GuiModCompatWarning container, int width, int height) {
|
||||||
|
pos(content, 10, 35);
|
||||||
|
pos(closeButtons, width / 2 - width(closeButtons) / 2, height - 10 - height(closeButtons));
|
||||||
|
size(content, width - 20, y(closeButtons) - 10 - y(content));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
content.getListLayout().setSpacing(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuiModCompatWarning(ReplayModReplay mod, ReplayFile replayFile, ModCompat.ModInfoDifference difference) {
|
||||||
|
VerticalLayout.Data data = new VerticalLayout.Data(0.5);
|
||||||
|
GuiPanel content = this.content.getListPanel();
|
||||||
|
content.addElements(data, new GuiLabel().setI18nText("replaymod.gui.modwarning.message1"));
|
||||||
|
content.addElements(data, new GuiLabel().setI18nText("replaymod.gui.modwarning.message2"));
|
||||||
|
content.addElements(data, new GuiLabel().setI18nText("replaymod.gui.modwarning.message3"));
|
||||||
|
|
||||||
|
if (!difference.getMissing().isEmpty()) {
|
||||||
|
content.addElements(data, new GuiLabel());
|
||||||
|
content.addElements(data, new GuiLabel().setI18nText("replaymod.gui.modwarning.missing"));
|
||||||
|
for (ModInfo modInfo : difference.getMissing()) {
|
||||||
|
content.addElements(data, new GuiPanel().setLayout(new VerticalLayout().setSpacing(3)).addElements(null,
|
||||||
|
GuiPanel.builder().layout(new HorizontalLayout().setSpacing(3))
|
||||||
|
.with(new GuiLabel().setI18nText("replaymod.gui.modwarning.name"), null)
|
||||||
|
.with(new GuiLabel().setText(modInfo.getName()), null).build(),
|
||||||
|
GuiPanel.builder().layout(new HorizontalLayout().setSpacing(3))
|
||||||
|
.with(new GuiLabel().setI18nText("replaymod.gui.modwarning.id"), null)
|
||||||
|
.with(new GuiLabel().setText(modInfo.getId()), null).build(),
|
||||||
|
new GuiLabel().setText(I18n.format("replaymod.gui.modwarning.version.expected")
|
||||||
|
+ ": " + modInfo.getVersion())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!difference.getDiffering().isEmpty()) {
|
||||||
|
content.addElements(data, new GuiLabel());
|
||||||
|
content.addElements(data, new GuiLabel().setI18nText("replaymod.gui.modwarning.version"));
|
||||||
|
for (Map.Entry<ModInfo, String> entry : difference.getDiffering().entrySet()) {
|
||||||
|
content.addElements(data, new GuiPanel().setLayout(new VerticalLayout().setSpacing(3)).addElements(null,
|
||||||
|
GuiPanel.builder().layout(new HorizontalLayout().setSpacing(3))
|
||||||
|
.with(new GuiLabel().setI18nText("replaymod.gui.modwarning.name"), null)
|
||||||
|
.with(new GuiLabel().setText(entry.getKey().getName()), null).build(),
|
||||||
|
GuiPanel.builder().layout(new HorizontalLayout().setSpacing(3))
|
||||||
|
.with(new GuiLabel().setI18nText("replaymod.gui.modwarning.id"), null)
|
||||||
|
.with(new GuiLabel().setText(entry.getKey().getId()), null).build(),
|
||||||
|
new GuiLabel().setText(I18n.format("replaymod.gui.modwarning.version.expected")
|
||||||
|
+ ": " + entry.getKey().getVersion()
|
||||||
|
+ ", " + I18n.format("replaymod.gui.modwarning.version.found")
|
||||||
|
+ ": " + entry.getValue())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelButton.onClick(() -> getMinecraft().displayGuiScreen(null));
|
||||||
|
loadButton.onClick(() -> {
|
||||||
|
try {
|
||||||
|
mod.startReplay(replayFile, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GuiModCompatWarning getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -485,3 +485,15 @@ replaymod.gui.objects=Custom Objects
|
|||||||
#Errors
|
#Errors
|
||||||
replaymod.error.unknownrestriction1=This replay cannot be played with your current version.
|
replaymod.error.unknownrestriction1=This replay cannot be played with your current version.
|
||||||
replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown.
|
replaymod.error.unknownrestriction2=It tried to enforce %s which is unknown.
|
||||||
|
|
||||||
|
#Replay Mod Incompatibility Warning
|
||||||
|
replaymod.gui.modwarning.title=Incompatibility detected
|
||||||
|
replaymod.gui.modwarning.message1=A possible incompatibility between your current
|
||||||
|
replaymod.gui.modwarning.message2=Minecraft version and this Replay has been detected.
|
||||||
|
replaymod.gui.modwarning.message3=Loading this Replay may result in errors or a crash.
|
||||||
|
replaymod.gui.modwarning.name=Mod Name:
|
||||||
|
replaymod.gui.modwarning.id=Mod Id:
|
||||||
|
replaymod.gui.modwarning.missing=Missing Mods
|
||||||
|
replaymod.gui.modwarning.version=Different Mod Versions
|
||||||
|
replaymod.gui.modwarning.version.expected=Expected
|
||||||
|
replaymod.gui.modwarning.version.found=Found
|
||||||
Reference in New Issue
Block a user