ModernFix-fix/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java
2023-01-06 21:57:44 -05:00

76 lines
3.4 KiB
Java

package org.embeddedt.modernfix.util;
import com.google.common.base.Stopwatch;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.ModWorkManager;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Iterates over all mods in the game, parallelizing where possible while preserving dependency ordering.
*
* Can also be given a list of mods to skip.
*/
public class OrderedParallelModDispatcher {
public static void dispatchBlocking(Consumer<String> task, Collection<String> modIDsToFilter) {
HashSet<String> finishedMods = new HashSet<>(modIDsToFilter);
HashMap<String, CompletableFuture<?>> submittedFutures = new HashMap<>();
int numMods = ModList.get().getMods().size();
Semaphore jobWaitingSemaphore = new Semaphore(0);
ArrayList<ModInfo> remainingModList = new ArrayList<>(ModList.get().getMods());
while(finishedMods.size() < numMods) {
remainingModList.removeIf(modInfo -> {
if(finishedMods.contains(modInfo.getModId()))
return true;
boolean allDependenciesLoaded = true;
for(IModInfo.ModVersion dep : modInfo.getDependencies()) {
if(dep.isMandatory() && !finishedMods.contains(dep.getModId())) {
allDependenciesLoaded = false;
break;
}
}
if(!allDependenciesLoaded)
return false;
Optional<? extends ModContainer> modContainerOpt = ModList.get().getModContainerById(modInfo.getModId());
if(!modContainerOpt.isPresent())
throw new IllegalStateException("Can't find mod container");
ModContainer container = modContainerOpt.get();
submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> {
Supplier<?> contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension");
ModLoadingContext.get().setActiveContainer(container, contextExtension.get());
task.accept(modInfo.getModId());
jobWaitingSemaphore.release();
}, ModWorkManager.parallelExecutor()));
return true;
});
try {
jobWaitingSemaphore.acquire();
} catch(InterruptedException e) {
throw new RuntimeException("Unexpected interruption", e);
}
submittedFutures.entrySet().removeIf(entry -> {
if(entry.getValue().isDone()) {
finishedMods.add(entry.getKey());
return true;
}
return false;
});
}
}
public static void dispatchBlocking(Consumer<String> task) {
dispatchBlocking(task, Collections.emptyList());
}
}