208 lines
7.4 KiB
Java
208 lines
7.4 KiB
Java
package com.hypixel.hytale.component.dependency;
|
|
|
|
import com.hypixel.hytale.component.ComponentRegistry;
|
|
import com.hypixel.hytale.component.system.ISystem;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class DependencyGraph<ECS_TYPE> {
|
|
@Nonnull
|
|
private final ISystem<ECS_TYPE>[] systems;
|
|
private final Map<ISystem<ECS_TYPE>, List<DependencyGraph.Edge<ECS_TYPE>>> beforeSystemEdges = new Object2ObjectOpenHashMap();
|
|
private final Map<ISystem<ECS_TYPE>, List<DependencyGraph.Edge<ECS_TYPE>>> afterSystemEdges = new Object2ObjectOpenHashMap();
|
|
private final Map<ISystem<ECS_TYPE>, Set<DependencyGraph.Edge<ECS_TYPE>>> afterSystemUnfulfilledEdges = new Object2ObjectOpenHashMap();
|
|
private DependencyGraph.Edge<ECS_TYPE>[] edges = DependencyGraph.Edge.emptyArray();
|
|
|
|
public DependencyGraph(@Nonnull ISystem<ECS_TYPE>[] systems) {
|
|
this.systems = systems;
|
|
|
|
for (int i = 0; i < systems.length; i++) {
|
|
ISystem<ECS_TYPE> system = systems[i];
|
|
this.beforeSystemEdges.put(system, new ObjectArrayList());
|
|
this.afterSystemEdges.put(system, new ObjectArrayList());
|
|
this.afterSystemUnfulfilledEdges.put(system, new HashSet<>());
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
public ISystem<ECS_TYPE>[] getSystems() {
|
|
return this.systems;
|
|
}
|
|
|
|
public void resolveEdges(@Nonnull ComponentRegistry<ECS_TYPE> registry) {
|
|
for (ISystem<ECS_TYPE> system : this.systems) {
|
|
for (Dependency<ECS_TYPE> dependency : system.getDependencies()) {
|
|
dependency.resolveGraphEdge(registry, system, this);
|
|
}
|
|
|
|
if (system.getGroup() != null) {
|
|
for (Dependency<ECS_TYPE> dependency : system.getGroup().getDependencies()) {
|
|
dependency.resolveGraphEdge(registry, system, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (ISystem<ECS_TYPE> system : this.systems) {
|
|
if (this.afterSystemEdges.get(system).isEmpty()) {
|
|
int priority = 0;
|
|
List<DependencyGraph.Edge<ECS_TYPE>> edges = this.beforeSystemEdges.get(system);
|
|
|
|
for (DependencyGraph.Edge<ECS_TYPE> edge : edges) {
|
|
priority += edge.priority / edges.size();
|
|
}
|
|
|
|
this.addEdgeFromRoot(system, priority);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void addEdgeFromRoot(@Nonnull ISystem<ECS_TYPE> afterSystem, int priority) {
|
|
this.addEdge(new DependencyGraph.Edge<>(null, afterSystem, priority));
|
|
}
|
|
|
|
public void addEdge(@Nonnull ISystem<ECS_TYPE> beforeSystem, @Nonnull ISystem<ECS_TYPE> afterSystem, int priority) {
|
|
this.addEdge(new DependencyGraph.Edge<>(beforeSystem, afterSystem, priority));
|
|
}
|
|
|
|
public void addEdge(@Nonnull DependencyGraph.Edge<ECS_TYPE> edge) {
|
|
int index = Arrays.binarySearch(this.edges, edge);
|
|
int insertionPoint;
|
|
if (index >= 0) {
|
|
insertionPoint = index;
|
|
|
|
while (insertionPoint < this.edges.length && this.edges[insertionPoint].priority == edge.priority) {
|
|
insertionPoint++;
|
|
}
|
|
} else {
|
|
insertionPoint = -(index + 1);
|
|
}
|
|
|
|
int oldLength = this.edges.length;
|
|
int newLength = oldLength + 1;
|
|
if (oldLength < newLength) {
|
|
this.edges = Arrays.copyOf(this.edges, newLength);
|
|
}
|
|
|
|
System.arraycopy(this.edges, insertionPoint, this.edges, insertionPoint + 1, oldLength - insertionPoint);
|
|
this.edges[insertionPoint] = edge;
|
|
if (edge.beforeSystem != null) {
|
|
this.beforeSystemEdges.get(edge.beforeSystem).add(edge);
|
|
}
|
|
|
|
this.afterSystemEdges.get(edge.afterSystem).add(edge);
|
|
if (!edge.fulfilled) {
|
|
this.afterSystemUnfulfilledEdges.get(edge.afterSystem).add(edge);
|
|
}
|
|
}
|
|
|
|
public void sort(ISystem<ECS_TYPE>[] sortedSystems) {
|
|
int index = 0;
|
|
|
|
label52:
|
|
while (index < this.systems.length) {
|
|
for (DependencyGraph.Edge<ECS_TYPE> edge : this.edges) {
|
|
if (!edge.resolved && edge.fulfilled) {
|
|
ISystem<ECS_TYPE> system = edge.afterSystem;
|
|
if (this.afterSystemUnfulfilledEdges.get(system).isEmpty() && !this.hasEdgeOfLaterPriority(system, edge.priority)) {
|
|
sortedSystems[index++] = system;
|
|
this.resolveEdgesFor(system);
|
|
this.fulfillEdgesFor(system);
|
|
continue label52;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (DependencyGraph.Edge<ECS_TYPE> edgex : this.edges) {
|
|
if (!edgex.resolved && edgex.fulfilled) {
|
|
ISystem<ECS_TYPE> system = edgex.afterSystem;
|
|
if (this.afterSystemUnfulfilledEdges.get(system).isEmpty()) {
|
|
sortedSystems[index++] = system;
|
|
this.resolveEdgesFor(system);
|
|
this.fulfillEdgesFor(system);
|
|
continue label52;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new IllegalArgumentException("Found a cyclic dependency!" + this);
|
|
}
|
|
}
|
|
|
|
private boolean hasEdgeOfLaterPriority(ISystem<ECS_TYPE> system, int priority) {
|
|
for (DependencyGraph.Edge<ECS_TYPE> edge : this.afterSystemEdges.get(system)) {
|
|
if (!edge.resolved && edge.priority > priority) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void resolveEdgesFor(ISystem<ECS_TYPE> system) {
|
|
for (DependencyGraph.Edge<ECS_TYPE> edge : this.afterSystemEdges.get(system)) {
|
|
edge.resolved = true;
|
|
}
|
|
}
|
|
|
|
private void fulfillEdgesFor(ISystem<ECS_TYPE> system) {
|
|
for (DependencyGraph.Edge<ECS_TYPE> edge : this.beforeSystemEdges.get(system)) {
|
|
edge.fulfilled = true;
|
|
this.afterSystemUnfulfilledEdges.get(edge.afterSystem).remove(edge);
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public String toString() {
|
|
return "DependencyGraph{systems=" + Arrays.toString((Object[])this.systems) + ", edges=" + Arrays.toString((Object[])this.edges) + "}";
|
|
}
|
|
|
|
private static class Edge<ECS_TYPE> implements Comparable<DependencyGraph.Edge<ECS_TYPE>> {
|
|
private static final DependencyGraph.Edge<?>[] EMPTY_ARRAY = new DependencyGraph.Edge[0];
|
|
@Nullable
|
|
private final ISystem<ECS_TYPE> beforeSystem;
|
|
private final ISystem<ECS_TYPE> afterSystem;
|
|
private final int priority;
|
|
private boolean fulfilled;
|
|
private boolean resolved;
|
|
|
|
public static <ECS_TYPE> DependencyGraph.Edge<ECS_TYPE>[] emptyArray() {
|
|
return (DependencyGraph.Edge<ECS_TYPE>[])EMPTY_ARRAY;
|
|
}
|
|
|
|
public Edge(@Nullable ISystem<ECS_TYPE> beforeSystem, ISystem<ECS_TYPE> afterSystem, int priority) {
|
|
this.beforeSystem = beforeSystem;
|
|
this.afterSystem = afterSystem;
|
|
this.priority = priority;
|
|
this.fulfilled = beforeSystem == null;
|
|
}
|
|
|
|
public int compareTo(@Nonnull DependencyGraph.Edge<ECS_TYPE> o) {
|
|
return Integer.compare(this.priority, o.priority);
|
|
}
|
|
|
|
@Nonnull
|
|
@Override
|
|
public String toString() {
|
|
return "Edge{beforeSystem="
|
|
+ this.beforeSystem
|
|
+ ", afterSystem="
|
|
+ this.afterSystem
|
|
+ ", priority="
|
|
+ this.priority
|
|
+ ", fulfilled="
|
|
+ this.fulfilled
|
|
+ ", resolved="
|
|
+ this.resolved
|
|
+ "}";
|
|
}
|
|
}
|
|
}
|