Use -Dfabric.addMods for adding mods in known sources
This commit is contained in:
parent
320ffac699
commit
56e7b0194b
|
@ -1,7 +1,7 @@
|
|||
.gradle/
|
||||
.idea/
|
||||
build/
|
||||
run/
|
||||
run*/
|
||||
imgui.ini
|
||||
hs_err_pid*
|
||||
inceptum.log
|
|
@ -32,7 +32,6 @@ public class WriteMetadataStep implements Step {
|
|||
logs/
|
||||
.mixin.out/
|
||||
.fabric/
|
||||
mods/*.jar
|
||||
*.lock
|
||||
eula.txt""");
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ 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 org.eclipse.jgit.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -21,53 +22,60 @@ public class ModDescription {
|
|||
public List<String> dependents; // by file name
|
||||
public List<String> dependencies; // by file name
|
||||
|
||||
public void hydrateUpdates(String gameVersion) {
|
||||
public void initialize(String gameVersion) {
|
||||
boolean modrinth = false;
|
||||
boolean curseforge = false;
|
||||
for (ModSource source : sources.keySet().toArray(ModSource[]::new)) {
|
||||
if (source instanceof ModrinthModSource) modrinth = true;
|
||||
if (source instanceof CurseforgeModSource) curseforge = true;
|
||||
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)) {
|
||||
if (!modrinth) {
|
||||
try {
|
||||
res.addSource(new ModrinthModSource(ModrinthApi.getVersionByHash(sha1).id), gameVersion);
|
||||
addSource(new ModrinthModSource(ModrinthApi.getVersionByHash(sha1).id), gameVersion);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
if (knownSource.isEmpty() || !(knownSource.get() instanceof CurseforgeModSource)) {
|
||||
if (!curseforge) {
|
||||
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);
|
||||
addSource(new CurseforgeModSource(f.id, f.file.id), gameVersion);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ModDescription of(Path mod) {
|
||||
ModDescription res = new ModDescription();
|
||||
res.sources = new LinkedHashMap<>();
|
||||
try {
|
||||
byte[] data = Files.readAllBytes(mod);
|
||||
res.sha1 = HashUtils.sha1(data);
|
||||
res.murmur2 = HashUtils.murmur2(data);
|
||||
} catch (IOException e) {
|
||||
Inceptum.LOGGER.error("Could not read file hash", e);
|
||||
}
|
||||
res.dependents = new ArrayList<>();
|
||||
res.dependencies = new ArrayList<>();
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ModDescription of(String sha1, String murmur2, @Nullable 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<>();
|
||||
if (knownSource != null) res.addSource(knownSource, gameVersion);
|
||||
res.initialize(gameVersion);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ public final class CurseforgeModSource implements ModSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ModDownload download(Path modsDir) throws IOException {
|
||||
Path path = modsDir.resolve(current.fileName);
|
||||
public ModDownload download() throws IOException {
|
||||
Path path = getJarPath();
|
||||
Utils.downloadFile(current.downloadUrl, path);
|
||||
byte[] data = Files.readAllBytes(path);
|
||||
return new ModDownload(HashUtils.sha1(data), HashUtils.murmur2(data), path);
|
||||
|
@ -65,7 +65,12 @@ public final class CurseforgeModSource implements ModSource {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "CurseForge/" + mod.name + '/' + current.displayName;
|
||||
return "curseforge/" + (mod.slug == null ? mod.id : mod.slug) + '/' + current.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return current.fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,8 +15,8 @@ public record DirectModSource(String fileName, String url, Set<ModSource> depend
|
|||
}
|
||||
|
||||
@Override
|
||||
public ModDownload download(Path modsDir) throws IOException {
|
||||
Path p = modsDir.resolve(fileName);
|
||||
public ModDownload download() throws IOException {
|
||||
Path p = getJarPath();
|
||||
Utils.downloadFile(url, p); //TODO test
|
||||
byte[] data = Files.readAllBytes(p);
|
||||
return new ModDownload(HashUtils.sha1(data), HashUtils.murmur2(data), p);
|
||||
|
@ -39,7 +39,12 @@ public record DirectModSource(String fileName, String url, Set<ModSource> depend
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Direct";
|
||||
return "direct/" + (url.contains(":") ? url.split(":")[1] : url).replaceAll("^/+", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
package io.gitlab.jfronny.inceptum.model.inceptum.source;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.Inceptum;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ModSource {
|
||||
ModDownload download(Path modsDir) throws IOException;
|
||||
ModDownload download() throws IOException;
|
||||
Set<ModSource> getDependencies() throws IOException;
|
||||
Optional<ModSource> getUpdate(String gameVersion) throws IOException;
|
||||
String getVersion();
|
||||
String getName();
|
||||
String getFileName();
|
||||
boolean equals(ModSource other);
|
||||
default Path getJarPath() {
|
||||
return Inceptum.LIBRARIES_DIR.resolve("com").resolve(getName()).resolve(getFileName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ public final class ModrinthModSource implements ModSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ModDownload download(Path modsDir) throws IOException {
|
||||
public ModDownload download() throws IOException {
|
||||
ModrinthVersion.File file = current.files.get(0);
|
||||
Path path = modsDir.resolve(file.filename);
|
||||
Path path = getJarPath();
|
||||
Utils.downloadFile(file.url, file.hashes.sha1, path);
|
||||
return new ModDownload(file.hashes.sha1, HashUtils.murmur2(Files.readAllBytes(path)), path);
|
||||
}
|
||||
|
@ -76,7 +76,12 @@ public final class ModrinthModSource implements ModSource {
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Modrinth/" + mod.title + '/' + current.name;
|
||||
return "modrinth/" + (mod.slug == null ? mod.id : mod.slug) + '/' + current.version_number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return current.files.get(0).filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.gitlab.jfronny.inceptum.model.inceptum.ModDescription;
|
|||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModSource;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
|
@ -39,8 +40,7 @@ public class ModsDirScanner implements Closeable {
|
|||
Set<IWModDescription> mods = new TreeSet<>();
|
||||
if (Files.isDirectory(modsDir)) {
|
||||
for (Path path : Utils.ls(modsDir)) {
|
||||
if (!path.toString().endsWith(".imod"))
|
||||
mods.add(get(path));
|
||||
mods.add(get(path));
|
||||
}
|
||||
}
|
||||
return mods;
|
||||
|
@ -51,7 +51,7 @@ public class ModsDirScanner implements Closeable {
|
|||
return descriptions.get(path);
|
||||
else {
|
||||
// not yet scanned
|
||||
return new IWModDescription(path, getGameVersion());
|
||||
return new IWModDescription(path); //TODO figure out why this causes blocking
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,41 +64,31 @@ public class ModsDirScanner implements Closeable {
|
|||
}
|
||||
for (Path mods : Utils.ls(modsDir)) {
|
||||
if (Files.isDirectory(mods)) {
|
||||
descriptions.put(mods, new IWModDescription(mods, getGameVersion()));
|
||||
descriptions.put(mods, new IWModDescription(mods));
|
||||
} else {
|
||||
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;
|
||||
try (FileSystem fs = Utils.openZipFile(mods, false)) {
|
||||
fmj = Files.exists(fs.getPath("fabric.mod.json"))
|
||||
? Optional.of(Utils.loadObject(fs.getPath("fabric.mod.json"), FabricModJson.class))
|
||||
: Optional.empty();
|
||||
}
|
||||
// mod description
|
||||
if (!Files.exists(imod.get())) {
|
||||
Utils.writeObject(imod.get(), ModDescription.of(mods, getGameVersion()));
|
||||
Utils.writeObject(imod.get(), ModDescription.of(mods));
|
||||
}
|
||||
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));
|
||||
ModDescription md = Utils.loadObject(imod.get(), ModDescription.class);
|
||||
evaluateSources(md, mods);
|
||||
descriptions.put(imod.get(), new IWModDescription(mods, Optional.of(md), getFmj(mods, md), imod));
|
||||
}
|
||||
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);
|
||||
String fn = mods.getFileName().toString();
|
||||
if (!Files.exists(mods.getParent().resolve(fn.substring(0, fn.length() - 5))))
|
||||
Files.delete(mods);
|
||||
Path modFile = mods.getParent().resolve(fn.substring(0, fn.length() - 5));
|
||||
ModDescription md = Utils.loadObject(mods, ModDescription.class);
|
||||
evaluateSources(md, modFile);
|
||||
descriptions.put(mods, new IWModDescription(mods, Optional.of(md), getFmj(modFile, md), Optional.of(mods)));
|
||||
}
|
||||
else {
|
||||
descriptions.put(mods, new IWModDescription(mods, getGameVersion()));
|
||||
descriptions.put(mods, new IWModDescription(mods));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +99,37 @@ public class ModsDirScanner implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
private void evaluateSources(ModDescription md, Path modFile) throws IOException {
|
||||
md.initialize(getGameVersion());
|
||||
boolean hasSource = false;
|
||||
for (ModSource source : md.sources.keySet()) {
|
||||
//Optional<ModSource> ms = source.getUpdate(instance.getMinecraftVersion());
|
||||
//TODO properly cache
|
||||
source.getUpdate(getGameVersion());
|
||||
if (!Files.exists(source.getJarPath())) source.download();
|
||||
hasSource = true;
|
||||
//if (ms.isEmpty()) continue;
|
||||
//if (update.isEmpty()) update = ms;
|
||||
}
|
||||
if (hasSource && Files.exists(modFile)) {
|
||||
Files.delete(modFile);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<FabricModJson> getFmj(Path modJarDefault, ModDescription md) throws IOException, URISyntaxException {
|
||||
if (!Files.exists(modJarDefault)) {
|
||||
if (md.sources.isEmpty()) {
|
||||
throw new FileNotFoundException("Mod " + modJarDefault.getFileName().toString() + " doesn't specify a source and has no file");
|
||||
}
|
||||
modJarDefault = List.copyOf(md.sources.keySet()).get(0).getJarPath();
|
||||
}
|
||||
try (FileSystem fs = Utils.openZipFile(modJarDefault, false)) {
|
||||
return Files.exists(fs.getPath("fabric.mod.json"))
|
||||
? Optional.of(Utils.loadObject(fs.getPath("fabric.mod.json"), FabricModJson.class))
|
||||
: Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
disposed = true;
|
||||
|
@ -147,8 +168,8 @@ 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, String gameVersion) {
|
||||
this(path, Files.isDirectory(path) ? Optional.empty() : Optional.of(ModDescription.of(path, gameVersion)), Optional.empty(), Optional.empty());
|
||||
public IWModDescription(Path path) {
|
||||
this(path, Files.isDirectory(path) ? Optional.empty() : Optional.of(ModDescription.of(path)), Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -100,10 +100,12 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static void downloadFile(String url, Path path) throws IOException {
|
||||
if (!Files.exists(path.getParent())) Files.createDirectories(path.getParent());
|
||||
Files.write(path, downloadData(url));
|
||||
}
|
||||
|
||||
public static void downloadFile(String url, String sha1, Path path) throws IOException {
|
||||
if (!Files.exists(path.getParent())) Files.createDirectories(path.getParent());
|
||||
Files.write(path, downloadData(url, sha1));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import io.gitlab.jfronny.inceptum.Inceptum;
|
|||
import io.gitlab.jfronny.inceptum.install.steps.DownloadLibrariesStep;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.ArtifactInfo;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.ModDescription;
|
||||
import io.gitlab.jfronny.inceptum.model.inceptum.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.model.mojang.MinecraftArgument;
|
||||
import io.gitlab.jfronny.inceptum.model.mojang.VersionInfo;
|
||||
import io.gitlab.jfronny.inceptum.model.mojang.VersionsListInfo;
|
||||
|
@ -21,12 +23,10 @@ import java.net.URISyntaxException;
|
|||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class InstanceLauncher {
|
||||
public static void launch(Path instancePath, InstanceMeta instance, LaunchType launchType, boolean restart, AuthInfo authInfo) throws LaunchException, IOException {
|
||||
|
@ -60,11 +60,29 @@ public class InstanceLauncher {
|
|||
if (instance.minMem != null) args.add("-Xms" + instance.minMem);
|
||||
if (instance.maxMem != null) args.add("-Xmx" + instance.maxMem);
|
||||
if (instance.arguments != null && instance.arguments.jvm != null) args.addAll(instance.arguments.jvm);
|
||||
// Native library path
|
||||
args.add("-Djava.library.path=" + Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath());
|
||||
// Fabric imods
|
||||
if (instance.isFabric()) {
|
||||
StringBuilder fabricAddMods = new StringBuilder("-Dfabric.addMods=");
|
||||
Path mods = instancePath.resolve("mods");
|
||||
if (Files.exists(mods)) {
|
||||
for (Path imod : Utils.ls(mods, ((Predicate<Path>) path -> path.getFileName().toString().endsWith(".imod")))) {
|
||||
String fn = imod.getFileName().toString();
|
||||
if (Files.exists(imod.getParent().resolve(fn.substring(0, fn.length() - 5))))
|
||||
continue;
|
||||
//TODO load imod from libraries via -Dfabric.addMods
|
||||
Map<ModSource, Optional<ModSource>> sources = Utils.loadObject(imod, ModDescription.class).sources;
|
||||
if (sources.isEmpty()) throw new LaunchException(".imod without attached jar contains no sources");
|
||||
fabricAddMods.append(List.copyOf(sources.keySet()).get(0).getJarPath().toAbsolutePath());
|
||||
fabricAddMods.append(File.pathSeparatorChar);
|
||||
}
|
||||
}
|
||||
args.add(fabricAddMods.substring(0, fabricAddMods.length() - 1));
|
||||
}
|
||||
// Add classpath to args
|
||||
args.add("-cp");
|
||||
args.add(classPath.toString());
|
||||
// Native library path
|
||||
args.add("-Djava.library.path=" + Inceptum.NATIVES_DIR.resolve(instance.getMinecraftVersion()).toAbsolutePath());
|
||||
// Main class
|
||||
args.add(resolveMainClass(instance, versionInfo, gameJar, launchType));
|
||||
// Game arguments
|
||||
|
|
|
@ -187,24 +187,24 @@ public class AddModWindow extends Window {
|
|||
if (value.mod().isEmpty()) continue;
|
||||
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);
|
||||
return new DownloadMeta(new ModDownload(value.mod().get().sha1, value.mod().get().murmur2, value.path()), value.mod().get(), source, modsDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
ModDownload md = ms.download(modsDir);
|
||||
ModDescription manifest = ModDescription.of(md.sha1(), md.murmur2(), Optional.of(ms), mds.getGameVersion());
|
||||
ModDownload md = ms.download();
|
||||
ModDescription manifest = ModDescription.of(md.sha1(), md.murmur2(), ms, mds.getGameVersion());
|
||||
for (ModSource dependency : ms.getDependencies()) {
|
||||
DownloadMeta depMan = download(dependency, modsDir, mds);
|
||||
depMan.description.dependents.add(md.file().getFileName().toString());
|
||||
manifest.dependencies.add(depMan.download.file().getFileName().toString());
|
||||
depMan.write();
|
||||
}
|
||||
return new DownloadMeta(md, manifest, ms);
|
||||
return new DownloadMeta(md, manifest, ms, modsDir);
|
||||
}
|
||||
|
||||
public static record DownloadMeta(ModDownload download, ModDescription description, ModSource source) {
|
||||
public static record DownloadMeta(ModDownload download, ModDescription description, ModSource source, Path modsDir) {
|
||||
public void write() throws IOException {
|
||||
Utils.writeObject(download.file().getParent().resolve(download.file().getFileName().toString() + ".imod"), description);
|
||||
Utils.writeObject(modsDir.resolve(download.file().getFileName().toString() + ".imod"), description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue