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}!")