aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/util/compatloader/CompatLoader.kt
blob: c3cc19c49e1a93d4794fd7d4824ebfb3b4a60d03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package moe.nea.firmament.util.compatloader

import java.util.ServiceLoader
import net.fabricmc.loader.api.FabricLoader
import kotlin.reflect.KClass
import kotlin.streams.asSequence
import moe.nea.firmament.Firmament
import moe.nea.firmament.util.ErrorUtil

open class CompatLoader<T : Any>(val kClass: Class<T>) {
	constructor(kClass: KClass<T>) : this(kClass.java)

	val loader: ServiceLoader<T> = ServiceLoader.load(kClass)
	val allValidInstances by lazy {
		val resources = kClass.classLoader.getResources("META-INF/services/${kClass.name}")
		val classes = resources
			.asSequence()
			.map { ErrorUtil.catch("Could not read service loader resource at $it") { it.readText() }.or { "" } }
			.flatMap { it.lineSequence() }
			.map { it.substringBefore('#').trim() }
			.filter { it.isNotBlank() }
			.mapNotNull {
				ErrorUtil.catch("Could not load class named $it for $kClass") {
					Class.forName(it,
					              false,
					              kClass.classLoader).asSubclass(kClass)
				}.or { null }
			}
			.toList()

		classes.asSequence()
			.filter { clazz ->
				runCatching {
					shouldLoad(clazz)
				}.getOrElse {
					Firmament.logger.error("Could not determine whether to load a ${kClass.name} subclass", it)
					false
				}
			}
			.mapNotNull { clazz ->
				runCatching {
					clazz.kotlin.objectInstance ?: clazz.getConstructor().newInstance()
				}.getOrElse {
					Firmament.logger.error(
						"Could not load desired instance ${clazz.name} for ${kClass.name}",
						it)
					null
				}
			}
			.toList()
	}
	val singleInstance by lazy { allValidInstances.singleOrNull() }

	open fun shouldLoad(type: Class<out T>): Boolean {
		return checkRequiredModsPresent(type)
	}

	fun checkRequiredModsPresent(type: Class<*>): Boolean {
		val requiredMods = type.getAnnotationsByType(RequireMod::class.java)
		return requiredMods.all { FabricLoader.getInstance().isModLoaded(it.modId) }
	}

	@Repeatable
	annotation class RequireMod(val modId: String)
}