Implement importing (untested)
This commit is contained in:
parent
3480e0d965
commit
92109d353e
|
@ -13,6 +13,7 @@ public class CliMain {
|
|||
new LaunchCommand(),
|
||||
new ListCommand(),
|
||||
new ModCommand(),
|
||||
new ImportCommand(),
|
||||
new ExportCommand(),
|
||||
new JvmStateCommand(),
|
||||
new BatchCommand()
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package io.gitlab.jfronny.inceptum.cli.commands;
|
||||
|
||||
import io.gitlab.jfronny.commons.log.OutputColors;
|
||||
import io.gitlab.jfronny.inceptum.cli.Command;
|
||||
import io.gitlab.jfronny.inceptum.cli.CommandArgs;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.importer.Importers;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
public class ImportCommand extends Command {
|
||||
public ImportCommand() {
|
||||
super("Import a CurseForge, Modrinth or MultiMC instance", "<pack file>", List.of("import"), List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invoke(CommandArgs args) throws Exception {
|
||||
if (args.length == 0) throw new IllegalAccessException("You must specify a pack file");
|
||||
if (args.length != 1) throw new IllegalAccessException("Too many arguments");
|
||||
ProcessState state = new ProcessState();
|
||||
String name = Importers.importPack(Paths.get(args.get(0)), state).getFileName().toString();
|
||||
System.out.println(OutputColors.GREEN_BOLD + "Imported as " + name + OutputColors.RESET);
|
||||
}
|
||||
}
|
|
@ -15,18 +15,21 @@ import io.gitlab.jfronny.inceptum.imgui.window.Window;
|
|||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
public class GuiMain {
|
||||
|
@ -246,4 +249,33 @@ public class GuiMain {
|
|||
if (InceptumConfig.darkTheme) ImGui.styleColorsDark();
|
||||
else ImGui.styleColorsLight();
|
||||
}
|
||||
|
||||
public static @Nullable Path saveFileDialog(String title, String defaultName, String[] filters, String filterDescription) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
PointerBuffer filterPatterns = stack.mallocPointer(filters.length);
|
||||
for (String filter : filters) {
|
||||
filterPatterns.put(stack.UTF8(filter));
|
||||
}
|
||||
filterPatterns.flip();
|
||||
String file = TinyFileDialogs.tinyfd_saveFileDialog(title, defaultName, filterPatterns, filterDescription);
|
||||
return file == null ? null : Paths.get(file).toAbsolutePath().normalize();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Path> openFileDialog(String title, String defaultName, String[] filters, String filterDescription, boolean selectMultiple) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
PointerBuffer filterPatterns = stack.mallocPointer(filters.length);
|
||||
for (String filter : filters) {
|
||||
filterPatterns.put(stack.UTF8(filter));
|
||||
}
|
||||
filterPatterns.flip();
|
||||
String files = TinyFileDialogs.tinyfd_openFileDialog(title, defaultName, filterPatterns, filterDescription, selectMultiple);
|
||||
if (files == null) return List.of();
|
||||
return Arrays.stream(files.split("\\|"))
|
||||
.map(Paths::get)
|
||||
.map(Path::toAbsolutePath)
|
||||
.map(Path::normalize)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.gitlab.jfronny.inceptum.launcher.model.inceptum.LoaderInfo;
|
|||
import io.gitlab.jfronny.inceptum.launcher.model.mojang.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.InstanceNameTool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -42,7 +43,7 @@ public class InstanceManageControls {
|
|||
}
|
||||
}
|
||||
version.set(getVersions(snapshots.get()).indexOf(selected));
|
||||
name.set(defaultName == null ? getDefaultName(selected, fabric.get()) : defaultName);
|
||||
name.set(defaultName == null ? InstanceNameTool.getDefaultName(selected.id, fabric.get()) : defaultName);
|
||||
fabric.set(meta == null || meta.isFabric());
|
||||
List<FabricVersionLoaderInfo> versions = getFabricLoaderInfo();
|
||||
for (int i = 0, fabricLoaderInfoSize = versions.size(); i < fabricLoaderInfoSize; i++) {
|
||||
|
@ -91,20 +92,14 @@ public class InstanceManageControls {
|
|||
if (ImGui.combo("Version", version, vil.stream().map(info -> info.id).toArray(String[]::new))) {
|
||||
VersionsListInfo prev = selected;
|
||||
selected = vil.get(version.get());
|
||||
if (getDefaultName(prev, fabric.get()).equals(name.get())) {
|
||||
name.set(getDefaultName(selected, fabric.get()));
|
||||
}
|
||||
exchangeNameIfDefault(prev.id, fabric.get());
|
||||
}
|
||||
if (getFabricLoaderInfo().isEmpty()) {
|
||||
if (fabric.get() && getDefaultName(selected, true).equals(name.get())) {
|
||||
name.set(getDefaultName(selected, false));
|
||||
}
|
||||
fabric.set(false);
|
||||
exchangeNameIfDefault(selected.id, true);
|
||||
} else {
|
||||
if (ImGui.checkbox("Fabric support", fabric)) {
|
||||
if (getDefaultName(selected, !fabric.get()).equals(name.get())) {
|
||||
name.set(getDefaultName(selected, fabric.get()));
|
||||
}
|
||||
exchangeNameIfDefault(selected.id, !fabric.get());
|
||||
}
|
||||
if (fabric.get()) {
|
||||
ImGui.sameLine();
|
||||
|
@ -122,6 +117,18 @@ public class InstanceManageControls {
|
|||
}
|
||||
}
|
||||
|
||||
private void exchangeNameIfDefault(String id, boolean fabric) {
|
||||
if (InstanceNameTool.getDefaultName(id, fabric).equals(InstanceNameTool.namePart(this.name.get()))) {
|
||||
String defName = InstanceNameTool.getDefaultName(this.selected.id, this.fabric.get());
|
||||
try {
|
||||
defName = InstanceNameTool.getNextValid(defName);
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not find valid number suffix", e);
|
||||
}
|
||||
name.set(defName);
|
||||
}
|
||||
}
|
||||
|
||||
public VersionInfo getVersionInfo() throws IOException {
|
||||
return VERSION_INFO_CACHE.get(Tuple.of(selected.id, fabric.get() ? selectedFabric.loader.version : ""), () -> {
|
||||
VersionInfo vi = McApi.getVersionInfo(selected);
|
||||
|
@ -141,10 +148,6 @@ public class InstanceManageControls {
|
|||
return res;
|
||||
}
|
||||
|
||||
private String getDefaultName(VersionsListInfo info, boolean fabric) {
|
||||
return fabric ? "Fabric " + info.id : info.id;
|
||||
}
|
||||
|
||||
private List<FabricVersionLoaderInfo> getFabricLoaderInfo() {
|
||||
return LOADER_INFO_CACHE.get(selected, () -> FabricMetaApi.getLoaderVersions(selected));
|
||||
}
|
||||
|
|
|
@ -3,11 +3,18 @@ package io.gitlab.jfronny.inceptum.imgui.window;
|
|||
import imgui.ImGui;
|
||||
import io.gitlab.jfronny.inceptum.common.R;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
|
||||
import io.gitlab.jfronny.inceptum.imgui.control.InstanceManageControls;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.dialog.ProcessStateWatcherWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.importer.Importers;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.install.SetupStepInfo;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class NewInstanceWindow extends Window {
|
||||
private final InstanceManageControls imc = new InstanceManageControls(null, null);
|
||||
|
@ -39,8 +46,19 @@ public class NewInstanceWindow extends Window {
|
|||
ImGui.text("You can also just add an instance directory and it'll be loaded automatically");
|
||||
ImGui.text("Using git to manage it is recommended if you do so");
|
||||
ImGui.spacing();
|
||||
ImGui.text("Importing CurseForge or Modrinth packs is not yet implemented");
|
||||
//TODO generic importer based on zip contents and file name ("zip", "mrpack")
|
||||
ImGui.text("Below, you can import a CurseForge, Modrinth or MultiMC pack");
|
||||
if (ImGui.button("Import")) {
|
||||
List<Path> packs = GuiMain.openFileDialog("Import Pack", null, new String[] {"*.zip", "*.mrpack"}, "Modpack", true);
|
||||
if (!packs.isEmpty()) {
|
||||
ProcessState state = new ProcessState(Importers.MAX_STEPS * packs.size(), "Initializing");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Importing", "Could not import packs", state, cToken -> {
|
||||
for (Path pack : packs) {
|
||||
Path imported = Importers.importPack(pack, state);
|
||||
LauncherEnv.showInfo(pack.getFileName() + " has been successfully imported as " + imported.getFileName(), "Imported pack");
|
||||
}
|
||||
}, null));
|
||||
}
|
||||
}
|
||||
ImGui.endTabItem();
|
||||
}
|
||||
ImGui.endTabBar();
|
||||
|
|
|
@ -10,9 +10,6 @@ import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
|||
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporters;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -33,11 +30,10 @@ public class ExportTab extends Tab {
|
|||
GuiMain.open(new TextBoxWindow("Version", "Please enter the current version of your modpack", "1.0", version -> {
|
||||
String defaultName = window.name + " " + version + " (" + exporter.getName() + ")." + exporter.getFileExtension();
|
||||
String filter = "*." + exporter.getFileExtension();
|
||||
String file = saveFileDialog("Export " + exporter.getName() + " Pack", defaultName, filter, exporter.getName() + " packs (" + filter + ")");
|
||||
if (file != null) {
|
||||
Path exportPath = GuiMain.saveFileDialog("Export " + exporter.getName() + " Pack", defaultName, new String[] {filter}, exporter.getName() + " packs (" + filter + ")");
|
||||
if (exportPath != null) {
|
||||
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
|
||||
Path exportPath = Paths.get(file);
|
||||
exporter.generate(state, window.path, window.instance, window.mds, exportPath, version);
|
||||
LauncherEnv.showInfo(window.name + " has been successfully exported to " + exportPath, "Successfully exported");
|
||||
}, null));
|
||||
|
@ -49,13 +45,4 @@ public class ExportTab extends Tab {
|
|||
ImGui.text("The mods directory scan must be completed.\nThe progress for this can be observed in the mods tab");
|
||||
}
|
||||
}
|
||||
|
||||
private String saveFileDialog(String title, String defaultName, String filter, String filterDescription) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
PointerBuffer filterPatterns = stack.mallocPointer(2);
|
||||
filterPatterns.put(stack.UTF8(filter));
|
||||
filterPatterns.flip();
|
||||
return TinyFileDialogs.tinyfd_saveFileDialog(title, defaultName, filterPatterns, filterDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,10 @@ public class CleanupFileVisitor implements FileVisitor<Path> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path path, IOException e) {
|
||||
public FileVisitResult postVisitDirectory(Path path, IOException e) throws IOException {
|
||||
if (Files.exists(path) && JFiles.list(path).isEmpty()) {
|
||||
Files.delete(path);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.Set;
|
|||
public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
|
||||
private static final String OVERRIDES_DIR_DEFAULT = "overrides";
|
||||
public CurseForgeExporter() {
|
||||
super("CurseForge", "zip", "overrides");
|
||||
super("CurseForge", "zip", OVERRIDES_DIR_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
|
@ -11,7 +10,6 @@ import io.gitlab.jfronny.inceptum.launcher.util.ignore.IgnoringWalk;
|
|||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class Exporter<Manifest> {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public class CurseForgeImporter extends Importer<CurseforgeModpackManifest> {
|
||||
public CurseForgeImporter() {
|
||||
super("CurseForge", "manifest.json", CurseforgeModpackManifest.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IntermediaryManifest validateManifest(CurseforgeModpackManifest manifest, Path sourceRoot) throws IOException {
|
||||
if (manifest.manifestVersion != 1) throw new IOException("Unsupported CurseForge modpack manifest version");
|
||||
if (!"minecraftModpack".equals(manifest.manifestType)) throw new IOException("Invalid input: not a minecraft modpack");
|
||||
if (manifest.minecraft == null) throw new IOException("Modpack missing minecraft metadata");
|
||||
if (manifest.minecraft.modLoaders == null) manifest.minecraft.modLoaders = Set.of();
|
||||
if (manifest.minecraft.version == null) throw new IOException("Modpack missing minecraft version");
|
||||
if (manifest.files == null) manifest.files = Set.of();
|
||||
|
||||
String fabric = null;
|
||||
for (CurseforgeModpackManifest.Minecraft.ModLoader loader : manifest.minecraft.modLoaders) {
|
||||
final String idPrefix = "fabric-";
|
||||
if (!loader.id.startsWith(idPrefix)) throw new IOException("Unsupported mod loader");
|
||||
fabric = loader.id.substring(idPrefix.length());
|
||||
}
|
||||
return new IntermediaryManifest(manifest.name, manifest.overrides, manifest.minecraft.version, fabric);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadMods(CurseforgeModpackManifest manifest, Path instanceDir, ProcessState state) throws IOException {
|
||||
Path modsPath = instanceDir.resolve("mods");
|
||||
for (CurseforgeModpackManifest.File file : manifest.files) {
|
||||
if (!file.required) continue;
|
||||
CurseforgeModSource source = new CurseforgeModSource(file.projectID, file.fileID);
|
||||
state.updateStep("Downloading " + source.getName());
|
||||
ModDownload download = source.download();
|
||||
ModDescription imod = ModDescription.of(download.sha1(), download.murmur2(), source, manifest.minecraft.version);
|
||||
Files.createDirectories(modsPath);
|
||||
JFiles.writeObject(modsPath.resolve(source.getShortName() + ModPath.EXT_IMOD), imod);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class Importer<T> {
|
||||
private final String name;
|
||||
private final String manifestFile;
|
||||
private final Class<T> manifestClass;
|
||||
|
||||
public Importer(String name, String manifestFile, Class<T> manifestClass) {
|
||||
this.name = name;
|
||||
this.manifestFile = manifestFile;
|
||||
this.manifestClass = manifestClass;
|
||||
}
|
||||
|
||||
public boolean canImport(Path root) {
|
||||
return Files.exists(root.resolve(manifestFile));
|
||||
}
|
||||
|
||||
protected abstract IntermediaryManifest validateManifest(T manifest, Path sourceRoot) throws IOException;
|
||||
protected abstract void downloadMods(T manifest, Path instanceDir, ProcessState state) throws IOException;
|
||||
|
||||
private String createVersionString(String gameVersion, @Nullable String fabricVersion) throws IOException {
|
||||
if (McApi.getVersions().versions.stream().filter(v -> v.id.equals(gameVersion)).findFirst().isEmpty()) {
|
||||
throw new IOException("Invalid minecraft version: " + gameVersion);
|
||||
}
|
||||
if (fabricVersion == null) {
|
||||
return gameVersion;
|
||||
} else {
|
||||
FabricMetaApi.getLoaderVersion(gameVersion, fabricVersion);
|
||||
return InstanceMeta.floaderPrefix + fabricVersion + '-' + gameVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public Path importPack(Path sourceRoot, ProcessState state) throws IOException {
|
||||
state.incrementStep("Generating skeleton");
|
||||
T manifest = JFiles.readObject(sourceRoot.resolve(manifestFile), manifestClass);
|
||||
IntermediaryManifest man = validateManifest(manifest, sourceRoot);
|
||||
String name = man.name();
|
||||
if (name == null || !Utils.VALID_FILENAME.matcher(name).matches()) name = "New Pack";
|
||||
name = InstanceNameTool.getNextValid(name);
|
||||
final Path iDir = MetaHolder.INSTANCE_DIR.resolve(name).toAbsolutePath().normalize();
|
||||
InstanceLock.setSetupLock(iDir, true);
|
||||
InstanceMeta meta = new InstanceMeta();
|
||||
meta.version = createVersionString(man.gameVersion(), man.fabricVersion());
|
||||
JFiles.writeObject(iDir.resolve("instance.json"), meta);
|
||||
|
||||
state.incrementStep("Downloading mods");
|
||||
downloadMods(manifest, iDir, state);
|
||||
|
||||
state.incrementStep("Adding overrides");
|
||||
if (man.overridesPath() != null) {
|
||||
Path overridesPath = sourceRoot.resolve(man.overridesPath());
|
||||
JFiles.listTo(overridesPath, path -> {
|
||||
JFiles.copyRecursive(path, iDir.resolve(path.getFileName().toString()));
|
||||
});
|
||||
}
|
||||
InstanceLock.setSetupLock(iDir, false);
|
||||
Steps.reDownload(iDir, state);
|
||||
return iDir;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class Importers {
|
||||
public static final int MAX_STEPS = Steps.STEPS.size() + 3;
|
||||
public static final CurseForgeImporter CURSE_FORGE = new CurseForgeImporter();
|
||||
public static final ModrinthImporter MODRINTH = new ModrinthImporter();
|
||||
public static final MultiMCImporter MULTI_MC = new MultiMCImporter();
|
||||
public static final List<Importer<?>> IMPORTERS = List.of(CURSE_FORGE, MODRINTH, MULTI_MC);
|
||||
|
||||
public static Path importPack(Path zipPath, ProcessState state) throws IOException {
|
||||
try (FileSystem fs = Utils.openZipFile(zipPath, false)) {
|
||||
for (Importer<?> importer : IMPORTERS) {
|
||||
if (importer.canImport(fs.getPath("."))) {
|
||||
return importer.importPack(fs.getPath("."), state);
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IOException("Could not open zip", e);
|
||||
}
|
||||
throw new IOException("Could not import pack: unsupported format");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record IntermediaryManifest(String name, @Nullable String overridesPath, String gameVersion, @Nullable String fabricVersion) {
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.Net;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.modrinth.ModrinthModpackManifest;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ModrinthImporter extends Importer<ModrinthModpackManifest> {
|
||||
public ModrinthImporter() {
|
||||
super("Modrinth", "modrinth.index.json", ModrinthModpackManifest.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IntermediaryManifest validateManifest(ModrinthModpackManifest manifest, Path sourceRoot) throws IOException {
|
||||
if (manifest.formatVersion != 1) throw new IOException("Unsupported Modrinth modpack manifest version");
|
||||
if (!"minecraft".equals(manifest.game)) throw new IOException("Invalid input: not a minecraft modpack");
|
||||
if (manifest.files == null) manifest.files = new ArrayList<>();
|
||||
if (manifest.dependencies == null) manifest.dependencies = new ModrinthModpackManifest.Dependencies();
|
||||
if (manifest.dependencies.minecraft == null) throw new IOException("Could not find minecraft version");
|
||||
if (manifest.dependencies.fabricLoader == null) {
|
||||
for (ModrinthModpackManifest.File file : manifest.files) {
|
||||
if (file.path == null) {
|
||||
throw new IOException("File lacks path");
|
||||
}
|
||||
if (file.path.startsWith("mods")) {
|
||||
throw new IOException("Found mod files but no fabric loader. Other loaders are unsupported!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return new IntermediaryManifest(manifest.name, "overrides", manifest.dependencies.minecraft, manifest.dependencies.fabricLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadMods(ModrinthModpackManifest manifest, Path instanceDir, ProcessState state) throws IOException {
|
||||
filesLoop: for (ModrinthModpackManifest.File file : manifest.files) {
|
||||
Path path = instanceDir.getParent().resolve(file.path).toAbsolutePath().normalize();
|
||||
if (!path.startsWith(instanceDir)) throw new IOException("Pack attempted to write file outside instance. This is unsupported!");
|
||||
String sha1 = file.hashes.sha1;
|
||||
IOException failedDownload = new IOException("Could not download file");
|
||||
for (String url : file.downloads) {
|
||||
try {
|
||||
Net.downloadFile(url, sha1, path);
|
||||
continue filesLoop;
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
failedDownload.addSuppressed(e);
|
||||
}
|
||||
}
|
||||
throw failedDownload;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.importer;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MultiMCImporter extends Importer<MMCPackMeta> {
|
||||
public MultiMCImporter() {
|
||||
super("MultiMC", "mmc-pack.json", MMCPackMeta.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IntermediaryManifest validateManifest(MMCPackMeta manifest, Path sourceRoot) throws IOException {
|
||||
if (manifest.formatVersion != 1) throw new IOException("Unsupported MultiMC format version");
|
||||
if (manifest.components == null) throw new IOException("MultiMC pack missing components");
|
||||
String gameVersion = null;
|
||||
String fabricVersion = null;
|
||||
String name = null;
|
||||
Path instanceCfg = sourceRoot.resolve("instance.cfg");
|
||||
if (Files.exists(instanceCfg)) {
|
||||
try (Stream<String> lines = Files.lines(instanceCfg)) {
|
||||
final String namePrefix = "name=";
|
||||
name = lines.filter(l -> l.startsWith(namePrefix))
|
||||
.map(l -> l.substring(namePrefix.length()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
for (MMCPackMeta.Component component : manifest.components) {
|
||||
if ("net.minecraft".equals(component.uid)) gameVersion = component.version;
|
||||
if ("net.fabricmc.fabric-loader".equals(component.uid)) fabricVersion = component.version;
|
||||
}
|
||||
if (gameVersion == null) throw new IOException("Pack lacks minecraft component");
|
||||
return new IntermediaryManifest(name, ".minecraft", gameVersion, fabricVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadMods(MMCPackMeta manifest, Path instanceDir, ProcessState state) throws IOException {
|
||||
// All mods are overrides, so do nothing
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ public record FileScanTask(Path file, BiConsumer<Path, IWModDescription> discove
|
|||
}
|
||||
if (ModPath.isImod(file)) {
|
||||
String fn = file.getFileName().toString();
|
||||
Path modFile = file.getParent().resolve(fn.substring(0, fn.length() - 5));
|
||||
Path modFile = file.getParent().resolve(fn.substring(0, fn.length() - ModPath.EXT_IMOD.length()));
|
||||
final var imod = new Object() {
|
||||
Path i = file;
|
||||
ModDescription md = JFiles.readObject(file, ModDescription.class);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.util;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.MetaHolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class InstanceNameTool {
|
||||
private static final Pattern NUMBER_PATTERN = Pattern.compile("^(.*)\\s*\\(\\d+\\)$");
|
||||
public static String getDefaultName(String versionId, boolean fabric) {
|
||||
return fabric ? "Fabric " + versionId : versionId;
|
||||
}
|
||||
|
||||
public static String namePart(String name) {
|
||||
Matcher matcher = NUMBER_PATTERN.matcher(name);
|
||||
return matcher.matches() ? matcher.group(1) : name;
|
||||
}
|
||||
|
||||
public static String getNextValid(String name) throws IOException {
|
||||
name = namePart(name);
|
||||
if (!Files.exists(MetaHolder.INSTANCE_DIR.resolve(name))) return name;
|
||||
long l = 0;
|
||||
String numberedName;
|
||||
do numberedName = name + " (" + ++l + ")";
|
||||
while (Files.exists(MetaHolder.INSTANCE_DIR.resolve(numberedName)));
|
||||
return numberedName;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue