package io.gitlab.jfronny.modsmod; import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken; import io.gitlab.jfronny.modsmod.util.IteratorCallbackList; import net.fabricmc.loader.impl.FabricLoaderImpl; import net.fabricmc.loader.impl.ModContainerImpl; import net.fabricmc.loader.impl.discovery.ModCandidate; import net.fabricmc.loader.impl.metadata.LoaderModMetadata; import java.lang.reflect.*; import java.nio.file.Path; import java.util.Collection; import java.util.List; public class FabricLoaderInterface { private static final Method ADD_MOD_METHOD; private static final Method CREATE_PLAIN_METHOD; private static final Field MODS_FIELD; static { try { ADD_MOD_METHOD = FabricLoaderImpl.class.getDeclaredMethod("addMod", ModCandidate.class); ADD_MOD_METHOD.setAccessible(true); MODS_FIELD = FabricLoaderImpl.class.getDeclaredField("mods"); MODS_FIELD.setAccessible(true); CREATE_PLAIN_METHOD = ModCandidate.class.getDeclaredMethod("createPlain", new TypeToken>(){}.getRawType(), LoaderModMetadata.class, boolean.class, new TypeToken>(){}.getRawType()); CREATE_PLAIN_METHOD.setAccessible(true); } catch (NoSuchMethodException | NoSuchFieldException e) { throw new IllegalStateException("Failed to get reference to fabric loader internals. This fabric loader version is probably unsupported by modsmod", e); } } public static void synchronize(FabricLoaderImpl fabricLoader) { try { MODS_FIELD.set(fabricLoader, new IteratorCallbackList<>((List) MODS_FIELD.get(fabricLoader), modContainers -> { try { MODS_FIELD.set(fabricLoader, modContainers); } catch (IllegalAccessException e) { ModsMod.LOGGER.error("Failed to reset mods field", e); } }, ModsMod::loadMods)); } catch (IllegalAccessException e) { throw new IllegalStateException("Failed to make mods list synchronized", e); } } public static void addMod(FabricLoaderImpl fabricLoader, ModCandidate modCandidate) { try { ADD_MOD_METHOD.invoke(fabricLoader, modCandidate); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Failed to inject mod into fabric loader", e); } } public static ModCandidate createPlain(Path path, LoaderModMetadata metadata, boolean requiresRemap, Collection nestedMods) { try { return (ModCandidate) CREATE_PLAIN_METHOD.invoke(null, List.of(path), metadata, requiresRemap, nestedMods); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Failed to create plain mod container", e); } } }