Skip to content

Commit f32d260

Browse files
committed
Add DependencyBundles + initial Kotlin support
1 parent 3359507 commit f32d260

File tree

9 files changed

+244
-13
lines changed

9 files changed

+244
-13
lines changed

dev/src/main/groovy/com/fox2code/foxloader/dev/FoxLoaderConfig.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,24 @@
2323
*/
2424
package com.fox2code.foxloader.dev;
2525

26+
import com.fox2code.foxloader.dependencies.DependencyHelper;
27+
2628
import java.text.Normalizer;
29+
import java.util.ArrayList;
30+
import java.util.NoSuchElementException;
31+
import java.util.StringJoiner;
2732

2833
/**
2934
* Config for the FoxLoader gradle plugin
3035
*/
3136
public class FoxLoaderConfig {
3237
public FoxLoaderConfig() {}
3338

39+
boolean configImmutable = false;
3440
boolean decompileSources = // Only decompile sources if we have no CI to not waste server time
3541
System.getenv("CI") == null && System.getenv("JITPACK") == null;
3642
boolean addJitPackCIPublish = false;
43+
ArrayList<String> usedDependencyBundlesList = new ArrayList<>();
3744
String username = Normalizer.normalize(System.getProperty("user.name"),
3845
Normalizer.Form.NFD).replaceAll("[^a-zA-Z0-9_]+","");
3946
public String modMain;
@@ -48,6 +55,7 @@ public FoxLoaderConfig() {}
4855
public String modLoadingPlugin;
4956

5057
public void modDesc() {
58+
this.checkConfigMutable();
5159
if (this.modDesc == null) {
5260
this.modDesc = "";
5361
} else {
@@ -56,13 +64,32 @@ public void modDesc() {
5664
}
5765

5866
public void modDesc(String text) {
67+
this.checkConfigMutable();
5968
if (this.modDesc == null) {
6069
this.modDesc = text;
6170
} else {
6271
this.modDesc += "\n" + text;
6372
}
6473
}
6574

75+
public void useDependencyBundle(String dependencyBundle) {
76+
this.checkConfigMutable();
77+
if (!DependencyHelper.availableDependencyBundles.contains(dependencyBundle)) {
78+
throw new NoSuchElementException("Unknown dependency bundle ID: " + dependencyBundle);
79+
}
80+
if (!this.usedDependencyBundlesList.contains(dependencyBundle)) {
81+
this.usedDependencyBundlesList.add(dependencyBundle);
82+
}
83+
}
84+
85+
public String getUsedDependencyBundles() {
86+
StringJoiner stringJoiner = new StringJoiner(",");
87+
for (String dependencyBundle : this.usedDependencyBundlesList) {
88+
stringJoiner.add(dependencyBundle);
89+
}
90+
return stringJoiner.toString();
91+
}
92+
6693
// For testing only
6794
public String dumpClass;
6895
public String foxLoaderLibVersionOverride;
@@ -72,4 +99,10 @@ public void modDesc(String text) {
7299
public boolean useLWJGLX = false;
73100
public String LWJGLXVersion = "0.21";
74101
public String LWJGLXLWJGLVersion = "3.3.1";
102+
103+
private void checkConfigMutable() {
104+
if (this.configImmutable) {
105+
throw new IllegalStateException("Trying to modify config after it has been loaded");
106+
}
107+
}
75108
}

dev/src/main/groovy/com/fox2code/foxloader/dev/GradlePlugin.groovy

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,20 @@ class GradlePlugin implements Plugin<Project> {
198198
options.encoding = 'UTF-8'
199199
}
200200
FoxLoaderConfig config = ((FoxLoaderConfig) project.extensions.getByName("foxloader"))
201+
if (project.pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
202+
config.useDependencyBundle("kotlin")
203+
}
204+
config.configImmutable = true
201205
for (DependencyHelper.Dependency dependency : DependencyHelper.commonDependencies) {
202206
if (dependency == DependencyHelper.jvmDowngraderCore ||
203207
dependency == DependencyHelper.jvmDowngraderJavaAPI) {
204208
continue
205209
}
206-
project.dependencies {
207-
implementation(dependency.name)
208-
if (!DependencyHelper.skipDevSources(dependency)) {
209-
sourcePreDownload(dependency.name + ":sources")
210-
}
210+
addDependencyToProject(project, dependency)
211+
}
212+
for (String dependencyBundle : config.usedDependencyBundlesList) {
213+
for (DependencyHelper.Dependency dependency : DependencyHelper.getDependencyBundle(dependencyBundle)) {
214+
addDependencyToProject(project, dependency)
211215
}
212216
}
213217
project.configurations.configureEach {
@@ -275,6 +279,9 @@ class GradlePlugin implements Plugin<Project> {
275279
(project.getTasks().named("jar").get() as Jar).manifest {
276280
attributes 'For-FoxLoader-Version': BuildConfig.FOXLOADER_VERSION
277281
attributes 'For-ReIndev-Version': BuildConfig.REINDEV_VERSION
282+
if (!config.usedDependencyBundles.isEmpty()) {
283+
attributes 'Request-FoxLoader-Dependency-Bundles': config.getUsedDependencyBundles()
284+
}
278285
attributes 'ModId': config.modId
279286
if (config.modMain != null &&
280287
!config.modMain.isEmpty()) {
@@ -407,6 +414,15 @@ class GradlePlugin implements Plugin<Project> {
407414
}
408415
}
409416

417+
static void addDependencyToProject(Project project, DependencyHelper.Dependency dependency) {
418+
project.dependencies {
419+
implementation(dependency.name)
420+
if (!DependencyHelper.skipDevSources(dependency)) {
421+
sourcePreDownload(dependency.name + ":sources")
422+
}
423+
}
424+
}
425+
410426
static void addJitPackPublishTask(Project project, FoxLoaderConfig config, JitPackService service, String path) {
411427
int i = path.indexOf('/')
412428
if (i == -1) return

gradle.properties

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,29 @@ rebuild.version=1.1.0
7777
fox-events.version=1.2.0
7878

7979
# https://github.com/Fox2Code/FoxFlexVer/releases
80-
fox-flex-ver.version=1.1.0
80+
fox-flex-ver.version=1.1.0
81+
82+
# For dependency bundles.
83+
# https://mvnrepository.com/artifact/net.java.dev.jna/jna
84+
jna.version=5.17.0
85+
86+
# https://kotlinlang.org/docs/home.html
87+
kotlin.version=2.1.21
88+
89+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/atomicfu-jvm
90+
kotlinx.atomicfu.version=0.27.0
91+
92+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm
93+
kotlinx.coroutines.version=1.10.2
94+
95+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-datetime-jvm
96+
kotlinx.datetime.version=0.6.2
97+
98+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-io-core-jvm
99+
kotlinx.io.version=0.7.0
100+
101+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-metadata-jvm
102+
kotlinx.metadata.version=0.9.0
103+
104+
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core-jvm
105+
kotlinx.serialization.version=1.8.1

loader/src/main/java/com/fox2code/foxloader/loader/ModInfo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import java.io.DataInputStream;
3131
import java.io.File;
3232
import java.io.IOException;
33+
import java.util.Collection;
34+
import java.util.Collections;
3335
import java.util.jar.Attributes;
3436
import java.util.logging.Level;
3537

@@ -131,4 +133,8 @@ public final String getId() {
131133
public final String getDescription() {
132134
return this.description;
133135
}
136+
137+
public Collection<String> getRequestedDependencyBundles() {
138+
return Collections.emptyList();
139+
}
134140
}

loader/src/main/java/com/fox2code/foxloader/loader/ModLoaderInit.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
207207
modContainers.put(FOX_LOADER_CONTAINER.getModId(), FOX_LOADER_CONTAINER);
208208
ArrayList<JavaModInfo> loadersInfo = new ArrayList<>();
209209
HashMap<String, LoadingPlugin> loaders = new HashMap<>();
210+
HashSet<String> loadedBundles = new HashSet<>();
210211
LinkedHashMap<String, EarlyModRegistryInfo> earlyModRegistryInfos = new LinkedHashMap<>();
211212
LinkedHashMap<String, EarlyModRegistryInfo> earlyInjectedModRegistryInfos = new LinkedHashMap<>();
212213
LinkedList<File> files = new LinkedList<>(Arrays.asList(
@@ -278,6 +279,8 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
278279
}
279280
// Construct loading plugins
280281
for (EarlyModRegistryInfo earlyModRegistryInfo : earlyModRegistryInfos.values()) {
282+
// Load dependencies bundles of potential loading plugins early.
283+
loadDependencyBundlesForMod(loadedBundles, earlyModRegistryInfo.modInfo);
281284
// Allow loading plugins to run
282285
FoxLauncher.getFoxClassLoader().addFileToClassLoader(earlyModRegistryInfo.modInfo);
283286
}
@@ -385,7 +388,7 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
385388
sortedEarlyModRegistryInfo.sort((o1, o2) -> Long.compare(
386389
o2.modInfo.loadOrderPriority, o1.modInfo.loadOrderPriority));
387390
for (EarlyModRegistryInfo earlyModRegistryInfo : sortedEarlyModRegistryInfo) {
388-
earlyModRegistryInfo.register();
391+
earlyModRegistryInfo.register(loadedBundles);
389392
}
390393
if (FoxLauncher.DEVELOPING_FOXLOADER && !files.isEmpty()) {
391394
throw new Error("Leftovers files detected: " + files);
@@ -406,6 +409,9 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
406409
for (LoadingPlugin loadingPlugin : loaders.values()) {
407410
loadingPlugin.onAllModContainersPreloaded();
408411
}
412+
if (FoxLauncher.DEVELOPING_FOXLOADER) {
413+
loadAllDependencyBundlesForDev(loadedBundles);
414+
}
409415
return loaders.values();
410416
}
411417

@@ -436,6 +442,30 @@ private static void assertValidModInfo(ModInfo modInfo, boolean privileged) {
436442
assertValidModField(modInfo.authors, "authors", modInfo.fileName);
437443
}
438444

445+
private static void loadAllDependencyBundlesForDev(HashSet<String> loadedBundles) {
446+
for (String dependencyBundle : DependencyHelper.availableDependencyBundles) {
447+
if (loadedBundles.add(dependencyBundle)) {
448+
for (DependencyHelper.Dependency dependency : DependencyHelper.getDependencyBundle(dependencyBundle)) {
449+
DependencyHelper.loadDependency(dependency);
450+
}
451+
}
452+
}
453+
}
454+
455+
private static void loadDependencyBundlesForMod(HashSet<String> loadedBundles, ModInfo modInfo) {
456+
for (String dependencyBundle : modInfo.getRequestedDependencyBundles()) {
457+
if (!DependencyHelper.availableDependencyBundles.contains(dependencyBundle)) {
458+
throw new RuntimeException("Mod " + idAndFile(modInfo) + " is asking for " + dependencyBundle +
459+
" dependency bundle, but it is missing on FoxLoader " + BuildConfig.FOXLOADER_VERSION);
460+
}
461+
if (loadedBundles.add(dependencyBundle)) {
462+
for (DependencyHelper.Dependency dependency : DependencyHelper.getDependencyBundle(dependencyBundle)) {
463+
DependencyHelper.loadDependency(dependency);
464+
}
465+
}
466+
}
467+
}
468+
439469
private static void assertValidModField(String value, String fieldName, String modName) {
440470
boolean invalid = false;
441471
boolean allowNewLines = "description".equals(fieldName);
@@ -479,7 +509,8 @@ private EarlyModRegistryInfo(LoadingPlugin loadingPlugin, ModInfo modInfo, boole
479509
this.direct = direct;
480510
}
481511

482-
private void register() {
512+
private void register(HashSet<String> loadedBundles) {
513+
loadDependencyBundlesForMod(loadedBundles, this.modInfo);
483514
FoxLauncher.getFoxClassLoader().addFileToClassLoader(this.modInfo);
484515
modContainers.put(this.modInfo.id, new ModContainer(this.loadingPlugin, this.modInfo));
485516
}

loader/src/main/java/com/fox2code/foxloader/loader/java/JavaLoadingPlugin.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.io.File;
4040
import java.io.IOException;
4141
import java.lang.reflect.Constructor;
42+
import java.lang.reflect.Field;
43+
import java.lang.reflect.Modifier;
4244
import java.util.ArrayList;
4345
import java.util.List;
4446
import java.util.logging.Level;
@@ -122,9 +124,23 @@ public void preLoadModContainer(@NotNull ModContainer modContainer) {
122124
// We are guaranteed to have created that mod info ourselves, so the cast is fine.
123125
JavaModInfo modInfo = (JavaModInfo) modContainer.getModInfo();
124126
if (modInfo.main != null && !modInfo.main.isEmpty()) {
125-
Constructor<? extends Mod> constructor = Class.forName(
126-
modInfo.main, false, FoxLauncher.getFoxClassLoader())
127-
.asSubclass(Mod.class).getConstructor();
127+
Class<? extends Mod> cls = Class.forName(modInfo.main, false,
128+
FoxLauncher.getFoxClassLoader()).asSubclass(Mod.class);
129+
try {
130+
// This is to support kotlin objects as Mod.
131+
Field field = cls.getDeclaredField("INSTANCE");
132+
if (Modifier.isPublic(field.getModifiers()) &&
133+
Modifier.isStatic(field.getModifiers()) &&
134+
Modifier.isFinal(field.getModifiers()) &&
135+
field.getType() == cls) {
136+
Mod mod = (Mod) field.get(null);
137+
if (mod != null && mod.getModContainer() == modContainer) {
138+
return mod;
139+
}
140+
}
141+
} catch (NoSuchFieldException ignored) {}
142+
143+
Constructor<? extends Mod> constructor = cls.getConstructor();
128144
if (constructor.getDeclaringClass().getClassLoader() ==
129145
JavaLoadingPlugin.class.getClassLoader()) {
130146
constructor.setAccessible(true);

loader/src/main/java/com/fox2code/foxloader/loader/java/JavaModInfo.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@
3131
import java.io.File;
3232
import java.io.IOError;
3333
import java.io.IOException;
34+
import java.util.Arrays;
35+
import java.util.Collection;
36+
import java.util.Collections;
37+
import java.util.List;
3438
import java.util.jar.Attributes;
35-
import java.util.jar.JarFile;
3639

3740
public final class JavaModInfo extends ModInfo {
3841
private static final Attributes.Name MOD_CLASS_TRANSFORMER = new Attributes.Name("ModClassTransformer");
@@ -41,6 +44,8 @@ public final class JavaModInfo extends ModInfo {
4144
private static final Attributes.Name MOD_MIXIN = new Attributes.Name("ModMixin");
4245
private static final Attributes.Name MOD_MAIN = new Attributes.Name("ModMain");
4346
private static final Attributes.Name FOR_FOX_LOADER_VERSION = new Attributes.Name("For-FoxLoader-Version");
47+
private static final Attributes.Name REQUEST_FOXLOADER_DEPENDENCY_BUNDLES =
48+
new Attributes.Name("Request-FoxLoader-Dependency-Bundles");
4449
public static final JavaModInfo FOX_LOADER_MOD_INFO;
4550

4651
static {
@@ -54,6 +59,7 @@ public final class JavaModInfo extends ModInfo {
5459
}
5560
}
5661

62+
public final List<String> requestedDependencyBundles;
5763
public final String forFoxLoaderVersion;
5864
public final String classTransformer;
5965
public final String loadingPlugin;
@@ -69,6 +75,13 @@ public final class JavaModInfo extends ModInfo {
6975

7076
private JavaModInfo(File file, String jarPath, Attributes mainAttributes) throws IOException {
7177
super(file, jarPath, mainAttributes);
78+
String requestedDependencyBundlesText = mainAttributes.getValue(REQUEST_FOXLOADER_DEPENDENCY_BUNDLES);
79+
if (requestedDependencyBundlesText == null || requestedDependencyBundlesText.isEmpty()) {
80+
this.requestedDependencyBundles = Collections.emptyList();
81+
} else {
82+
this.requestedDependencyBundles = Collections.unmodifiableList(
83+
Arrays.asList(requestedDependencyBundlesText.split(",")));
84+
}
7285
this.forFoxLoaderVersion = mainAttributes.getValue(FOR_FOX_LOADER_VERSION);
7386
this.classTransformer = mainAttributes.getValue(MOD_CLASS_TRANSFORMER);
7487
this.loadingPlugin = mainAttributes.getValue(MOD_LOADING_PLUGIN);
@@ -84,6 +97,7 @@ private JavaModInfo(File file, String jarPath, Attributes mainAttributes) throws
8497
JavaModInfo(File file, String jarPath, String id, String name, String version, String description, String authors, String iconPath,
8598
String environment, boolean unofficial, long loadOrderPriority, String main) throws IOException {
8699
super(file, jarPath, id, name, version, description, authors, iconPath, environment, unofficial, loadOrderPriority);
100+
this.requestedDependencyBundles = Collections.emptyList();
87101
this.forFoxLoaderVersion = BuildConfig.FOXLOADER_VERSION;
88102
this.classTransformer = null;
89103
this.loadingPlugin = null;
@@ -103,4 +117,9 @@ private JavaModInfo(File file, String jarPath, Attributes mainAttributes) throws
103117
public final boolean isJavaArchive() {
104118
return true;
105119
}
120+
121+
@Override
122+
public Collection<String> getRequestedDependencyBundles() {
123+
return this.requestedDependencyBundles;
124+
}
106125
}

patching/generate.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ static void generateBuildConfig0(File buildConfigSrc, Project project) {
5757
final String REBUILD_VERSION = project['rebuild.version']
5858
final String FOX_EVENTS_VERSION = project['fox-events.version']
5959
final String FOX_FLEX_VER_VERSION = project['fox-flex-ver.version']
60+
final String JNA_VERSION = project['jna.version']
61+
final String KOTLIN_VERSION = project['kotlin.version']
62+
final String KOTLINX_ATOMICFU_VERSION = project['kotlinx.atomicfu.version']
63+
final String KOTLINX_COROUTINES_VERSION = project['kotlinx.coroutines.version']
64+
final String KOTLINX_DATETIME_VERSION = project['kotlinx.datetime.version']
65+
final String KOTLINX_IO_VERSION = project['kotlinx.io.version']
66+
final String KOTLINX_METADATA_VERSION = project['kotlinx.metadata.version']
67+
final String KOTLINX_SERIALIZATION_VERSION = project['kotlinx.serialization.version']
6068
FileOutputStream fileOutputStream = new FileOutputStream(buildConfigSrc)
6169
try {
6270
PrintStream printStream = new PrintStream(fileOutputStream)
@@ -94,6 +102,14 @@ static void generateBuildConfig0(File buildConfigSrc, Project project) {
94102
printStream.println(" public static final String REBUILD_VERSION = \"" + REBUILD_VERSION + "\";")
95103
printStream.println(" public static final String FOX_EVENTS_VERSION = \"" + FOX_EVENTS_VERSION + "\";")
96104
printStream.println(" public static final String FOX_FLEX_VER_VERSION = \"" + FOX_FLEX_VER_VERSION + "\";")
105+
printStream.println(" public static final String JNA_VERSION = \"" + JNA_VERSION + "\";")
106+
printStream.println(" public static final String KOTLIN_VERSION = \"" + KOTLIN_VERSION + "\";")
107+
printStream.println(" public static final String KOTLINX_ATOMICFU_VERSION = \"" + KOTLINX_ATOMICFU_VERSION + "\";")
108+
printStream.println(" public static final String KOTLINX_COROUTINES_VERSION = \"" + KOTLINX_COROUTINES_VERSION + "\";")
109+
printStream.println(" public static final String KOTLINX_DATETIME_VERSION = \"" + KOTLINX_DATETIME_VERSION + "\";")
110+
printStream.println(" public static final String KOTLINX_IO_VERSION = \"" + KOTLINX_IO_VERSION + "\";")
111+
printStream.println(" public static final String KOTLINX_METADATA_VERSION = \"" + KOTLINX_METADATA_VERSION + "\";")
112+
printStream.println(" public static final String KOTLINX_SERIALIZATION_VERSION = \"" + KOTLINX_SERIALIZATION_VERSION + "\";")
97113
printStream.println("}")
98114
} finally {
99115
fileOutputStream.close()

0 commit comments

Comments
 (0)