Sevaral TODOs
This commit is contained in:
parent
e6e6c5822f
commit
56a16f5ee1
|
@ -20,11 +20,11 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
//TODO mods browser
|
||||
//TODO allow instance sync through metadata
|
||||
public class Inceptum {
|
||||
public static final Path CACHE_DIR = MetaHolder.BASE_PATH.resolve("cache");
|
||||
|
@ -42,6 +42,7 @@ public class Inceptum {
|
|||
.registerTypeAdapter(OauthTokenResponse.class, new OauthTokenResponseDeserializer())
|
||||
.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
|
||||
.registerTypeAdapter(ModSource.class, new ModSourceTypeAdapter())
|
||||
.registerTypeAdapter(ModSourceMapDeserializer.modSourceMapType, new ModSourceMapDeserializer())
|
||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
|
||||
.excludeFieldsWithModifiers(Modifier.PRIVATE)
|
||||
.addSerializationExclusionStrategy(new GsonIgnoreExclusionStrategy())
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package io.gitlab.jfronny.inceptum.gson;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModSource;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ModSourceMapDeserializer implements JsonSerializer<Map<ModSource, Optional<ModSource>>>, JsonDeserializer<Map<ModSource, Optional<ModSource>>> {
|
||||
public static final Type modSourceMapType = new TypeToken<Map<ModSource, Optional<ModSource>>>(){}.getType();
|
||||
@Override
|
||||
public Map<ModSource, Optional<ModSource>> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (!json.isJsonArray()) throw new JsonParseException("Not an array");
|
||||
Map<ModSource, Optional<ModSource>> res = new LinkedHashMap<>();
|
||||
for (JsonElement element : json.getAsJsonArray()) {
|
||||
res.put(context.deserialize(element, ModSource.class), Optional.empty());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Map<ModSource, Optional<ModSource>> src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonArray res = new JsonArray();
|
||||
for (ModSource source : src.keySet()) {
|
||||
res.add(context.serialize(source, ModSource.class));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ public class InstanceMeta {
|
|||
public String java;
|
||||
public Long minMem;
|
||||
public Long maxMem;
|
||||
public List<String> jvmArgsCustom; //TODO allow configuring
|
||||
public List<String> gameArgsCustom; //TODO allow configuring
|
||||
public List<String> jvmArgsCustom; //TODO allow configuring in UI
|
||||
public List<String> gameArgsCustom; //TODO allow configuring in UI
|
||||
|
||||
public boolean isFabric() {
|
||||
return version.startsWith(floaderPrefix);
|
||||
|
|
|
@ -1,13 +1,81 @@
|
|||
package io.gitlab.jfronny.inceptum.model.inceptum;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.Inceptum;
|
||||
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFingerprint;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.CurseforgeModSource;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModrinthModSource;
|
||||
import io.gitlab.jfronny.inceptum.util.HashUtils;
|
||||
import io.gitlab.jfronny.inceptum.util.api.CurseforgeApi;
|
||||
import io.gitlab.jfronny.inceptum.util.api.ModrinthApi;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class ModDescription {
|
||||
public List<ModSource> sources;
|
||||
public Map<ModSource, Optional<ModSource>> sources; //key: source, value: update
|
||||
public String sha1;
|
||||
public String murmur2;
|
||||
public List<String> dependents; // by file name
|
||||
public List<String> dependencies; // by file name
|
||||
|
||||
public void hydrateUpdates(String gameVersion) {
|
||||
for (ModSource source : sources.keySet().toArray(ModSource[]::new)) {
|
||||
addSource(source, gameVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public static ModDescription of(Path mod, String gameVersion) {
|
||||
String sha1 = null;
|
||||
String murmur2 = null;
|
||||
try {
|
||||
byte[] data = Files.readAllBytes(mod);
|
||||
sha1 = HashUtils.sha1(data);
|
||||
murmur2 = HashUtils.murmur2(data);
|
||||
} catch (IOException e) {
|
||||
Inceptum.LOGGER.error("Could not read file hash", e);
|
||||
}
|
||||
return of(sha1, murmur2, Optional.empty(), gameVersion);
|
||||
}
|
||||
|
||||
public static ModDescription of(String sha1, String murmur2, Optional<ModSource> knownSource, String gameVersion) {
|
||||
ModDescription res = new ModDescription();
|
||||
res.sources = new LinkedHashMap<>();
|
||||
res.sha1 = sha1;
|
||||
res.murmur2 = murmur2;
|
||||
res.dependents = new ArrayList<>();
|
||||
res.dependencies = new ArrayList<>();
|
||||
knownSource.ifPresent(modSource -> res.addSource(modSource, gameVersion));
|
||||
if (knownSource.isEmpty() || !(knownSource.get() instanceof ModrinthModSource)) {
|
||||
try {
|
||||
res.addSource(new ModrinthModSource(ModrinthApi.getVersionByHash(sha1).id), gameVersion);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
if (knownSource.isEmpty() || !(knownSource.get() instanceof CurseforgeModSource)) {
|
||||
try {
|
||||
CurseforgeFingerprint cf = CurseforgeApi.checkFingerprint(murmur2);
|
||||
if (!cf.exactMatches.isEmpty()) {
|
||||
CurseforgeFingerprint.Mod f = cf.exactMatches.get(0);
|
||||
res.addSource(new CurseforgeModSource(f.id, f.file.id), gameVersion);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void addSource(ModSource source, String gameVersion) {
|
||||
try {
|
||||
sources.put(source, source.getUpdate(gameVersion));
|
||||
} catch (IOException e) {
|
||||
Inceptum.LOGGER.error("Could not check " + source.getName() + " for updates", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,18 +18,19 @@ public final class CurseforgeModSource implements ModSource {
|
|||
private final int projectId;
|
||||
private final int fileId;
|
||||
private final CurseforgeFile current;
|
||||
private final CurseforgeMod mod;
|
||||
|
||||
public CurseforgeModSource(int projectId, int fileId) throws IOException {
|
||||
this.projectId = projectId;
|
||||
this.fileId = fileId;
|
||||
current = CurseforgeApi.getFile(projectId, fileId);
|
||||
this.current = CurseforgeApi.getFile(projectId, fileId);
|
||||
this.mod = CurseforgeApi.getMod(projectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModDownload download(Path modsDir) throws IOException {
|
||||
//TODO this doesn't work
|
||||
Path path = modsDir.resolve(current.fileName);
|
||||
Utils.downloadFile(current.downloadUrl.replace("edge.forgecdn.net", "media.forgecdn.net"), path);
|
||||
Utils.downloadFile(current.downloadUrl, path);
|
||||
byte[] data = Files.readAllBytes(path);
|
||||
return new ModDownload(HashUtils.sha1(data), HashUtils.murmur2(data), path);
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ public final class CurseforgeModSource implements ModSource {
|
|||
@Override
|
||||
public Optional<ModSource> getUpdate(String gameVersion) throws IOException {
|
||||
//TODO test
|
||||
for (CurseforgeMod.GameVersionLatestFile file : CurseforgeApi.getMod(projectId).gameVersionLatestFiles) {
|
||||
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
|
||||
if (file.gameVersion.equals(gameVersion)) {
|
||||
return file.projectFileId == fileId
|
||||
? Optional.empty()
|
||||
|
@ -62,6 +63,11 @@ public final class CurseforgeModSource implements ModSource {
|
|||
return current.displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "CurseForge/" + mod.name + '/' + current.displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(ModSource other) {
|
||||
return other instanceof CurseforgeModSource cu && cu.projectId == projectId && cu.fileId == fileId;
|
||||
|
|
|
@ -37,6 +37,11 @@ public record DirectModSource(String fileName, String url, Set<ModSource> depend
|
|||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Direct";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(ModSource other) {
|
||||
return false;
|
||||
|
|
|
@ -10,5 +10,6 @@ public interface ModSource {
|
|||
Set<ModSource> getDependencies() throws IOException;
|
||||
Optional<ModSource> getUpdate(String gameVersion) throws IOException;
|
||||
String getVersion();
|
||||
String getName();
|
||||
boolean equals(ModSource other);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.gitlab.jfronny.inceptum.model.inceptum.source;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthMod;
|
||||
import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthVersion;
|
||||
import io.gitlab.jfronny.inceptum.util.HashUtils;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
|
@ -12,14 +13,15 @@ import java.util.HashSet;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
//TODO auto-discover curseforge page
|
||||
public final class ModrinthModSource implements ModSource {
|
||||
private final String versionId;
|
||||
private final ModrinthVersion current;
|
||||
private final ModrinthMod mod;
|
||||
|
||||
public ModrinthModSource(String versionId) throws IOException {
|
||||
this.versionId = versionId;
|
||||
current = ModrinthApi.getVersion(versionId);
|
||||
this.current = ModrinthApi.getVersion(versionId);
|
||||
this.mod = ModrinthApi.getMod(getModId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,8 +48,7 @@ public final class ModrinthModSource implements ModSource {
|
|||
ModrinthVersion stable = null;
|
||||
ModrinthVersion beta = null;
|
||||
ModrinthVersion latest = null;
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(current.mod_id)) {
|
||||
//TODO sort versions
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(getModId())) {
|
||||
if (version.game_versions.contains(gameVersion) && version.loaders.contains("fabric")) {
|
||||
if (latest == null) latest = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release) {
|
||||
|
@ -73,12 +74,21 @@ public final class ModrinthModSource implements ModSource {
|
|||
return current.version_number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Modrinth/" + mod.title + '/' + current.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(ModSource other) {
|
||||
return other instanceof ModrinthModSource ms && ms.current.mod_id.equals(current.mod_id);
|
||||
return other instanceof ModrinthModSource ms && ms.getModId().equals(getModId());
|
||||
}
|
||||
|
||||
public String getVersionId() {
|
||||
return versionId;
|
||||
}
|
||||
|
||||
public String getModId() {
|
||||
return current.mod_id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.net.http.HttpRequest;
|
|||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -109,6 +110,12 @@ public class HttpUtils {
|
|||
throw new IOException("Could not send request", e);
|
||||
}
|
||||
if (res.statusCode() == 200) return res.body();
|
||||
if (res.statusCode() == 302 && url.startsWith("https://edge.forgecdn.net/") && method == Method.GET) {
|
||||
Optional<String> location = res.headers().firstValue("location");
|
||||
if (location.isPresent()) {
|
||||
return HttpUtils.get(location.get())._send(accept, responseBodyHandler);
|
||||
}
|
||||
}
|
||||
throw new IOException("Unexpected return method: " + res.statusCode() + " (URL=" + url + ")\n" + res.body());
|
||||
}
|
||||
|
||||
|
|
|
@ -24,15 +24,19 @@ public class ModsDirScanner implements Closeable {
|
|||
public ModsDirScanner(Path modsDir, InstanceMeta instance) {
|
||||
this.modsDir = modsDir;
|
||||
this.instance = instance;
|
||||
th = new Thread(this::scanTaskInternal);
|
||||
this.th = new Thread(this::scanTaskInternal);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
th.start();
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return instance.getMinecraftVersion();
|
||||
}
|
||||
|
||||
public Set<IWModDescription> getMods() throws IOException {
|
||||
Set<IWModDescription> mods = new LinkedHashSet<>();
|
||||
Set<IWModDescription> mods = new TreeSet<>();
|
||||
if (Files.isDirectory(modsDir)) {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (!path.toString().endsWith(".imod"))
|
||||
|
@ -47,7 +51,7 @@ public class ModsDirScanner implements Closeable {
|
|||
return descriptions.get(path);
|
||||
else {
|
||||
// not yet scanned
|
||||
return new IWModDescription(path);
|
||||
return new IWModDescription(path, getGameVersion());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +64,9 @@ public class ModsDirScanner implements Closeable {
|
|||
}
|
||||
for (Path mods : Utils.ls(modsDir)) {
|
||||
if (Files.isDirectory(mods)) {
|
||||
descriptions.put(mods, new IWModDescription(mods));
|
||||
descriptions.put(mods, new IWModDescription(mods, getGameVersion()));
|
||||
} else {
|
||||
if (mods.toString().endsWith(".jar")) {
|
||||
if (mods.toString().endsWith(".jar") || mods.toString().endsWith(".jar.disabled")) {
|
||||
Optional<Path> imod = Optional.of(mods.getParent().resolve(mods.getFileName() + ".imod"));
|
||||
// fabric.mod.json
|
||||
Optional<FabricModJson> fmj;
|
||||
|
@ -72,24 +76,29 @@ public class ModsDirScanner implements Closeable {
|
|||
: Optional.empty();
|
||||
}
|
||||
// mod description
|
||||
Optional<ModDescription> md = Optional.empty();
|
||||
//TODO attempt to compare versions across sources
|
||||
Optional<ModSource> update = Optional.empty();
|
||||
if (Files.exists(imod.get())) {
|
||||
try (FileSystem fs = Utils.openZipFile(mods, false)) {
|
||||
md = Optional.of(Utils.loadObject(imod.get(), ModDescription.class));
|
||||
for (ModSource source : md.get().sources) {
|
||||
Optional<ModSource> ms = source.getUpdate(instance.getMinecraftVersion());
|
||||
if (ms.isEmpty()) continue;
|
||||
if (update.isEmpty()) update = ms;
|
||||
}
|
||||
}
|
||||
if (!Files.exists(imod.get())) {
|
||||
Utils.writeObject(imod.get(), ModDescription.of(mods, getGameVersion()));
|
||||
}
|
||||
else imod = Optional.empty();
|
||||
descriptions.put(mods, new IWModDescription(mods, md, fmj, imod, update));
|
||||
Optional<ModDescription> md = Optional.of(Utils.loadObject(imod.get(), ModDescription.class));
|
||||
md.get().hydrateUpdates(getGameVersion());
|
||||
for (ModSource source : md.get().sources.keySet()) {
|
||||
//Optional<ModSource> ms = source.getUpdate(instance.getMinecraftVersion());
|
||||
//TODO properly cache
|
||||
source.getUpdate(getGameVersion());
|
||||
//if (ms.isEmpty()) continue;
|
||||
//if (update.isEmpty()) update = ms;
|
||||
}
|
||||
descriptions.put(mods, new IWModDescription(mods, md, fmj, imod));
|
||||
}
|
||||
else if (!mods.toString().endsWith(".imod")) {
|
||||
descriptions.put(mods, new IWModDescription(mods));
|
||||
else if (mods.toString().endsWith(".imod")) {
|
||||
// remove .imod if the corresponding jar doesn't exist
|
||||
//TODO ensure this is not called while downloading a pack
|
||||
String fn = mods.getFileName().toString();
|
||||
if (!Files.exists(mods.getParent().resolve(fn.substring(0, fn.length() - 5))))
|
||||
Files.delete(mods);
|
||||
}
|
||||
else {
|
||||
descriptions.put(mods, new IWModDescription(mods, getGameVersion()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +114,7 @@ public class ModsDirScanner implements Closeable {
|
|||
disposed = true;
|
||||
}
|
||||
|
||||
public static record IWModDescription(Path path, Optional<ModDescription> mod, Optional<FabricModJson> fmj, Optional<Path> imod, Optional<ModSource> update) {
|
||||
public static record IWModDescription(Path path, Optional<ModDescription> mod, Optional<FabricModJson> fmj, Optional<Path> imod) implements Comparable<IWModDescription> {
|
||||
public String getName() {
|
||||
if (fmj.isEmpty()) return path.getFileName().toString();
|
||||
String base;
|
||||
|
@ -138,8 +147,22 @@ public class ModsDirScanner implements Closeable {
|
|||
* Generates a description with only the path and no additional information
|
||||
* @param path The path of the mod entry
|
||||
*/
|
||||
public IWModDescription(Path path) {
|
||||
this(path, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty());
|
||||
public IWModDescription(Path path, String gameVersion) {
|
||||
this(path, Files.isDirectory(path) ? Optional.empty() : Optional.of(ModDescription.of(path, gameVersion)), Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new mod description based on an existing one with an adjusted path
|
||||
* @param path The new path to use
|
||||
* @param base The mod description to copy
|
||||
*/
|
||||
public IWModDescription(Path path, IWModDescription base) {
|
||||
this(path, base.mod, base.fmj, base.imod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IWModDescription o) {
|
||||
return getName().compareTo(o.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import imgui.ImGui;
|
|||
import imgui.flag.ImGuiTableFlags;
|
||||
import imgui.type.ImString;
|
||||
import io.gitlab.jfronny.inceptum.Inceptum;
|
||||
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeFingerprint;
|
||||
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeMod;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.ModDescription;
|
||||
|
@ -23,8 +22,8 @@ import java.io.IOException;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AddModWindow extends Window {
|
||||
private final ImString query = new ImString("", 128);
|
||||
|
@ -42,9 +41,9 @@ public class AddModWindow extends Window {
|
|||
}
|
||||
|
||||
private void refreshMR() throws IOException {
|
||||
//TODO move to thread maybe
|
||||
mr = ModrinthApi.search(query.get(), page, instance.getMinecraftVersion());
|
||||
cf = CurseforgeApi.search(instance.getMinecraftVersion(), query.get(), page, "Popularity");
|
||||
//TODO move to thread maybe
|
||||
//if (mr.hits.isEmpty()) page = 0;
|
||||
}
|
||||
|
||||
|
@ -80,31 +79,43 @@ public class AddModWindow extends Window {
|
|||
ImGui.tableNextColumn();
|
||||
ImGui.text(mod.description);
|
||||
ImGui.tableNextColumn();
|
||||
//TODO check if already added
|
||||
if (ImGui.button("Add##" + mod.mod_id)) {
|
||||
ModrinthVersion stable = null;
|
||||
ModrinthVersion beta = null;
|
||||
ModrinthVersion latest = null;
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(mod.mod_id)) {
|
||||
//TODO sort versions
|
||||
if (version.game_versions.contains(instance.getMinecraftVersion()) && version.loaders.contains("fabric")) {
|
||||
if (latest == null) latest = version;
|
||||
if (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release) {
|
||||
beta = version;
|
||||
}
|
||||
if (version.version_type == ModrinthVersion.VersionType.release) {
|
||||
stable = version;
|
||||
boolean alreadyPresent = false;
|
||||
for (ModsDirScanner.IWModDescription mdsMod : mds.getMods()) {
|
||||
alreadyPresent = mdsMod.mod().isPresent()
|
||||
&& mdsMod.mod().get().sources.keySet().stream()
|
||||
.anyMatch(s -> s instanceof ModrinthModSource ms
|
||||
&& ms.getModId().equals(mod.mod_id));
|
||||
if (alreadyPresent)
|
||||
break;
|
||||
}
|
||||
if (alreadyPresent) {
|
||||
ImGui.text("Installed");
|
||||
}
|
||||
else {
|
||||
if (ImGui.button("Add##" + mod.mod_id)) {
|
||||
ModrinthVersion stable = null;
|
||||
ModrinthVersion beta = null;
|
||||
ModrinthVersion latest = null;
|
||||
for (ModrinthVersion version : ModrinthApi.getVersions(mod.mod_id)) {
|
||||
if (version.game_versions.contains(instance.getMinecraftVersion()) && version.loaders.contains("fabric")) {
|
||||
if (latest == null) latest = version;
|
||||
if (beta == null && (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release)) {
|
||||
beta = version;
|
||||
}
|
||||
if (stable == null && (version.version_type == ModrinthVersion.VersionType.release)) {
|
||||
stable = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stable != null) beta = stable;
|
||||
if (beta != null) latest = beta;
|
||||
if (latest == null) {
|
||||
Inceptum.showError("No valid version could be identified for this mod", "No version found");
|
||||
}
|
||||
else {
|
||||
//TODO don't block
|
||||
download(new ModrinthModSource(latest.id), modsDir, mds).write();
|
||||
if (stable != null) beta = stable;
|
||||
if (beta != null) latest = beta;
|
||||
if (latest == null) {
|
||||
Inceptum.showError("No valid version could be identified for this mod", "No version found");
|
||||
}
|
||||
else {
|
||||
//TODO don't block
|
||||
download(new ModrinthModSource(latest.id), modsDir, mds).write();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui.sameLine();
|
||||
|
@ -125,20 +136,33 @@ public class AddModWindow extends Window {
|
|||
ImGui.tableNextColumn();
|
||||
ImGui.text(mod.summary);
|
||||
ImGui.tableNextColumn();
|
||||
//TODO check if already added
|
||||
if (ImGui.button("Add##" + mod.id)) {
|
||||
CurseforgeMod.GameVersionLatestFile latest = null;
|
||||
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
|
||||
if (file.gameVersion.equals(instance.getMinecraftVersion())) {
|
||||
if (latest == null) latest = file;
|
||||
boolean alreadyPresent = false;
|
||||
for (ModsDirScanner.IWModDescription mdsMod : mds.getMods()) {
|
||||
alreadyPresent = mdsMod.mod().isPresent()
|
||||
&& mdsMod.mod().get().sources.keySet().stream()
|
||||
.anyMatch(s -> s instanceof CurseforgeModSource ms
|
||||
&& ms.getProjectId() == mod.id);
|
||||
if (alreadyPresent)
|
||||
break;
|
||||
}
|
||||
if (alreadyPresent) {
|
||||
ImGui.text("Installed");
|
||||
}
|
||||
else {
|
||||
if (ImGui.button("Add##" + mod.id)) {
|
||||
CurseforgeMod.GameVersionLatestFile latest = null;
|
||||
for (CurseforgeMod.GameVersionLatestFile file : mod.gameVersionLatestFiles) {
|
||||
if (file.gameVersion.equals(instance.getMinecraftVersion())) {
|
||||
if (latest == null) latest = file;
|
||||
}
|
||||
}
|
||||
if (latest == null) {
|
||||
Inceptum.showError("No valid version could be identified for this mod", "No version found");
|
||||
}
|
||||
else {
|
||||
//TODO don't block
|
||||
download(new CurseforgeModSource(mod.id, latest.projectFileId), modsDir, mds).write();
|
||||
}
|
||||
}
|
||||
if (latest == null) {
|
||||
Inceptum.showError("No valid version could be identified for this mod", "No version found");
|
||||
}
|
||||
else {
|
||||
//TODO don't block
|
||||
download(new CurseforgeModSource(mod.id, latest.projectFileId), modsDir, mds).write();
|
||||
}
|
||||
}
|
||||
ImGui.sameLine();
|
||||
|
@ -154,47 +178,21 @@ public class AddModWindow extends Window {
|
|||
}
|
||||
}
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
Inceptum.LOGGER.error("Something went wrong while rendering an AddModWindow", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static DownloadMeta download(ModSource ms, Path modsDir, ModsDirScanner mds) throws IOException {
|
||||
for (ModsDirScanner.IWModDescription value : mds.getMods()) {
|
||||
if (value.mod().isEmpty()) continue;
|
||||
for (ModSource source : value.mod().get().sources) {
|
||||
for (ModSource source : value.mod().get().sources.keySet()) {
|
||||
if (ms.equals(source)) {
|
||||
return new DownloadMeta(new ModDownload(value.mod().get().sha1, value.mod().get().murmur2, value.path()), value.mod().get(), source);
|
||||
}
|
||||
}
|
||||
}
|
||||
ModDownload md = ms.download(modsDir);
|
||||
ModDescription manifest = new ModDescription();
|
||||
manifest.sha1 = md.sha1();
|
||||
manifest.murmur2 = md.murmur2();
|
||||
manifest.sources = new ArrayList<>();
|
||||
manifest.sources.add(ms);
|
||||
if (!(ms instanceof ModrinthModSource)) {
|
||||
try {
|
||||
manifest.sources.add(new ModrinthModSource(ModrinthApi.getVersionByHash(manifest.sha1).id));
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
if (!(ms instanceof CurseforgeModSource)) {
|
||||
try {
|
||||
CurseforgeFingerprint cf = CurseforgeApi.checkFingerprint(manifest.murmur2);
|
||||
if (!cf.exactMatches.isEmpty()) {
|
||||
CurseforgeFingerprint.Mod f = cf.exactMatches.get(0);
|
||||
manifest.sources.add(new CurseforgeModSource(f.id, f.file.id));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
manifest.dependents = new ArrayList<>();
|
||||
manifest.dependencies = new ArrayList<>();
|
||||
ModDescription manifest = ModDescription.of(md.sha1(), md.murmur2(), Optional.of(ms), mds.getGameVersion());
|
||||
for (ModSource dependency : ms.getDependencies()) {
|
||||
DownloadMeta depMan = download(dependency, modsDir, mds);
|
||||
depMan.description.dependents.add(md.file().getFileName().toString());
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.gitlab.jfronny.inceptum.Inceptum;
|
|||
import io.gitlab.jfronny.inceptum.InceptumGui;
|
||||
import io.gitlab.jfronny.inceptum.install.Steps;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.util.JvmUtils;
|
||||
import io.gitlab.jfronny.inceptum.util.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.util.Utils;
|
||||
|
@ -15,6 +16,9 @@ import io.gitlab.jfronny.inceptum.windows.control.InstanceManageControls;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class InstanceEditWindow extends Window {
|
||||
private final Path path;
|
||||
|
@ -83,7 +87,9 @@ public class InstanceEditWindow extends Window {
|
|||
ImGui.text("Did you know that every instance in Inceptum is a git repository?");
|
||||
ImGui.endTabItem();
|
||||
}
|
||||
//TODO update all
|
||||
//TODO update all/better updatability indicator
|
||||
//TODO there is still noticeable lag here
|
||||
//TODO drag-and-drop mods
|
||||
if (instance.isFabric() && ImGui.beginTabItem("Mods")) {
|
||||
if (!Files.exists(path.resolve("mods"))) {
|
||||
try {
|
||||
|
@ -107,9 +113,24 @@ public class InstanceEditWindow extends Window {
|
|||
ImGui.separator();
|
||||
try {
|
||||
for (ModsDirScanner.IWModDescription mod : mds.getMods()) {
|
||||
if (ImGui.button(mod.getName())) {
|
||||
selected = mod.path();
|
||||
boolean wasEnabled = !mod.path().toString().endsWith(".disabled");
|
||||
if (ImGui.checkbox("##" + mod.getName(), wasEnabled)) {
|
||||
String fName = mod.path().getFileName().toString();
|
||||
final String disabledSuffix = ".disabled";
|
||||
if (fName.endsWith(disabledSuffix))
|
||||
fName = fName.substring(0, fName.length() - disabledSuffix.length());
|
||||
if (wasEnabled) fName += disabledSuffix;
|
||||
Path newSel = mod.path().getParent().resolve(fName);
|
||||
try {
|
||||
Files.move(mod.path(), newSel);
|
||||
if (mod.path().equals(selected)) selected = newSel;
|
||||
mod = new ModsDirScanner.IWModDescription(newSel, mod);
|
||||
} catch (IOException e) {
|
||||
Inceptum.showError("Could not change disabled state", e);
|
||||
}
|
||||
}
|
||||
ImGui.sameLine();
|
||||
if (ImGui.button(mod.getName())) selected = mod.path();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -126,15 +147,32 @@ public class InstanceEditWindow extends Window {
|
|||
for (String s : md.getDescription()) {
|
||||
ImGui.text(s);
|
||||
}
|
||||
if (md.update().isPresent() && ImGui.button("Update to " + md.update().get().getVersion())) {
|
||||
try {
|
||||
AddModWindow.DownloadMeta dm = AddModWindow.download(md.update().get(), path.resolve("mods"), mds);
|
||||
dm.write();
|
||||
Files.delete(md.path());
|
||||
if (md.imod().isPresent() && Files.exists(md.imod().get())) Files.delete(md.imod().get());
|
||||
selected = dm.download().file();
|
||||
} catch (IOException e) {
|
||||
Inceptum.showError("Update failed", e);
|
||||
ImGui.separator();
|
||||
if (md.mod().isPresent()) {
|
||||
Map<ModSource, Optional<ModSource>> sources = md.mod().get().sources;
|
||||
ImGui.text("Sources:");
|
||||
if (sources.isEmpty())
|
||||
ImGui.bulletText("Local Drive");
|
||||
else {
|
||||
for (Map.Entry<ModSource, Optional<ModSource>> source : sources.entrySet()) {
|
||||
ImGui.bulletText(source.getKey().getName());
|
||||
Optional<ModSource> ms = source.getValue();
|
||||
if (ms.isPresent()) {
|
||||
ImGui.sameLine();
|
||||
if (ImGui.button("Update to " + ms.get().getVersion())) {
|
||||
try {
|
||||
AddModWindow.DownloadMeta dm = AddModWindow.download(ms.get(), path.resolve("mods"), mds);
|
||||
dm.write();
|
||||
Files.delete(md.path());
|
||||
if (md.imod().isPresent() && Files.exists(md.imod().get()))
|
||||
Files.delete(md.imod().get());
|
||||
selected = dm.download().file();
|
||||
} catch (IOException e) {
|
||||
Inceptum.showError("Update failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui.button("Delete")) {
|
||||
|
|
Loading…
Reference in New Issue