diff --git a/src/main/java/de/bixilon/minosoft/modding/loader/ModList.kt b/src/main/java/de/bixilon/minosoft/modding/loader/ModList.kt
new file mode 100644
index 000000000..a6ef92df5
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/modding/loader/ModList.kt
@@ -0,0 +1,67 @@
+/*
+ * Minosoft
+ * Copyright (C) 2020-2022 Moritz Zwerger
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ *
+ * This software is not affiliated with Mojang AB, the original developer of Minecraft.
+ */
+
+package de.bixilon.minosoft.modding.loader
+
+import de.bixilon.minosoft.modding.loader.error.NoManifestError
+import de.bixilon.minosoft.modding.loader.mod.MinosoftMod
+
+class ModList(
+ val mods: MutableMap = mutableMapOf(),
+ val virtual: MutableMap = mutableMapOf(),
+) : Iterable {
+
+ inline operator fun minusAssign(mod: MinosoftMod) = remove(mod)
+
+ @Synchronized
+ fun remove(mod: MinosoftMod) {
+ val manifest = mod.manifest ?: throw NoManifestError(mod.path)
+ this.mods -= manifest.name
+ this.virtual -= manifest.name
+
+ manifest.packages?.provides?.let { this.virtual -= it }
+ }
+
+ inline operator fun plusAssign(mod: MinosoftMod) = add(mod)
+
+ @Synchronized
+ fun add(mod: MinosoftMod) {
+ val manifest = mod.manifest ?: throw NoManifestError(mod.path)
+
+ this.mods[manifest.name] = mod
+ this.virtual[manifest.name] = mod
+
+ manifest.packages?.provides?.let {
+ for (name in it) {
+ this.virtual[name] = mod
+ }
+ }
+ }
+
+ operator fun contains(name: String): Boolean = get(name) != null
+
+ @Synchronized
+ operator fun get(name: String): MinosoftMod? {
+ return mods[name] ?: virtual[name]
+ }
+
+ @Synchronized
+ operator fun plusAssign(list: ModList) {
+ this.mods += list.mods
+ this.virtual += list.virtual
+ }
+
+ override fun iterator(): Iterator {
+ return mods.values.iterator()
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/modding/loader/ModLoader.kt b/src/main/java/de/bixilon/minosoft/modding/loader/ModLoader.kt
index d07e5ef0c..31d90bd56 100644
--- a/src/main/java/de/bixilon/minosoft/modding/loader/ModLoader.kt
+++ b/src/main/java/de/bixilon/minosoft/modding/loader/ModLoader.kt
@@ -22,10 +22,7 @@ import de.bixilon.minosoft.assets.directory.DirectoryAssetsManager
import de.bixilon.minosoft.assets.file.ZipAssetsManager
import de.bixilon.minosoft.assets.util.FileUtil.readJson
import de.bixilon.minosoft.modding.loader.LoaderUtil.load
-import de.bixilon.minosoft.modding.loader.error.DuplicateModError
-import de.bixilon.minosoft.modding.loader.error.NoManifestError
-import de.bixilon.minosoft.modding.loader.error.NoModMainError
-import de.bixilon.minosoft.modding.loader.error.NoObjectMainError
+import de.bixilon.minosoft.modding.loader.error.*
import de.bixilon.minosoft.modding.loader.mod.MinosoftMod
import de.bixilon.minosoft.modding.loader.mod.ModMain
import de.bixilon.minosoft.modding.loader.mod.manifest.load.LoadM
@@ -43,7 +40,7 @@ object ModLoader {
private val BASE_PATH = RunConfiguration.HOME_DIRECTORY + "mods/"
private const val MANIFEST = "manifest.json"
private var latch: CountUpAndDownLatch? = null
- val mods: MutableMap = mutableMapOf()
+ val mods = ModList()
var currentPhase by watched(LoadingPhases.PRE_BOOT)
private set
var state by watched(PhaseStates.WAITING)
@@ -137,7 +134,7 @@ object ModLoader {
main!!.postInit()
}
- private fun inject(mods: MutableMap, file: File, phase: LoadingPhases, latch: CountUpAndDownLatch) {
+ private fun inject(list: ModList, file: File, phase: LoadingPhases, latch: CountUpAndDownLatch) {
if (!file.isDirectory && !file.name.endsWith(".jar") && !file.name.endsWith(".zip")) {
return
}
@@ -149,11 +146,20 @@ object ModLoader {
} else {
mod.processJar(file)
}
- val name = mod.manifest!!.name
- if (name in mods || name in this.mods) {
+ val manifest = mod.manifest!!
+ val name = manifest.name
+ if (name in list || name in this.mods) {
throw DuplicateModError(name)
}
- mods[name] = mod
+ manifest.packages?.provides?.let {
+ for (providedName in it) {
+ val provided = list[providedName] ?: this.mods[providedName]
+ if (provided != null) {
+ throw DuplicateProvidedError(mod, provided, providedName)
+ }
+ }
+ }
+ list += mod
mod.latch.dec()
} catch (exception: Throwable) {
Log.log(LogMessageType.MOD_LOADING, LogLevels.WARN) { "Error injecting mod: $file" }
@@ -186,18 +192,18 @@ object ModLoader {
state = PhaseStates.COMPLETE
return
}
- val mods: MutableMap = synchronizedMapOf()
+ val list = ModList(synchronizedMapOf(), synchronizedMapOf())
state = PhaseStates.INJECTING
var worker = UnconditionalWorker()
for (file in files) {
- worker += add@{ inject(mods, file, phase, inner) }
+ worker += add@{ inject(list, file, phase, inner) }
}
worker.work(inner)
state = PhaseStates.CONSTRUCTING
worker = UnconditionalWorker()
- for ((name, mod) in mods) {
+ for (mod in list) {
worker += {
try {
mod.construct()
@@ -205,7 +211,7 @@ object ModLoader {
} catch (error: Throwable) {
mod.latch.count = 0
error.printStackTrace()
- mods -= name
+ list -= mod
}
}
}
@@ -213,7 +219,7 @@ object ModLoader {
state = PhaseStates.POST_INIT
worker = UnconditionalWorker()
- for ((name, mod) in mods) {
+ for (mod in list) {
worker += {
try {
mod.postInit()
@@ -221,14 +227,14 @@ object ModLoader {
} catch (error: Throwable) {
mod.latch.count = 0
error.printStackTrace()
- mods -= name
+ list -= mod
}
}
}
worker.work(inner)
- this.mods += mods
+ this.mods += list
state = PhaseStates.COMPLETE
inner.dec()
if (phase == LoadingPhases.POST_BOOT) {
diff --git a/src/main/java/de/bixilon/minosoft/modding/loader/error/DuplicateProvidedError.kt b/src/main/java/de/bixilon/minosoft/modding/loader/error/DuplicateProvidedError.kt
new file mode 100644
index 000000000..ea72f7e71
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/modding/loader/error/DuplicateProvidedError.kt
@@ -0,0 +1,22 @@
+/*
+ * Minosoft
+ * Copyright (C) 2020-2022 Moritz Zwerger
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ *
+ * This software is not affiliated with Mojang AB, the original developer of Minecraft.
+ */
+
+package de.bixilon.minosoft.modding.loader.error
+
+import de.bixilon.minosoft.modding.loader.mod.MinosoftMod
+
+class DuplicateProvidedError(
+ val mod: MinosoftMod,
+ val providedBy: MinosoftMod,
+ val virtualName: String,
+) : Exception("${mod.manifest?.name} provides $virtualName, but already provided by ${providedBy.manifest?.name}!")