package net.minecraft.util; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; /** AFTER — fixed: isCyclic passes visited set — O(E) per call, O(E²) total */ public class DependencySorter> { private final Map contents = new HashMap<>(); public DependencySorter addEntry(K id, V value) { this.contents.put(id, value); return this; } private void visitDependenciesAndElement(Multimap dependencies, Set alreadyVisited, K id, BiConsumer output) { if (!alreadyVisited.add(id)) return; dependencies.get(id).forEach(dep -> visitDependenciesAndElement(dependencies, alreadyVisited, dep, output)); V current = this.contents.get(id); if (current != null) output.accept(id, current); } // FIX: visited set prevents exponential revisiting of diamond nodes private static boolean isCyclic(Multimap directDependencies, K from, K to, Set visited) { if (!visited.add(to)) return false; // already explored — no cycle via here Collection dependencies = directDependencies.get(to); if (dependencies.contains(from)) return true; return dependencies.stream().anyMatch(dep -> isCyclic(directDependencies, from, dep, visited)); } private static void addDependencyIfNotCyclic(Multimap directDependencies, K from, K to) { if (!isCyclic(directDependencies, from, to, new HashSet<>())) directDependencies.put(from, to); } public void orderByDependencies(BiConsumer output) { HashMultimap directDependencies = HashMultimap.create(); this.contents.forEach((id, value) -> value.visitRequiredDependencies(dep -> addDependencyIfNotCyclic(directDependencies, id, dep))); this.contents.forEach((id, value) -> value.visitOptionalDependencies(dep -> addDependencyIfNotCyclic(directDependencies, id, dep))); Set alreadyVisited = new HashSet<>(); this.contents.keySet().forEach(id -> visitDependenciesAndElement(directDependencies, alreadyVisited, id, output)); } public interface Entry { void visitRequiredDependencies(Consumer consumer); void visitOptionalDependencies(Consumer consumer); } }