Skip to content

Commit

Permalink
Simplify extension point system (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
Technici4n authored Mar 25, 2024
1 parent ed93dac commit 82313aa
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 23 deletions.
15 changes: 3 additions & 12 deletions loader/src/main/java/net/neoforged/fml/IExtensionPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,10 @@
*
* <p>An extension point can be registered for a mod container using {@link ModContainer#registerExtensionPoint(Class, Supplier)}
* and retrieved (if present) using {@link ModContainer#getCustomExtension(Class)}. An extension point allows a mod to
* supply an arbitrary value as a record class to another mod or framework through their mod container class, avoiding
* supply an arbitrary value to another mod or framework through their mod container class, avoiding
* the use of {@link InterModComms} or other external frameworks to pass around these values.</p>
*
* <p>The usual way to declare an extension point is to implement this interface on a record class, with the type
* parameter being a reference to the class itself. For example, {@code record MyExtension(...) extends
* IExtensionPoint<MyExtension>} would declare an extension point which supplies a {@code MyExtension} object. However,
* there is no hard requirement that an extension point's type parameter must be in reference to itself; the type
* parameter may reference another record class instead.</p>
*
* @param <T> the type of the record which is held by the extension point
*/
@SuppressWarnings("unused") // Type parameter T
public interface IExtensionPoint<T extends Record>
public interface IExtensionPoint
{
/**
* Extension point for the compatibility display test used on the server selection screen.
Expand Down Expand Up @@ -93,7 +84,7 @@ public interface IExtensionPoint<T extends Record>
* @see net.neoforged.client.ForgeHooksClient#processForgeListPingData(net.minecraft.network.protocol.status.ServerStatus, net.minecraft.client.multiplayer.ServerData)
*/
@SuppressWarnings("JavadocReference") // reference to NetworkConstants, ForgeHooksClient
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint<DisplayTest> {
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint {
public static final String IGNORESERVERONLY = "OHNOES\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31";
}
}
30 changes: 20 additions & 10 deletions loader/src/main/java/net/neoforged/fml/ModContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public abstract class ModContainer
protected ModLoadingStage modLoadingStage;
protected Supplier<?> contextExtension;
protected final Map<ModLoadingStage, Runnable> activityMap = new HashMap<>();
protected final Map<Class<? extends IExtensionPoint<?>>, Supplier<?>> extensionPoints = new IdentityHashMap<>();
protected final Map<Class<? extends IExtensionPoint>, Supplier<?>> extensionPoints = new IdentityHashMap<>();
protected final EnumMap<ModConfig.Type, ModConfig> configs = new EnumMap<>(ModConfig.Type.class);

public ModContainer(IModInfo info)
Expand All @@ -66,20 +66,20 @@ public ModContainer(IModInfo info)
this.modLoadingStage = ModLoadingStage.CONSTRUCT;

final String displayTestString = info.getConfig().<String>getConfigElement("displayTest").orElse("MATCH_VERSION"); // missing defaults to DEFAULT type
Supplier<IExtensionPoint.DisplayTest> displayTestSupplier = switch (displayTestString) {
var displayTest = switch (displayTestString) {
case "MATCH_VERSION" -> // default displaytest checks for version string match
() -> new IExtensionPoint.DisplayTest(() -> this.modInfo.getVersion().toString(),
new IExtensionPoint.DisplayTest(() -> this.modInfo.getVersion().toString(),
(incoming, isNetwork) -> Objects.equals(incoming, this.modInfo.getVersion().toString()));
case "IGNORE_SERVER_VERSION" -> // Ignores any version information coming from the server - use for server only mods
() -> new IExtensionPoint.DisplayTest(() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY, (incoming, isNetwork) -> true);
new IExtensionPoint.DisplayTest(() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY, (incoming, isNetwork) -> true);
case "IGNORE_ALL_VERSION" -> // Ignores all information and provides no information
() -> new IExtensionPoint.DisplayTest(() -> "", (incoming, isNetwork) -> true);
new IExtensionPoint.DisplayTest(() -> "", (incoming, isNetwork) -> true);
case "NONE" -> null; // NO display test at all - use this if you're going to do your own display test
default -> // any other value throws an exception
throw new IllegalArgumentException("Invalid displayTest value supplied in mods.toml");
};
if (displayTestSupplier != null)
registerExtensionPoint(IExtensionPoint.DisplayTest.class, displayTestSupplier);
if (displayTest != null)
registerExtensionPoint(IExtensionPoint.DisplayTest.class, displayTest);
else
extensionPoints.remove(IExtensionPoint.DisplayTest.class);
}
Expand Down Expand Up @@ -143,12 +143,22 @@ public IModInfo getModInfo()
}

@SuppressWarnings("unchecked")
public <T extends Record> Optional<T> getCustomExtension(Class<? extends IExtensionPoint<T>> point) {
public <T extends IExtensionPoint> Optional<T> getCustomExtension(Class<T> point) {
return Optional.ofNullable((T)extensionPoints.getOrDefault(point,()-> null).get());
}

public <T extends Record & IExtensionPoint<T>> void registerExtensionPoint(Class<? extends IExtensionPoint<T>> point, Supplier<T> extension)
{
/**
* Registers an {@link IExtensionPoint} with the mod container.
*/
public <T extends IExtensionPoint> void registerExtensionPoint(Class<T> point, T extension) {
extensionPoints.put(point, () -> extension);
}

/**
* Registers an {@link IExtensionPoint} with the mod container.
* This overload allows passing a supplier that will only be evaluated when the extension is requested.
*/
public <T extends IExtensionPoint> void registerExtensionPoint(Class<T> point, Supplier<T> extension) {
extensionPoints.put(point, extension);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public String getActiveNamespace() {
* @param extension An extension operator
* @param <T> The type signature of the extension operator
*/
public <T extends Record & IExtensionPoint<T>> void registerExtensionPoint(Class<? extends IExtensionPoint<T>> point, Supplier<T> extension) {
public <T extends IExtensionPoint> void registerExtensionPoint(Class<T> point, Supplier<T> extension) {
getActiveContainer().registerExtensionPoint(point, extension);
}

Expand Down

0 comments on commit 82313aa

Please sign in to comment.