package io.gitlab.jfronny.modsmod; import io.gitlab.jfronny.libjf.Libjf; import io.gitlab.jfronny.libjf.entry.UltraEarlyInit; import net.fabricmc.loader.FabricLoader; import net.fabricmc.loader.discovery.ModCandidate; import net.fabricmc.loader.discovery.ModResolver; import net.fabricmc.loader.discovery.RuntimeModRemapper; import net.fabricmc.loader.launch.common.FabricLauncherBase; import net.fabricmc.loader.metadata.LoaderModMetadata; import net.fabricmc.loader.metadata.ModMetadataParser; import net.fabricmc.loader.metadata.ParseMetadataException; import net.fabricmc.loader.util.FileSystemUtil; import java.io.IOException; import java.net.URL; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Collections; import java.util.HashSet; public class ModsMod { static final HashSet m = new HashSet<>(); static final FabricLoader loader = FabricLoader.INSTANCE; public static final String MOD_ID = "modsmod"; private static final String CACHE_NAME = MOD_ID + "cache"; public static void prepare() throws IOException { //Load config Libjf.registerConfig(MOD_ID, Cfg.class); Path configDir = loader.getConfigDir(); Path modsmodCfgFile = Libjf.getConfigs().get(MOD_ID).path; //make sure the modsmodcache dir is ok Path path = configDir.resolve(CACHE_NAME); if (!Files.isDirectory(path)) { if (Files.exists(path)) Files.delete(path); Files.createDirectories(path); } //remove modsmodcache if the cache is outdated try { Path cfgCache = path.resolve("_basecfg"); if (Files.exists(cfgCache)) { if (Files.isRegularFile(cfgCache)) { if (!IOUtil.contentEquals(modsmodCfgFile, cfgCache)) { IOUtil.clearDirectory(path); } } else { IOUtil.clearDirectory(path); } } Files.copy(modsmodCfgFile, cfgCache, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { System.err.println("Failed to validate modsmod config cache, caching will not be available"); e.printStackTrace(); } m.clear(); //Generate mods for (int i = 0; i < Cfg.modsCount; i++) { Path f = path.resolve("f" + (i + 1) + ".jar"); boolean exists = Files.exists(f); //Do not load if cached if (exists) { if (Cfg.cache) { m.add(new ModMeta(f)); continue; } else Files.delete(f); } try (FileSystem fs = FileSystemUtil.getJarFileSystem(f, true).get()) { //META-INF/MANIFEST.MF (java jar file spec) Path inf = fs.getPath("META-INF"); Files.createDirectory(inf); StringBuilder sb = new StringBuilder(); sb.append("Manifest-Version: 1.0\n"); Files.writeString(inf.resolve("MANIFEST.MF"), sb.toString()); sb.delete(0, sb.length()); //fabric.mod.json (fabric mod metadata) sb.append("{"); sb.append("\"schemaVersion\": 1,"); sb.append("\"id\": \"modmod_").append(i + 1).append("\","); sb.append("\"version\": \"1.0\","); sb.append("\"name\": \"ModsMod ").append(i + 1).append("\","); sb.append("\"entrypoints\": {},"); sb.append("\"custom\": {"); if (Cfg.parent) { sb.append("\"modmenu\": {"); sb.append("\"parent\": \"modsmod\""); sb.append("}"); } sb.append("}"); sb.append("}"); Files.writeString(fs.getPath("fabric.mod.json"), sb.toString()); sb.delete(0, sb.length()); } catch (Throwable e) { e.printStackTrace(); } System.gc(); m.add(new ModMeta(f)); } FabricLoaderInterface.synchronize(loader); } public static void init() { for (ModMeta f : m) { loadMod(f); } } private static void loadMod(ModMeta meta) { ModCandidate candidate = parseMod(meta.fs.getPath("fabric.mod.json"), meta.url); if (loader.isDevelopmentEnvironment()) { candidate = RuntimeModRemapper.remap(Collections.singletonList(candidate), ModResolver.getInMemoryFs()).stream().findFirst().get(); } FabricLoaderInterface.addMod(loader, candidate); FabricLauncherBase.getLauncher().propose(candidate.getOriginUrl()); } private static ModCandidate parseMod(Path fabricModJson, URL originUrl) { try { LoaderModMetadata info = ModMetadataParser.parseMetadata(FabricLoaderInterface.logger, fabricModJson); return new ModCandidate(info, originUrl, 0, true); } catch (IOException | ParseMetadataException e) { throw new IllegalStateException(e); } } }