mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-09 15:29:20 -04:00
wip (2) Mod Dependencies (check or fail)
If a dependency is now given, Minosoft will check if this dependency is loaded
This commit is contained in:
parent
b68f1772a7
commit
cf02469411
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.modding.loading;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record ModIdentifier(UUID uuid, int versionId) {
|
||||
public static ModIdentifier serialize(JsonObject json) {
|
||||
return new ModIdentifier(Util.getUUIDFromString(json.get("uuid").getAsString()), json.get("versionId").getAsInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("uuid=%s, versionId=%d", uuid, versionId);
|
||||
}
|
||||
}
|
@ -15,15 +15,12 @@ package de.bixilon.minosoft.modding.loading;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ModInfo {
|
||||
final UUID uuid;
|
||||
final int versionId;
|
||||
final String versionName;
|
||||
final String name;
|
||||
final String[] authors;
|
||||
@ -32,11 +29,11 @@ public class ModInfo {
|
||||
final String mainClass;
|
||||
final HashSet<ModDependency> hardDependencies = new HashSet<>();
|
||||
final HashSet<ModDependency> softDependencies = new HashSet<>();
|
||||
private final ModIdentifier modIdentifier;
|
||||
LoadingInfo loadingInfo;
|
||||
|
||||
public ModInfo(JsonObject json) throws ModLoadingException {
|
||||
this.uuid = Util.getUUIDFromString(json.get("uuid").getAsString());
|
||||
this.versionId = json.get("versionId").getAsInt();
|
||||
this.modIdentifier = ModIdentifier.serialize(json);
|
||||
this.versionName = json.get("versionName").getAsString();
|
||||
this.name = json.get("name").getAsString();
|
||||
JsonArray authors = json.get("authors").getAsJsonArray();
|
||||
@ -83,12 +80,18 @@ public class ModInfo {
|
||||
return loadingInfo;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
return modIdentifier.uuid();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getVersionId() {
|
||||
return versionId;
|
||||
return modIdentifier.versionId();
|
||||
}
|
||||
|
||||
public ModIdentifier getModIdentifier() {
|
||||
return modIdentifier;
|
||||
}
|
||||
|
||||
public String getVersionName() {
|
||||
@ -99,28 +102,16 @@ public class ModInfo {
|
||||
return name;
|
||||
}
|
||||
|
||||
public HashSet<ModDependency> getHardDependencies() {
|
||||
return hardDependencies;
|
||||
}
|
||||
|
||||
public HashSet<ModDependency> getSoftDependencies() {
|
||||
return softDependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("name=\"%s\", uuid=%s, versionName=\"%s\", versionId=%d", getName(), getUUID(), getVersionName(), getVersionId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode() * versionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (super.equals(obj)) {
|
||||
return true;
|
||||
}
|
||||
if (this.hashCode() != obj.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
ModInfo their = (ModInfo) obj;
|
||||
return getUUID().equals(their.getUUID()) && getVersionId() == their.getVersionId();
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,12 @@ import de.bixilon.minosoft.util.Util;
|
||||
import org.xeustechnologies.jcl.JarClassLoader;
|
||||
import org.xeustechnologies.jcl.JclObjectFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -31,9 +35,11 @@ import java.util.zip.ZipFile;
|
||||
|
||||
public class ModLoader {
|
||||
public static final int CURRENT_MODDING_API_VERSION = 1;
|
||||
public static final LinkedList<MinosoftMod> mods = new LinkedList<>();
|
||||
public static final ConcurrentHashMap<UUID, MinosoftMod> mods = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public static void loadMods(CountUpAndDownLatch progress) throws Exception {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
Log.info("Start loading mods...");
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), Util.getThreadFactory("ModLoader"));
|
||||
|
||||
@ -53,7 +59,7 @@ public class ModLoader {
|
||||
executor.execute(() -> {
|
||||
MinosoftMod mod = loadMod(progress, modFile);
|
||||
if (mod != null) {
|
||||
mods.add(mod);
|
||||
mods.put(mod.getInfo().getModIdentifier().uuid(), mod);
|
||||
}
|
||||
latch.countDown();
|
||||
});
|
||||
@ -67,46 +73,92 @@ public class ModLoader {
|
||||
|
||||
progress.addCount(mods.size() * ModPhases.values().length); // count * mod phases
|
||||
|
||||
// sort for priority
|
||||
mods.sort((a, b) -> {
|
||||
if (a == null || b == null) {
|
||||
|
||||
// check if all dependencies are available
|
||||
modLoop:
|
||||
for (Map.Entry<UUID, MinosoftMod> modEntry : mods.entrySet()) {
|
||||
ModInfo currentModInfo = modEntry.getValue().getInfo();
|
||||
|
||||
for (ModDependency dependency : currentModInfo.getHardDependencies()) {
|
||||
ModInfo info = getModInfoByUUID(dependency.getUUID());
|
||||
if (info == null) {
|
||||
Log.warn("Could not satisfy mod dependency for mod %s (Requires %s)", modEntry.getValue().getInfo(), dependency.getUUID());
|
||||
mods.remove(modEntry.getKey());
|
||||
continue modLoop;
|
||||
}
|
||||
if (dependency.getVersionMinimum() < info.getModIdentifier().versionId()) {
|
||||
Log.warn("Could not satisfy mod dependency for mod %s (Requires %s version > %d)", modEntry.getValue().getInfo(), dependency.getUUID(), dependency.getVersionMinimum());
|
||||
mods.remove(modEntry.getKey());
|
||||
continue modLoop;
|
||||
}
|
||||
if (dependency.getVersionMaximum() > info.getModIdentifier().versionId()) {
|
||||
Log.warn("Could not satisfy mod dependency for mod %s (Requires %s version < %d)", modEntry.getValue().getInfo(), dependency.getUUID(), dependency.getVersionMaximum());
|
||||
mods.remove(modEntry.getKey());
|
||||
continue modLoop;
|
||||
}
|
||||
}
|
||||
for (ModDependency dependency : currentModInfo.getSoftDependencies()) {
|
||||
ModInfo info = getModInfoByUUID(dependency.getUUID());
|
||||
if (info == null) {
|
||||
Log.warn("Could not satisfy mod soft dependency for mod %s (Requires %s)", modEntry.getValue().getInfo(), dependency.getUUID());
|
||||
continue;
|
||||
}
|
||||
if (dependency.getVersionMinimum() < info.getModIdentifier().versionId()) {
|
||||
Log.warn("Could not satisfy mod dependency for mod %s (Requires %s version > %d)", modEntry.getValue().getInfo(), dependency.getUUID(), dependency.getVersionMinimum());
|
||||
continue;
|
||||
}
|
||||
if (dependency.getVersionMaximum() > info.getModIdentifier().versionId()) {
|
||||
Log.warn("Could not satisfy mod soft dependency for mod %s (Requires %s version < %d)", modEntry.getValue().getInfo(), dependency.getUUID(), dependency.getVersionMaximum());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
final TreeMap<UUID, MinosoftMod> sortedModMap = new TreeMap<>((mod1UUID, mod2UUID) -> {
|
||||
// ToDo: Load dependencies first
|
||||
if (mod1UUID == null || mod2UUID == null) {
|
||||
return 0;
|
||||
}
|
||||
return -(getLoadingPriorityOrDefault(b.getInfo()).ordinal() - getLoadingPriorityOrDefault(a.getInfo()).ordinal());
|
||||
return -(getLoadingPriorityOrDefault(mods.get(mod2UUID).getInfo()).ordinal() - getLoadingPriorityOrDefault(mods.get(mod1UUID).getInfo()).ordinal());
|
||||
});
|
||||
// ToDo: check dependencies
|
||||
|
||||
sortedModMap.putAll(mods);
|
||||
|
||||
for (ModPhases phase : ModPhases.values()) {
|
||||
Log.verbose(String.format("Mod loading phase changed: %s", phase));
|
||||
CountDownLatch modLatch = new CountDownLatch(mods.size());
|
||||
mods.forEach((instance) -> executor.execute(() -> {
|
||||
if (!instance.isEnabled()) {
|
||||
CountDownLatch modLatch = new CountDownLatch(sortedModMap.size());
|
||||
for (Map.Entry<UUID, MinosoftMod> entry : sortedModMap.entrySet()) {
|
||||
executor.execute(() -> {
|
||||
if (!entry.getValue().isEnabled()) {
|
||||
modLatch.countDown();
|
||||
progress.countDown();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!entry.getValue().start(phase)) {
|
||||
throw new ModLoadingException(String.format("Could not load mod %s", entry.getValue().getInfo()));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Log.warn(String.format("An error occurred while loading %s", entry.getValue().getInfo()));
|
||||
entry.getValue().setEnabled(false);
|
||||
}
|
||||
modLatch.countDown();
|
||||
progress.countDown();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!instance.start(phase)) {
|
||||
throw new ModLoadingException(String.format("Could not load nod %s", instance.getInfo()));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Log.warn(String.format("An error occurred while loading %s", instance.getInfo()));
|
||||
instance.setEnabled(false);
|
||||
}
|
||||
modLatch.countDown();
|
||||
progress.countDown();
|
||||
}));
|
||||
});
|
||||
}
|
||||
modLatch.await();
|
||||
}
|
||||
mods.forEach((instance) -> {
|
||||
if (instance.isEnabled()) {
|
||||
Minosoft.eventManagers.add(instance.getEventManager());
|
||||
|
||||
for (Map.Entry<UUID, MinosoftMod> entry : sortedModMap.entrySet()) {
|
||||
if (entry.getValue().isEnabled()) {
|
||||
Minosoft.eventManagers.add(entry.getValue().getEventManager());
|
||||
} else {
|
||||
mods.remove(instance);
|
||||
mods.remove(entry.getKey());
|
||||
}
|
||||
});
|
||||
Log.info("Loading of %d mods finished!", mods.size());
|
||||
}
|
||||
Log.info("Loading of %d mods finished in %dms!", sortedModMap.size(), (System.currentTimeMillis() - startTime));
|
||||
}
|
||||
|
||||
public static MinosoftMod loadMod(CountUpAndDownLatch progress, File file) {
|
||||
@ -144,12 +196,21 @@ public class ModLoader {
|
||||
return Priorities.NORMAL;
|
||||
}
|
||||
|
||||
static boolean isModLoaded(ModInfo info) {
|
||||
for (MinosoftMod instance : mods) {
|
||||
if (instance.getInfo().getUUID().equals(info.getUUID())) {
|
||||
return true;
|
||||
}
|
||||
public static boolean isModLoaded(ModInfo info) {
|
||||
return mods.containsKey(info.getModIdentifier().uuid());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MinosoftMod getModByUUID(UUID uuid) {
|
||||
return mods.get(uuid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ModInfo getModInfoByUUID(UUID uuid) {
|
||||
MinosoftMod mod = getModByUUID(uuid);
|
||||
if (mod == null) {
|
||||
return null;
|
||||
}
|
||||
return false;
|
||||
return mod.getInfo();
|
||||
}
|
||||
}
|
||||
|
@ -18,5 +18,10 @@ public enum Priorities {
|
||||
LOW,
|
||||
NORMAL,
|
||||
HIGH,
|
||||
HIGHEST
|
||||
HIGHEST,
|
||||
ULTRA_HIGH; // this priority is even higher. Do not use in normal case!
|
||||
|
||||
public static Priorities getHigherPriority(Priorities priority) {
|
||||
return Priorities.values()[priority.ordinal() + 1];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user