Skip to content

Commit 4ed307a

Browse files
committed
Add ModMenu config screen factory bridge
1 parent 9ef2692 commit 4ed307a

11 files changed

Lines changed: 285 additions & 1 deletion

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ be used instead. Connector Extras fixes a few issues that have arised with this
5959
some items invisible.
6060
- Initialize `Minecraft#resourceManager` before Connector loads fabric mods, which is required to run Geckolib Fabric
6161

62+
### ModMenu
63+
64+
Integrates ModMenu the config screen factory with Forge's mod list gui. Includes the
65+
[ModMenu API](https://github.com/TerraformersMC/ModMenu/tree/91460879c6d0693b14d1632baccea56bf3619f2e/src/main/java/com/terraformersmc/modmenu/api),
66+
licensed under the MIT license.
67+
6268
## Get help
6369

6470
If you're having trouble using Connector Extras or believe it is not functioning correctly, ask us

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ dependencies {
8686
includeProject("architectury-bridge")
8787
includeProject("terrablender-bridge")
8888
includeProject("geckolib-fabric-compat")
89+
includeProject("modmenu-bridge")
8990

9091
// Misc
9192
modImplementation("curse.maven:mcpitanlibarch-682213:4723157")
@@ -176,5 +177,6 @@ publishMods {
176177
optional { id.set("kkmrDlKT") } // TerraBlender
177178
optional { id.set("lhGA9TYQ") } // Architectury API
178179
optional { id.set("8BmcQJ2H") } // Geckolib
180+
optional { id.set("mOgUt4GM") } // Mod Menu
179181
}
180182
}

modmenu-bridge/build.gradle.kts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id("dev.architectury.loom")
3+
}
4+
5+
val versionMc: String by rootProject
6+
val versionForge: String by rootProject
7+
8+
loom {
9+
forge {
10+
mixinConfig("mixins.connectorextras_modmenu_bridge.json")
11+
}
12+
mixin {
13+
defaultRefmapName.set("refmap.connectorextras_modmenu_bridge.json")
14+
}
15+
}
16+
17+
dependencies {
18+
mappings(loom.officialMojangMappings())
19+
forge(group = "net.minecraftforge", name = "forge", version = "$versionMc-$versionForge")
20+
21+
modImplementation(group = "dev.su5ed.sinytra", name = "fabric-loader", version = "2.3.4+0.14.21+1.20.1")
22+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2018-2020 Prospector
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.terraformersmc.modmenu.api;
25+
26+
import net.minecraft.client.gui.screens.Screen;
27+
28+
@FunctionalInterface
29+
public interface ConfigScreenFactory<S extends Screen> {
30+
S create(Screen parent);
31+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2018-2020 Prospector
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.terraformersmc.modmenu.api;
25+
26+
import com.google.common.collect.ImmutableMap;
27+
import net.minecraft.client.gui.screens.Screen;
28+
import net.minecraft.network.chat.CommonComponents;
29+
import net.minecraft.network.chat.Component;
30+
import net.minecraftforge.client.gui.ModListScreen;
31+
32+
import java.util.Map;
33+
import java.util.function.Consumer;
34+
35+
public interface ModMenuApi {
36+
37+
/**
38+
* Used for creating a {@link Screen} instance of the Mod Menu
39+
* "Mods" screen
40+
*
41+
* @param previous The screen before opening
42+
* @return A "Mods" Screen
43+
*/
44+
static Screen createModsScreen(Screen previous) {
45+
return new ModListScreen(previous);
46+
}
47+
48+
/**
49+
* Used for creating a {@link Component} just like what would appear
50+
* on a Mod Menu Mods button
51+
*
52+
* @return The text that would be displayed on a Mods button
53+
*/
54+
static Component createModsButtonText() {
55+
return CommonComponents.EMPTY;
56+
}
57+
58+
/**
59+
* Used to construct a new config screen instance when your mod's
60+
* configuration button is selected on the mod menu screen. The
61+
* screen instance parameter is the active mod menu screen.
62+
*
63+
* @return A factory for constructing config screen instances.
64+
*/
65+
default ConfigScreenFactory<?> getModConfigScreenFactory() {
66+
return screen -> null;
67+
}
68+
69+
/**
70+
* Used to provide config screen factories for other mods. This takes second
71+
* priority to a mod's own config screen factory provider. For example, if
72+
* mod `xyz` supplies a config screen factory, mod `abc` providing a config
73+
* screen to `xyz` will be pointless, as the one provided by `xyz` will be
74+
* used.
75+
* <p>
76+
* This method is NOT meant to be used to add a config screen factory to
77+
* your own mod.
78+
*
79+
* @return a map of mod ids to screen factories.
80+
*/
81+
default Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
82+
return ImmutableMap.of();
83+
}
84+
85+
/**
86+
* Used to mark mods with a badge indicating that they are
87+
* provided by a modpack.
88+
* <p>
89+
* Builtin mods such as `minecraft` cannot be marked as
90+
* provided by a modpack.
91+
*/
92+
default void attachModpackBadges(Consumer<String> consumer) {
93+
94+
}
95+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package dev.su5ed.sinytra.connectorextras.modmenu;
2+
3+
import com.mojang.logging.LogUtils;
4+
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
5+
import com.terraformersmc.modmenu.api.ModMenuApi;
6+
import net.fabricmc.loader.api.FabricLoader;
7+
import net.minecraft.client.gui.screens.Screen;
8+
import net.minecraftforge.client.ConfigScreenHandler;
9+
import net.minecraftforge.client.gui.ModListScreen;
10+
import net.minecraftforge.fml.ModList;
11+
import net.minecraftforge.fml.common.Mod;
12+
import org.slf4j.Logger;
13+
14+
import java.util.ArrayList;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
@Mod(ModMenuCompat.MODID)
20+
public class ModMenuCompat {
21+
public static final String MODID = "connectorextras_modmenu_bridge";
22+
private static final Logger LOGGER = LogUtils.getLogger();
23+
private static final String MODMENU_MODID = "modmenu";
24+
25+
private static boolean initialized = false;
26+
27+
public static void init() {
28+
if (initialized) {
29+
return;
30+
}
31+
initialized = true;
32+
33+
Map<String, ConfigScreenFactory<?>> modFactories = new HashMap<>();
34+
List<Map<String, ConfigScreenFactory<?>>> providedFactories = new ArrayList<>();
35+
FabricLoader.getInstance().getEntrypointContainers(MODMENU_MODID, ModMenuApi.class).forEach(container -> {
36+
String modId = container.getProvider().getMetadata().getId();
37+
try {
38+
ModMenuApi entry = container.getEntrypoint();
39+
modFactories.put(modId, entry.getModConfigScreenFactory());
40+
providedFactories.add(entry.getProvidedConfigScreenFactories());
41+
} catch (Throwable t) {
42+
LOGGER.error("Failed to load ModMenuApi entrypoint for {}", modId, t);
43+
}
44+
});
45+
46+
providedFactories.forEach(map -> map.forEach(modFactories::putIfAbsent));
47+
providedFactories.clear();
48+
49+
Screen dummyParent = new ModListScreen(null);
50+
modFactories.forEach((modId, factory) -> {
51+
// Ensure factory is active. This is required to avoid cases where the integration conditionally
52+
// disables itself (e.g. when cloth config is absent) and returns a dummy factory.
53+
try {
54+
if (factory.create(dummyParent) == null) {
55+
return;
56+
}
57+
} catch (Throwable t) {
58+
// If an error occurs, it might be due to us creating the factory so early.
59+
// Since we can't be sure about the factory's status, continue ahead
60+
LOGGER.warn("Error testing config screen factory status for mod {}", modId, t);
61+
}
62+
63+
ModList.get().getModContainerById(modId).ifPresent(fmlContainer ->
64+
fmlContainer.registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
65+
() -> new ConfigScreenHandler.ConfigScreenFactory((mc, screen) -> factory.create(screen))));
66+
});
67+
}
68+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dev.su5ed.sinytra.connectorextras.modmenu.mixin;
2+
3+
import dev.su5ed.sinytra.connectorextras.modmenu.ModMenuCompat;
4+
import net.minecraftforge.client.gui.ModListScreen;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.injection.At;
7+
import org.spongepowered.asm.mixin.injection.Inject;
8+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
9+
10+
@Mixin(ModListScreen.class)
11+
public class ModListScreenMixin {
12+
13+
@Inject(method = "init", at = @At("HEAD"))
14+
private void initModMenuCompat(CallbackInfo ci) {
15+
ModMenuCompat.init();
16+
}
17+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
modLoader="javafml"
2+
loaderVersion="[47,)"
3+
license="MIT"
4+
issueTrackerURL="https://github.com/Sinytra/ConnectorExtras/issues"
5+
6+
[[mods]]
7+
modId="connectorextras_modmenu_bridge"
8+
version="${file.jarVersion}"
9+
displayName="Connector Extras ModMenu Bridge"
10+
authors="Su5eD"
11+
displayURL="https://github.com/Sinytra/ConnectorExtras"
12+
description='''
13+
Integrates ModMenu the config screen factory with Forge's mod list gui.
14+
'''
15+
displayTest = 'IGNORE_ALL_VERSION'
16+
[[dependencies.connectorextras_modmenu_bridge]]
17+
modId="forge"
18+
mandatory=true
19+
versionRange="[47,)"
20+
ordering="NONE"
21+
side="BOTH"
22+
[[dependencies.connectorextras_modmenu_bridge]]
23+
modId="minecraft"
24+
mandatory=true
25+
versionRange="[1.20.1,1.21)"
26+
ordering="NONE"
27+
side="BOTH"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"minVersion": "0.8",
3+
"compatibilityLevel": "JAVA_16",
4+
"required": true,
5+
"package": "dev.su5ed.sinytra.connectorextras.modmenu.mixin",
6+
"client": [
7+
"ModListScreenMixin"
8+
]
9+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"pack": {
3+
"description": "The default data for connectorextras_modmenu_bridge",
4+
"pack_format": 15
5+
}
6+
}

0 commit comments

Comments
 (0)