Inceptum/launcher-cli/src/main/java/io/gitlab/jfronny/inceptum/cli/commands/ModCommand.java

193 lines
8.7 KiB
Java

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<String> aliases, List<Command> 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<ModSource, Optional<ModSource>> 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<String> aliases, List<Command> subCommands, boolean ignoreDependencies) {
super(help, "<mod file>...", 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<Path> 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<CommandArgs, Path, Set<Path>, IOException> pathSupplier;
public ModUpdateCommand() {
this("Update mods in an instance", "<mod file>...", 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<Path> 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<String> aliases, List<Command> subCommands, ThrowingBiFunction<CommandArgs, Path, Set<Path>, 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<Path> 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);
}
}
}
}
}