package io.gitlab.jfronny.inceptum.cli.commands; import io.gitlab.jfronny.commons.io.JFiles; import io.gitlab.jfronny.commons.throwable.ThrowingBiFunction; import io.gitlab.jfronny.inceptum.cli.*; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance; import io.gitlab.jfronny.inceptum.launcher.system.instance.ModManager; import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath; import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod; import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner; import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; import io.gitlab.jfronny.inceptum.launcher.util.Unchecked; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; public class ModCommand extends Command { //TODO ModAddCommand (search and install) public ModCommand() { super("Allows managing mods in instances", "", List.of("mod"), List.of( new ModListCommand(), new ModUpdateCommand(), new ModRemoveCommand() )); } @Override protected void invoke(CommandArgs args) { Utils.LOGGER.error("No known mod command was specified"); } public static class ModListCommand extends BaseInstanceCommand { private final boolean filterUpdatable; public ModListCommand() { this("Lists the mods in an instance", List.of("list", "ls"), List.of( new ModListCommand("List only updatable mods", List.of("updatable"), List.of(), true) ), false); } private ModListCommand(String help, List aliases, List subCommands, boolean filterUpdatable) { super(help, "", aliases, subCommands); this.filterUpdatable = filterUpdatable; } @Override protected void invoke(CommandArgs args, Instance instance) throws IOException { if (!instance.isFabric()) { System.err.println("This is not a fabric instance"); return; } System.out.println("Scanning installed mods, this might take a while"); instance.mds().runOnce((path, mod) -> { boolean hasSources = !mod.getMetadata().sources.isEmpty(); boolean updatable = hasSources && mod.getMetadata().sources.values().stream().anyMatch(Optional::isPresent); if (filterUpdatable && !updatable) return; System.out.println("- " + path.getFileName().toString()); System.out.println(" " + mod.getName()); for (String s : mod.getDescription()) { System.out.println(" " + s); } if (hasSources) { System.out.println(" Sources:"); for (Map.Entry> entry : mod.getMetadata().sources.entrySet()) { System.out.println(" - " + entry.getKey().getName() + " (" + entry.getKey().getVersion() + ")"); System.out.println(" Local: " + entry.getKey().getJarPath().toString()); if (entry.getValue().isPresent()) System.out.println(" Updatable to: " + entry.getValue().get().getVersion()); } } }); } } public static class ModRemoveCommand extends BaseInstanceCommand { private final boolean ignoreDependencies; public ModRemoveCommand() { this("Removes mods from an instance", List.of("remove", "delete", "rm", "del"), List.of( new ModRemoveCommand("Skip dependency checks", List.of("ignore-dependencies"), List.of(), true) ), false); } private ModRemoveCommand(String help, List aliases, List subCommands, boolean ignoreDependencies) { super(help, "...", aliases, subCommands); this.ignoreDependencies = ignoreDependencies; } @Override protected void invoke(CommandArgs args, Instance instance) throws IOException { if (!instance.isFabric()) { Utils.LOGGER.error("This is not a fabric instance"); return; } if (args.length == 0) { Utils.LOGGER.error("You must specify mods to remove"); return; } Set mods = new HashSet<>(); for (String arg : args) { Path p = instance.modsDir().resolve(arg); if (!Files.exists(p)) p = instance.modsDir().resolve(arg + ".imod"); if (!Files.exists(p)) { Utils.LOGGER.error("Nonexistant mod file: " + instance.modsDir().resolve(arg)); return; } mods.add(p); } ModsDirScanner mds = instance.mds(); if (!ignoreDependencies && !mds.isComplete()) { Utils.LOGGER.error("Scanning mods dir to search for dependencies. This might take a while"); mds.runOnce((path, mod) -> System.out.println("Scanned " + path)); } for (Path mod : mods) { try { mds.get(mod).delete(); } catch (IOException e) { Utils.LOGGER.error("Could not delete " + mod, e); return; } } } } public static class ModUpdateCommand extends BaseInstanceCommand { private final ThrowingBiFunction, IOException> pathSupplier; public ModUpdateCommand() { this("Update mods in an instance", "...", List.of("update", "upgrade", "up"), List.of( new ModUpdateCommand("Update all mods in an instance", "", List.of("all"), List.of(), (args, instancePath) -> Set.copyOf(JFiles.list(instancePath))) ), (args, instancePath) -> { Set mods = new HashSet<>(); for (String arg : args) { Path p = instancePath.resolve("mods").resolve(arg); if (!Files.exists(p)) p = instancePath.resolve("mods").resolve(arg + ".imod"); if (!Files.exists(p)) { throw new FileNotFoundException("Nonexistant mod file: " + instancePath.resolve("mods").resolve(arg)); } mods.add(p); } return mods; }); } private ModUpdateCommand(String help, String usage, List aliases, List subCommands, ThrowingBiFunction, IOException> pathSupplier) { super(help, usage, aliases, subCommands); this.pathSupplier = pathSupplier; } @Override protected void invoke(CommandArgs args, Instance instance) throws IOException { if (!instance.isFabric()) { throw new IOException("This is not a fabric instance"); } if (args.length == 0) { throw new IllegalArgumentException("You must specify mods to remove"); } Set mods = pathSupplier.apply(args, instance.path()); ModsDirScanner mds = instance.mds(); if (!mds.isComplete()) { Utils.LOGGER.error("Scanning mods dir to search for dependencies. This might take a while"); mds.runOnce((path, mod) -> System.out.println("Scanned " + path)); } for (Path mod : mods) { try { mds.get(mod).delete(); Mod md = mds.get(mod); md.getMetadata().sources.values().stream() .filter(Optional::isPresent) .map(Optional::get) .findFirst() .ifPresentOrElse(update -> { try { Utils.LOGGER.info("Updating " + mod + " to " + update.getVersion()); md.update(update); Utils.LOGGER.info("Update completed"); } catch (IOException e) { throw new Unchecked(e); } }, () -> Utils.LOGGER.error("Could not find any update for " + mod)); } catch (IOException e) { throw new IOException("Could not delete " + mod, e); } catch (Unchecked e) { throw new IOException("Could not delete " + mod, e.exception); } } } } }