Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify extension point system #80

Merged
merged 2 commits into from
Mar 25, 2024
Merged
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
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
Loading