From 0486b56ec39e1739e45758da01512b6e01144fd0 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 20 Aug 2021 03:05:36 +0200 Subject: [PATCH] Refactored to expose the ModMenu Integration to other Mods --- .../java/ru/bclib/gui/modmenu/EntryPoint.java | 12 ++ .../ModMenu => modmenu}/MainScreen.java | 2 +- .../bclib/integration/ModMenuIntegration.java | 170 +++++++++++++----- src/main/resources/fabric.mod.json | 2 +- 4 files changed, 136 insertions(+), 50 deletions(-) create mode 100644 src/main/java/ru/bclib/gui/modmenu/EntryPoint.java rename src/main/java/ru/bclib/gui/{screens/ModMenu => modmenu}/MainScreen.java (95%) diff --git a/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java b/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java new file mode 100644 index 00000000..18f3e4d5 --- /dev/null +++ b/src/main/java/ru/bclib/gui/modmenu/EntryPoint.java @@ -0,0 +1,12 @@ +package ru.bclib.gui.modmenu; + +import com.terraformersmc.modmenu.util.ModMenuApiMarker; +import ru.bclib.integration.ModMenuIntegration; + +public class EntryPoint extends ModMenuIntegration { + public static final ModMenuApiMarker entrypointObject = createEntrypoint(new EntryPoint()); + + public EntryPoint() { + super(MainScreen::new); + } +} diff --git a/src/main/java/ru/bclib/gui/screens/ModMenu/MainScreen.java b/src/main/java/ru/bclib/gui/modmenu/MainScreen.java similarity index 95% rename from src/main/java/ru/bclib/gui/screens/ModMenu/MainScreen.java rename to src/main/java/ru/bclib/gui/modmenu/MainScreen.java index e56f2f3c..086da6b2 100644 --- a/src/main/java/ru/bclib/gui/screens/ModMenu/MainScreen.java +++ b/src/main/java/ru/bclib/gui/modmenu/MainScreen.java @@ -1,4 +1,4 @@ -package ru.bclib.gui.screens.ModMenu; +package ru.bclib.gui.modmenu; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.CommonComponents; diff --git a/src/main/java/ru/bclib/integration/ModMenuIntegration.java b/src/main/java/ru/bclib/integration/ModMenuIntegration.java index e05b3afb..53301931 100644 --- a/src/main/java/ru/bclib/integration/ModMenuIntegration.java +++ b/src/main/java/ru/bclib/integration/ModMenuIntegration.java @@ -3,23 +3,18 @@ package ru.bclib.integration; import com.google.common.collect.ImmutableMap; import com.terraformersmc.modmenu.util.ModMenuApiMarker; import net.minecraft.client.gui.screens.Screen; -import ru.bclib.gui.screens.ModMenu.MainScreen; +import ru.bclib.integration.ModMenuIntegration.ModMenuScreenFactory; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; -@FunctionalInterface -interface IModMenuScreenFactory { - S create(Screen parent); -} - -class ModMenuScreenFactory { +class ModMenuScreenFactoryImpl { static class ScreenFactoryInvocationHandler implements InvocationHandler { - private final IModMenuScreenFactory act; + private final ModMenuScreenFactory act; - public ScreenFactoryInvocationHandler(IModMenuScreenFactory act) { + public ScreenFactoryInvocationHandler(ModMenuScreenFactory act) { this.act = act; } @@ -29,7 +24,7 @@ class ModMenuScreenFactory { } } - public static IModMenuScreenFactory create(IModMenuScreenFactory act) { + public static ModMenuScreenFactory create(ModMenuScreenFactory act) { Class iConfigScreenFactory = null; try { iConfigScreenFactory = Class.forName("com.terraformersmc.modmenu.api.ConfigScreenFactory"); @@ -40,14 +35,128 @@ class ModMenuScreenFactory { Object o = Proxy.newProxyInstance( ModMenuIntegration.class.getClassLoader(), - new Class[] {iConfigScreenFactory, IModMenuScreenFactory.class}, + new Class[] {iConfigScreenFactory, ModMenuScreenFactory.class}, new ScreenFactoryInvocationHandler(act)); - return (IModMenuScreenFactory)o; + return (ModMenuScreenFactory)o; } } -public class ModMenuIntegration { +/** + * Integration, to provide a custom Screen for ModMenu. + *

+ * This integration allows you to use ModMenu without adding a dependency to your project. If the + * Mod is installed on the Client, and the correct ModMenu-EntryPoint is registered in your fabric.mod.json + * the screen will show up. + *

+ * You only need to subclass this class, and initialize a static Field of Type {@link ModMenuApiMarker} using + * the {@link #createEntrypoint(ModMenuIntegration)}-Method. + *

+ * Example: + *

{@code public class ModMenu extends ModMenuIntegration {
+ *     public static final ModMenuApiMarker entrypointObject = createEntrypoint(new EntryPoint());
+ *
+ * 	    public EntryPoint() {
+ * 		    super(GridScreen::new);
+ *      }
+ * }
+ * You'd also need to add the ModMenu-Entrypoint to your fabric.mod.json: + *
"entrypoints": {
+ * 	    ...
+ *     "modmenu": [ "your.mod.ModMenu::entrypointObject" ]
+ * }
+ */ +public abstract class ModMenuIntegration { + /** + * Creates a new EntryPoint Object that is accepted by ModMenu + * @param target The delegate Object that will receive calls from ModMenu + * @return A Proxy that conforms to the ModMenu spec + */ + public static ModMenuApiMarker createEntrypoint(ModMenuIntegration target) { + Class iModMenuAPI = null; + //Class iModMenuAPIMarker = null; + try { + iModMenuAPI = Class.forName("com.terraformersmc.modmenu.api.ModMenuApi"); + //iModMenuAPIMarker = Class.forName("com.terraformersmc.modmenu.util.ModMenuApiMarker"); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + return (ModMenuApiMarker)new Object(); + } + + Object o = Proxy.newProxyInstance( + ModMenuIntegration.class.getClassLoader(), + new Class[] {iModMenuAPI}, + new BCLibModMenuInvocationHandler(target)); + + return (ModMenuApiMarker)o; + } + + protected final ModMenuScreenFactory screenFactory; + + /** + * Create a new ModMenu delegate + * @param screenFactory A Factory. The Factory receives the currently visible {@code parent}-Screen + * and must return a new Screen Object. + */ + public ModMenuIntegration(ModMenuScreenFactory screenFactory){ + this.screenFactory = screenFactory; + } + + /** + * A Helper class to make a BCLib-Factory conform to the ModMenu-Factory Interface. + * @param factory + * @return A ModMenu Factory for a Screen + */ + final protected ModMenuScreenFactory createFactory(ModMenuScreenFactory factory){ + return ModMenuScreenFactoryImpl.create( factory ); + } + + /** + * Used to construct a new config screen instance when your mod's + * configuration button is selected on the mod menu screen. The + * screen instance parameter is the active mod menu screen. + * (Text copied from ModMenu) + * + * @return A factory for constructing config screen instances. + * + */ + public ModMenuScreenFactory getModConfigScreenFactory() { + return createFactory(screenFactory); + } + + /** + * Used to provide config screen factories for other mods. This takes second + * priority to a mod's own config screen factory provider. For example, if + * mod `xyz` supplies a config screen factory, mod `abc` providing a config + * screen to `xyz` will be pointless, as the one provided by `xyz` will be + * used. + *

+ * This method is NOT meant to be used to add a config screen factory to + * your own mod. + * (Text copied from ModMenu) + * + * @return a map of mod ids to screen factories. + */ + public Map getProvidedConfigScreenFactories() { + return ImmutableMap.of(); + } + + @Override + public String toString() { + return super.toString(); + } + + /** + * A Factory Interface for ModMenu-Screens + *

+ * The Interface matches {@code com.terraformersmc.modmenu.api.ConfigScreenFactory} + */ + @FunctionalInterface + public static interface ModMenuScreenFactory { + Screen create(Screen parent); + } + static class BCLibModMenuInvocationHandler implements InvocationHandler { private final ModMenuIntegration target; @@ -68,39 +177,4 @@ public class ModMenuIntegration { } } } - - public static final ModMenuApiMarker entrypointObject = create(); - - public static ModMenuApiMarker create() { - Class iModMenuAPI = null; - //Class iModMenuAPIMarker = null; - try { - iModMenuAPI = Class.forName("com.terraformersmc.modmenu.api.ModMenuApi"); - //iModMenuAPIMarker = Class.forName("com.terraformersmc.modmenu.util.ModMenuApiMarker"); - } - catch (ClassNotFoundException e) { - e.printStackTrace(); - return (ModMenuApiMarker)new Object(); - } - - Object o = Proxy.newProxyInstance( - ModMenuIntegration.class.getClassLoader(), - new Class[] {iModMenuAPI}, - new BCLibModMenuInvocationHandler(new ModMenuIntegration())); - - return (ModMenuApiMarker)o; - } - - IModMenuScreenFactory getModConfigScreenFactory() { - return ModMenuScreenFactory.create( MainScreen::new ); - } - - Map> getProvidedConfigScreenFactories() { - return ImmutableMap.of(); - } - - @Override - public String toString() { - return super.toString(); - } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 0a193be3..86fbd747 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -26,7 +26,7 @@ "server": [ "ru.bclib.server.BCLibServer" ], - "modmenu": [ "ru.bclib.integration.ModMenuIntegration::entrypointObject" ] + "modmenu": [ "ru.bclib.gui.modmenu.EntryPoint::entrypointObject" ] }, "mixins": [ "bclib.mixins.common.json",