/* * Minecraft Forge * Copyright (c) 2018. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml; import com.google.common.collect.Streams; import net.minecraftforge.fml.language.ModFileScanData; import net.minecraftforge.fml.loading.DefaultModInfos; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.FutureTask; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import static net.minecraftforge.fml.Logging.LOADING; /** * Master list of all mods - game-side version. This is classloaded in the game scope and * can dispatch game level events as a result. */ public class ModList { private static Logger LOGGER = LogManager.getLogger(); private static ModList INSTANCE; private final List modFiles; private final List sortedList; private final Map fileById; private List mods; private Map indexedMods; private ForkJoinPool modLoadingThreadPool = new ForkJoinPool(); private List modFileScanData; private ModList(final List modFiles, final List sortedList) { this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).map(ModFileInfo.class::cast).collect(Collectors.toList()); this.sortedList = Streams.concat(DefaultModInfos.getModInfos().stream(), sortedList.stream()). map(ModInfo.class::cast). collect(Collectors.toList()); this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream). map(ModInfo.class::cast). collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile)); } public static ModList of(List modFiles, List sortedList) { INSTANCE = new ModList(modFiles, sortedList); return INSTANCE; } public static ModList get() { return INSTANCE; } public List getModFiles() { return modFiles; } public ModFileInfo getModFileById(String modid) { return this.fileById.get(modid); } public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.preLifecycleEvent(lifecycleEvent)); DeferredWorkQueue.deferredWorkQueue.clear(); try { modLoadingThreadPool.submit(()->this.mods.parallelStream().forEach(m->m.transitionState(lifecycleEvent))).get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error(LOADING, "Encountered an exception during parallel processing", e); } DeferredWorkQueue.deferredWorkQueue.forEach(FutureTask::run); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.postLifecycleEvent(lifecycleEvent)); final List erroredContainers = this.mods.stream().filter(m -> m.getCurrentState() == ModLoadingStage.ERROR).collect(Collectors.toList()); if (!erroredContainers.isEmpty()) { throw new RuntimeException("Errored containers found!", erroredContainers.get(0).modLoadingError.get(0)); } } public void setLoadedMods(final List modContainers) { this.mods = modContainers; this.indexedMods = modContainers.stream().collect(Collectors.toMap(ModContainer::getModId, Function.identity())); } @SuppressWarnings("unchecked") public Optional getModObjectById(String modId) { return getModContainerById(modId).map(ModContainer::getMod).map(o -> (T) o); } public Optional getModContainerById(String modId) { return Optional.ofNullable(this.indexedMods.get(modId)); } public List getMods() { return this.sortedList; } public boolean isLoaded(String modTarget) { return this.indexedMods.containsKey(modTarget); } public int size() { return mods.size(); } public List getAllScanData() { if (modFileScanData == null) { modFileScanData = this.sortedList.stream(). map(ModInfo::getOwningFile). filter(Objects::nonNull). map(ModFileInfo::getFile). map(ModFile::getScanResult). collect(Collectors.toList()); } return modFileScanData; } public void forEachModFile(Consumer fileConsumer) { modFiles.stream().map(ModFileInfo::getFile).forEach(fileConsumer); } }