Skip to content

Commit 54a5278

Browse files
committed
feat: try to add forge support
blocking on my language provider in plugin layer can't access my lib in game layer
1 parent 929bfb2 commit 54a5278

File tree

12 files changed

+216
-27
lines changed

12 files changed

+216
-27
lines changed

common/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ dependencies {
4848
tasks {
4949
jar {
5050
manifest.attributes(
51-
"FMLModType" to "GAMELIBRARY",
51+
"FMLModType" to "LIBRARY",
5252
)
5353
}
5454
}

common/src/main/kotlin/settingdust/modsets/PlatformHelper.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import java.util.*
66
interface PlatformHelper {
77
companion object {
88
@JvmStatic
9-
val INSTANCE = ServiceLoader.load(PlatformHelper::class.java).first()!!
9+
val INSTANCE = ServiceLoader.load(PlatformHelper::class.java, Companion::class.java.classLoader).first()!!
1010

1111
val configDir: Path
1212
get() = INSTANCE.configDir

forge/build.gradle.kts

+39-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ sourceSets {
1414
compileClasspath += main.get().compileClasspath
1515
compileClasspath += main.get().output
1616
}
17+
18+
val language by registering {
19+
compileClasspath += main.get().compileClasspath
20+
compileClasspath += main.get().output
21+
}
1722
}
1823

1924
repositories {
@@ -51,6 +56,28 @@ val modJar by tasks.registering(Jar::class) {
5156
}
5257
}
5358

59+
val languageJar by tasks.registering(Jar::class) {
60+
archiveClassifier.set("language")
61+
from(sourceSets.named("language").get().output)
62+
destinationDirectory.set(project.buildDir.resolve("devlibs"))
63+
manifest {
64+
attributes(
65+
"FMLModType" to "LANGPROVIDER",
66+
)
67+
}
68+
}
69+
70+
val commonCoreJar by tasks.registering(Jar::class) {
71+
archiveClassifier.set("common-core")
72+
from(project(":common").sourceSets.main.get().output)
73+
destinationDirectory.set(project.buildDir.resolve("devlibs"))
74+
manifest {
75+
attributes(
76+
"FMLModType" to "LIB",
77+
)
78+
}
79+
}
80+
5481
loom {
5582
mods {
5683
named("main") {
@@ -102,6 +129,17 @@ tasks {
102129
modJar.get().outputs.files.singleFile
103130
),
104131
)
132+
forgeNestedJars.add(
133+
IncludedJarFactory.NestedFile(
134+
IncludedJarFactory.Metadata(
135+
"settingdust.modsets.forge.language",
136+
"language",
137+
version.toString(),
138+
"language"
139+
),
140+
languageJar.get().outputs.files.singleFile
141+
),
142+
)
105143
}
106144

107145
processResources {
@@ -115,8 +153,7 @@ tasks {
115153
afterEvaluate {
116154
withType<AbstractRunTask> {
117155
classpath = classpath.filter { it !in sourceSets.main.get().output }
118-
classpath += files(jar)
119-
classpath += files(modJar)
156+
classpath += files(jar, modJar, languageJar)
120157
}
121158
}
122159
}

forge/src/core/kotlin/settingdust/modsets/forge/Entrypoint.kt

+9-12
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,31 @@ import net.minecraftforge.client.ConfigScreenHandler.ConfigScreenFactory
88
import net.minecraftforge.fml.ModList
99
import net.minecraftforge.fml.common.Mod
1010
import net.minecraftforge.fml.loading.FMLPaths
11-
import net.minecraftforge.fml.loading.moddiscovery.BuiltinGameLibraryLocator
12-
import net.minecraftforge.fml.loading.moddiscovery.ClasspathLocator
13-
import net.minecraftforge.fml.loading.moddiscovery.JarInJarDependencyLocator
14-
import net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator
15-
import settingdust.modsets.ModSet
16-
import settingdust.modsets.ModSets
17-
import settingdust.modsets.config
11+
import net.minecraftforge.fml.loading.moddiscovery.*
12+
import settingdust.modsets.*
1813
import settingdust.modsets.forge.service.ModSetsModLocator
19-
import settingdust.modsets.rules
2014
import thedarkcolour.kotlinforforge.forge.LOADING_CONTEXT
2115
import kotlin.io.path.div
2216

2317
@Mod("mod_sets")
2418
class Entrypoint {
2519
init {
2620
// Take from https://github.com/isXander/YetAnotherConfigLib/blob/1.20.x/dev/test-forge/src/main/java/dev/isxander/yacl/test/forge/ForgeTest.java
21+
val gameClassLoader = javaClass.classLoader
22+
val rulesClass = gameClassLoader.loadClass("settingdust.modsets.Rules")
23+
val rules = rulesClass.getDeclaredField("INSTANCE")[null] as Rules
2724
LOADING_CONTEXT.registerExtensionPoint(ConfigScreenFactory::class.java) {
2825
ConfigScreenFactory { _, parent ->
29-
ModSets.rules.createScreen(parent)
26+
rules.createScreen(parent)
3027
}
3128
}
3229

3330
val gameDir = FMLPaths.GAMEDIR.get()
3431
val modsPath = FMLPaths.MODSDIR.get()
35-
val modSets = ModSets.rules.modSets
32+
val modSets = rules.modSets
3633

3734
GlobalScope.launch(Dispatchers.IO) {
38-
ModSets.rules.ModSetsRegisterCallback.collect {
35+
rules.ModSetsRegisterCallback.collect {
3936
for ((key, value) in ModSetsModLocator.directoryModSet.mapValues {
4037
ModSet(
4138
Component.literal(it.key),
@@ -49,7 +46,7 @@ class Entrypoint {
4946

5047
for (mod in ModList.get().mods) {
5148
val provider = mod.owningFile.file.provider
52-
if (provider is MinecraftLocator || provider is BuiltinGameLibraryLocator || provider is JarInJarDependencyLocator || provider is ClasspathLocator) continue
49+
if (provider !is ModsFolderLocator) continue
5350
if (mod.modId in modSets) ModSets.logger.warn("Duplicate mod set with directory name: ${mod.modId}")
5451
modSets.putIfAbsent(
5552
mod.modId, ModSet(

forge/src/core/kotlin/settingdust/modsets/forge/PlatformHelperForge.kt

-10
This file was deleted.

forge/src/core/resources/META-INF/services/settingdust.modsets.PlatformHelper

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package settingdust.modsets.forge.language;
2+
3+
import net.minecraftforge.fml.loading.FMLLoader;
4+
import net.minecraftforge.fml.loading.moddiscovery.ModValidator;
5+
import net.minecraftforge.forgespi.language.ILifecycleEvent;
6+
import net.minecraftforge.forgespi.language.IModLanguageProvider;
7+
import net.minecraftforge.forgespi.language.ModFileScanData;
8+
9+
import java.lang.reflect.Field;
10+
import java.util.function.Consumer;
11+
import java.util.function.Supplier;
12+
13+
public class DummyModLanguageProvider implements IModLanguageProvider {
14+
private static final Class<FMLLoader> loaderClass = FMLLoader.class;
15+
private static final Field modValidator;
16+
private static ModValidator originalValidator;
17+
18+
static {
19+
try {
20+
modValidator = loaderClass.getDeclaredField("modValidator");
21+
modValidator.setAccessible(true);
22+
} catch (NoSuchFieldException e) {
23+
throw new RuntimeException(e);
24+
}
25+
}
26+
27+
public DummyModLanguageProvider() {
28+
hookModValidator();
29+
}
30+
31+
public static void hookModValidator() {
32+
try {
33+
originalValidator = (ModValidator) modValidator.get(null);
34+
modValidator.set(null, new DummyModValidator(originalValidator));
35+
} catch (IllegalAccessException e) {
36+
throw new RuntimeException(e);
37+
}
38+
}
39+
40+
public static void resetModValidator() throws IllegalAccessException {
41+
modValidator.set(null, originalValidator);
42+
}
43+
44+
@Override
45+
public String name() {
46+
return "mod sets dummy";
47+
}
48+
49+
@Override
50+
public Consumer<ModFileScanData> getFileVisitor() {
51+
return (data) -> {
52+
};
53+
}
54+
55+
@Override
56+
public <R extends ILifecycleEvent<R>> void consumeLifecycleEvent(Supplier<R> consumeEvent) {
57+
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package settingdust.modsets.forge.language;
2+
3+
import com.google.common.collect.Lists;
4+
import net.minecraftforge.fml.loading.EarlyLoadingException;
5+
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
6+
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
7+
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
8+
import net.minecraftforge.fml.loading.moddiscovery.ModValidator;
9+
import net.minecraftforge.forgespi.language.IModInfo;
10+
import net.minecraftforge.forgespi.locating.IModFile;
11+
import settingdust.modsets.ConfigKt;
12+
import settingdust.modsets.ModSets;
13+
import settingdust.modsets.forge.platform.PlatformHelperForge;
14+
15+
import java.lang.reflect.Field;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.stream.Collectors;
20+
21+
public class DummyModValidator extends ModValidator {
22+
private static final Class<ModValidator> classModValidator = ModValidator.class;
23+
private static final Field fieldModFiles;
24+
25+
private static final Field fieldBrokenFiles;
26+
27+
private static final Field fieldDiscoveryErrorData;
28+
private static final Field fieldCandidateMods;
29+
30+
private static final Class<ModFileInfo> classModFileInfo = ModFileInfo.class;
31+
private static final Field fieldMods;
32+
33+
static {
34+
try {
35+
fieldModFiles = classModValidator.getDeclaredField("modFiles");
36+
fieldBrokenFiles = classModValidator.getDeclaredField("brokenFiles");
37+
fieldDiscoveryErrorData = classModValidator.getDeclaredField("discoveryErrorData");
38+
fieldCandidateMods = classModValidator.getDeclaredField("candidateMods");
39+
40+
fieldModFiles.setAccessible(true);
41+
fieldBrokenFiles.setAccessible(true);
42+
fieldDiscoveryErrorData.setAccessible(true);
43+
fieldCandidateMods.setAccessible(true);
44+
45+
fieldMods = classModFileInfo.getDeclaredField("mods");
46+
47+
fieldMods.setAccessible(true);
48+
} catch (NoSuchFieldException e) {
49+
throw new RuntimeException(e);
50+
}
51+
}
52+
53+
private final ModValidator validator;
54+
55+
public DummyModValidator(ModValidator validator) throws IllegalAccessException {
56+
super(
57+
(Map<IModFile.Type, List<ModFile>>) fieldModFiles.get(validator),
58+
((List<IModFile>) fieldBrokenFiles.get(validator))
59+
.stream().map(IModFile::getModFileInfo).collect(Collectors.toList()),
60+
(List<EarlyLoadingException.ExceptionData>) fieldDiscoveryErrorData.get(validator));
61+
this.validator = validator;
62+
}
63+
64+
@Override
65+
public BackgroundScanHandler stage2Validation() {
66+
new PlatformHelperForge().getConfigDir();
67+
Set<String> disabledMods = ConfigKt.getConfig(ModSets.INSTANCE).getDisabledMods();
68+
try {
69+
DummyModLanguageProvider.resetModValidator();
70+
final var candidateMods = (List<ModFile>) fieldCandidateMods.get(validator);
71+
final var toRemove = Lists.<ModFile>newArrayList();
72+
for (ModFile mod : candidateMods) {
73+
if (mod.getModFileInfo() instanceof ModFileInfo info) {
74+
List<IModInfo> filtered = mod.getModInfos().stream()
75+
.filter(it -> !disabledMods.contains(it.getModId()))
76+
.toList();
77+
if (filtered.isEmpty()) {
78+
toRemove.add(mod);
79+
} else fieldMods.set(info, filtered);
80+
}
81+
}
82+
candidateMods.removeAll(toRemove);
83+
return validator.stage2Validation();
84+
} catch (IllegalAccessException e) {
85+
throw new RuntimeException(e);
86+
}
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package settingdust.modsets.forge.platform;
2+
3+
import net.minecraftforge.fml.loading.FMLPaths;
4+
import org.jetbrains.annotations.NotNull;
5+
import settingdust.modsets.PlatformHelper;
6+
7+
import java.nio.file.Path;
8+
9+
public class PlatformHelperForge implements PlatformHelper {
10+
11+
@NotNull
12+
@Override
13+
public Path getConfigDir() {
14+
return FMLPaths.CONFIGDIR.get();
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
settingdust.modsets.forge.language.DummyModLanguageProvider
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
settingdust.modsets.forge.platform.PlatformHelperForge
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
settingdust.modsets.forge.service.DummyModLanguageProvider

0 commit comments

Comments
 (0)