fix: Isolate mixins in alternative source sets

This commit is contained in:
Linnea Gräf
2025-03-18 21:40:32 +01:00
parent 40ce4f8567
commit 4c5ddd2fdd
11 changed files with 205 additions and 29 deletions

11
src/main/kotlin/Compat.kt Normal file
View File

@@ -0,0 +1,11 @@
package moe.nea.firmament
import moe.nea.firmament.util.compatloader.CompatMeta
import moe.nea.firmament.util.compatloader.ICompatMeta
@CompatMeta
object Compat : ICompatMeta {
override fun shouldLoad(): Boolean {
return true
}
}

View File

@@ -31,6 +31,7 @@ import moe.nea.firmament.features.mining.PickaxeAbility
import moe.nea.firmament.features.mining.PristineProfitTracker
import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.features.world.Waypoints
import moe.nea.firmament.util.compatloader.ICompatMeta
import moe.nea.firmament.util.data.DataHolder
object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "features", ::Config) {
@@ -83,17 +84,18 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
fun subscribeEvents() {
SubscriptionList.allLists.forEach { list ->
runCatching {
list.provideSubscriptions {
it.owner.javaClass.classes.forEach {
runCatching { it.getDeclaredField("INSTANCE").get(null) }
if (ICompatMeta.shouldLoad(list.javaClass.name))
runCatching {
list.provideSubscriptions {
it.owner.javaClass.classes.forEach {
runCatching { it.getDeclaredField("INSTANCE").get(null) }
}
subscribeSingleEvent(it)
}
subscribeSingleEvent(it)
}.getOrElse {
// TODO: allow annotating source sets to specifically opt out of loading for mods, maybe automatically
Firmament.logger.info("Ignoring events from $list, likely due to a missing compat mod.", it)
}
}.getOrElse {
// TODO: allow annotating source sets to specifically opt out of loading for mods, maybe automatically
Firmament.logger.info("Ignoring events from $list, likely due to a missing compat mod.", it)
}
}
}

View File

@@ -6,7 +6,7 @@ import kotlin.reflect.KClass
import kotlin.streams.asSequence
import moe.nea.firmament.Firmament
abstract class CompatLoader<T : Any>(val kClass: Class<T>) {
open class CompatLoader<T : Any>(val kClass: Class<T>) {
constructor(kClass: KClass<T>) : this(kClass.java)
val loader: ServiceLoader<T> = ServiceLoader.load(kClass)

View File

@@ -0,0 +1,48 @@
package moe.nea.firmament.util.compatloader
import java.util.ServiceLoader
import moe.nea.firmament.events.subscription.SubscriptionList
import moe.nea.firmament.init.AutoDiscoveryPlugin
import moe.nea.firmament.util.ErrorUtil
/**
* Declares the compat meta interface for the current source set.
* This is used by [CompatLoader], [SubscriptionList], and [AutoDiscoveryPlugin]. Annotate a [ICompatMeta] object with
* this.
*/
annotation class CompatMeta
interface ICompatMetaGen {
fun owns(className: String): Boolean
val meta: ICompatMeta
}
interface ICompatMeta {
fun shouldLoad(): Boolean
companion object {
val allMetas = ServiceLoader
.load(ICompatMetaGen::class.java)
.toList()
fun shouldLoad(className: String): Boolean {
// TODO: replace this with a more performant package lookup
val meta = if (ErrorUtil.aggressiveErrors) {
val fittingMetas = allMetas.filter { it.owns(className) }
require(fittingMetas.size == 1) { "Orphaned or duplicate owned class $className (${fittingMetas.map { it.meta }}). Consider adding a @CompatMeta object." }
fittingMetas.single()
} else {
allMetas.firstOrNull { it.owns(className) }
}
return meta?.meta?.shouldLoad() ?: true
}
}
}
object CompatHelper {
fun isOwnedByPackage(className: String, vararg packages: String): Boolean {
// TODO: create package lookup structure once
val packageName = className.substringBeforeLast('.')
return packageName in packages
}
}