Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions loader/src/main/java/net/neoforged/fml/common/Mod.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* <p>
* Any class found with this annotation applied will be loaded as a mod entrypoint for the mod with the given {@linkplain #value() ID}. <br>
* A mod loaded with the {@code javafml} language loader may have multiple entrypoints.
* Entrypoints with the least {@link #depends} are run first.
* Entrypoints for all {@link #dist}s are always run before entrypoints for a single {@link #dist}.
*/
@Retention(RetentionPolicy.RUNTIME)
Expand All @@ -34,4 +35,9 @@
* {@return the side to load this mod entrypoint on}
*/
Dist[] dist() default { Dist.CLIENT, Dist.DEDICATED_SERVER };

/**
* A list of mod IDs which are all required to be present in order to load this mod entrypoint.
*/
String[] depends() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

import java.lang.annotation.ElementType;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.ModLoadingIssue;
Expand All @@ -33,7 +35,9 @@ public ModContainer loadMod(IModInfo info, ModFileScanData modFileScanResults, M
var modClasses = modFileScanResults.getAnnotatedBy(Mod.class, ElementType.TYPE)
.filter(data -> data.annotationData().get("value").equals(info.getModId()))
.filter(ad -> AutomaticEventSubscriber.getSides(ad.annotationData().get("dist")).contains(FMLLoader.getCurrent().getDist()))
.sorted(Comparator.comparingInt(ad -> -AutomaticEventSubscriber.getSides(ad.annotationData().get("dist")).size()))
.filter(ad -> getDepends(ad).stream().allMatch(otherMod -> FMLLoader.getCurrent().getLoadingModList().getModFileById(otherMod) != null))
.sorted(Comparator.<ModFileScanData.AnnotationData>comparingInt(ad -> getDepends(ad).size())
.thenComparingInt(ad -> -AutomaticEventSubscriber.getSides(ad.annotationData().get("dist")).size()))
.map(ad -> ad.clazz().getClassName())
.toList();
return new FMLModContainer(info, modClasses, modFileScanResults, layer);
Expand All @@ -57,4 +61,10 @@ public void validate(IModFile file, Collection<ModContainer> loadedContainers, I
reporter.addIssue(issue);
});
}

@SuppressWarnings("unchecked")
private static List<String> getDepends(ModFileScanData.AnnotationData data) {
var depends = data.annotationData().get("depends");
return depends != null ? (List<String>) depends : Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,61 @@ public ClientEntryPoint() {
assertThat(MESSAGES).isEqualTo(List.of("common", "client"));
}

@Test
void testDependsEntrypointDoesntFire() throws Exception {
installation.setupProductionClient();

installation.buildModJar("test.jar")
.withTestmodModsToml()
.addClass("testmod.DependsEntryPoint", """
@net.neoforged.fml.common.Mod(value = "testmod", depends = "othermod")
public class DependsEntryPoint {
public DependsEntryPoint() {
net.neoforged.fml.javafmlmod.FMLJavaModLanguageProviderTest.MESSAGES.add("fired");
}
}
""")
.build();

launchAndLoad("neoforgeclient");

assertThat(MESSAGES).isEmpty();
}

@Test
void testDependsEntrypointOrdering() throws Exception {
installation.setupProductionClient();

installation.buildModJar("othermod.jar").withMod("othermod", "1.0").build();
installation.buildModJar("test.jar")
.withTestmodModsToml(builder -> {
builder.addDependency("testmod", "othermod", "[1,)", config -> {
config.set("type", "optional");
});
})
.addClass("testmod.EntryPoint", """
@net.neoforged.fml.common.Mod("testmod")
public class EntryPoint {
public EntryPoint() {
net.neoforged.fml.javafmlmod.FMLJavaModLanguageProviderTest.MESSAGES.add("common");
}
}
""")
.addClass("testmod.DependsEntryPoint", """
@net.neoforged.fml.common.Mod(value = "testmod", depends = "othermod")
public class DependsEntryPoint {
public DependsEntryPoint() {
net.neoforged.fml.javafmlmod.FMLJavaModLanguageProviderTest.MESSAGES.add("dependency");
}
}
""")
.build();

launchAndLoad("neoforgeclient");

assertThat(MESSAGES).isEqualTo(List.of("common", "dependency"));
}

@Test
void testErrorDuringEventDispatch() throws Exception {
installation.setupProductionClient();
Expand Down
Loading