package io.gitlab.jfronny.inceptum.launcher.model.inceptum; import io.gitlab.jfronny.commons.HashUtils; import io.gitlab.jfronny.commons.data.MutCollection; import io.gitlab.jfronny.commons.data.delegate.DelegateMap; import io.gitlab.jfronny.gson.compile.annotations.GPrefer; import io.gitlab.jfronny.gson.compile.annotations.GSerializable; import io.gitlab.jfronny.inceptum.common.GsonPreset; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi; import io.gitlab.jfronny.inceptum.launcher.api.ModrinthApi; import io.gitlab.jfronny.inceptum.launcher.gson.ModMetaSourcesAdapter; import io.gitlab.jfronny.inceptum.launcher.model.curseforge.response.FingerprintMatchesResponse; import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource; import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; import io.gitlab.jfronny.inceptum.launcher.system.source.ModrinthModSource; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Optional; @GSerializable(configure = GsonPreset.Config.class) public record ModMeta( Sources sources, //key: source, value: update String sha1, Long murmur2, List dependents, // by file name List dependencies, // by file name boolean explicit ) { @GSerializable(with = ModMetaSourcesAdapter.class, configure = GsonPreset.Config.class) public static class Sources extends DelegateMap> { public Sources() { super(MutCollection.mapOf()); } } @GPrefer public ModMeta {} public static ModMeta of(Path mod) { String sha1 = null; Long murmur2 = null; if (!Files.isDirectory(mod)) { try { byte[] data = Files.readAllBytes(mod); sha1 = HashUtils.sha1(data); murmur2 = HashUtils.murmur2(data); } catch (IOException e) { Utils.LOGGER.error("Could not read file hash", e); } } return new ModMeta( new Sources(), sha1, murmur2, new ArrayList<>(), new ArrayList<>(), true ); } public static ModMeta of(String sha1, Long murmur2, @Nullable ModSource knownSource, String gameVersion) { ModMeta res = new ModMeta( new Sources(), sha1, murmur2, new ArrayList<>(), new ArrayList<>(), true ); if (knownSource != null) res.addSource(knownSource, gameVersion); res.initialize(gameVersion); return res; } public boolean 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); } boolean changed = false; if (!modrinth) { try { addSource(new ModrinthModSource(ModrinthApi.getVersionByHash(sha1).id), gameVersion); changed = true; } catch (IOException e) { // not found } } if (!curseforge) { try { FingerprintMatchesResponse.Result cf = CurseforgeApi.checkFingerprint(murmur2); if (!cf.exactMatches.isEmpty) { FingerprintMatchesResponse.Result.Match f = cf.exactMatches[0]; addSource(new CurseforgeModSource(f.id, f.file.id), gameVersion); changed = true; } } catch (IOException | URISyntaxException e) { // not found } } return changed; } public void addSource(ModSource source, String gameVersion) { try { sources[source] = source.getUpdate(gameVersion); } catch (IOException e) { Utils.LOGGER.error("Could not check " + source.name + " for updates", e); } } }