mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-11 08:27:29 -04:00
wip mod dependencies, improve mod loading (reuse threads), rename StaticConfiguration.homeDir to HOME_DIR, remove old code
This commit is contained in:
parent
3d169d8e37
commit
4b4ae6903d
@ -12,6 +12,7 @@ In the root folder of your jar file (the mod) must be a file called `mod.json`.
|
|||||||
"Example dev"
|
"Example dev"
|
||||||
],
|
],
|
||||||
"name": "Example Mod",
|
"name": "Example Mod",
|
||||||
|
"moddingAPIVersion": 1,
|
||||||
"identifier": "example",
|
"identifier": "example",
|
||||||
"mainClass": "de.example.mod.Main",
|
"mainClass": "de.example.mod.Main",
|
||||||
"loading": {
|
"loading": {
|
||||||
@ -43,7 +44,8 @@ In the root folder of your jar file (the mod) must be a file called `mod.json`.
|
|||||||
- `uuid` is a unique id for the mod. Generate 1 and keep it in all versions (used for dependencies, etc). **Required**
|
- `uuid` is a unique id for the mod. Generate 1 and keep it in all versions (used for dependencies, etc). **Required**
|
||||||
- `versionId` like in android there is a numeric version id. It is used to compare between versions (and as identifier). **Required**
|
- `versionId` like in android there is a numeric version id. It is used to compare between versions (and as identifier). **Required**
|
||||||
- `versionName`, `authors`, `name` is the classic implementation of metadata. Can be anything, will be displayed in the mod list. **Required**
|
- `versionName`, `authors`, `name` is the classic implementation of metadata. Can be anything, will be displayed in the mod list. **Required**
|
||||||
- `identifier` is the prefix of items (for Minecraft it is `minecraft`). Aka the thing before the ``. **Required**
|
- `moddingAPIVersion` Modding API version of minosoft. Currently `1` **Required**
|
||||||
|
- `identifier` is the prefix of items (for Minecraft it is `minecraft`). Aka the thing before the `:`. **Required**
|
||||||
- `mainClass` the Main class of your mod (self explaining). The main class needs to extent the abstract class `MinosoftMod`. **Required**
|
- `mainClass` the Main class of your mod (self explaining). The main class needs to extent the abstract class `MinosoftMod`. **Required**
|
||||||
- `loading` Loading attributes. **Optional**
|
- `loading` Loading attributes. **Optional**
|
||||||
- `priority` should the mod be loaded at the beginning or at the end. Possible values are `LOWEST`, `LOW`, `NORMAL`, `HIGH`, `HIGHEST` **Optional**
|
- `priority` should the mod be loaded at the beginning or at the end. Possible values are `LOWEST`, `LOW`, `NORMAL`, `HIGH`, `HIGHEST` **Optional**
|
||||||
@ -52,8 +54,8 @@ In the root folder of your jar file (the mod) must be a file called `mod.json`.
|
|||||||
- `soft` These mods are **optional** to work. Both use the following format: **Optional**
|
- `soft` These mods are **optional** to work. Both use the following format: **Optional**
|
||||||
- `uuid` the uuid of the mod to load. **Required**
|
- `uuid` the uuid of the mod to load. **Required**
|
||||||
- `version` Specifies the version you need to load. **Optional**
|
- `version` Specifies the version you need to load. **Optional**
|
||||||
- `minimum` Minimum versionId required. **Maximum, minimum or both**
|
- `minimum` Minimum versionId required. **Maximum, minimum, both or none**
|
||||||
- `maximum` Maximum versionId required. **Maximum, minimum or both**
|
- `maximum` Maximum versionId required. **Maximum, minimum, both or none**
|
||||||
|
|
||||||
## Mod loading (aka Main class)
|
## Mod loading (aka Main class)
|
||||||
Your main class must extend the following class: `de.bixilon.minosoft.MinosoftMod`.
|
Your main class must extend the following class: `de.bixilon.minosoft.MinosoftMod`.
|
||||||
|
@ -33,19 +33,19 @@ public class Configuration {
|
|||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public Configuration() throws IOException {
|
public Configuration() throws IOException {
|
||||||
File file = new File(StaticConfiguration.homeDir + "config/" + StaticConfiguration.CONFIG_FILENAME);
|
File file = new File(StaticConfiguration.HOME_DIR + "config/" + StaticConfiguration.CONFIG_FILENAME);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
// no configuration file
|
// no configuration file
|
||||||
InputStream input = getClass().getResourceAsStream("/config/" + StaticConfiguration.CONFIG_FILENAME);
|
InputStream input = getClass().getResourceAsStream("/config/" + StaticConfiguration.CONFIG_FILENAME);
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
throw new FileNotFoundException(String.format("[Config] Missing default config: %s!", StaticConfiguration.CONFIG_FILENAME));
|
throw new FileNotFoundException(String.format("[Config] Missing default config: %s!", StaticConfiguration.CONFIG_FILENAME));
|
||||||
}
|
}
|
||||||
File folder = new File(StaticConfiguration.homeDir + "config/");
|
File folder = new File(StaticConfiguration.HOME_DIR + "config/");
|
||||||
if (!folder.exists() && !folder.mkdirs()) {
|
if (!folder.exists() && !folder.mkdirs()) {
|
||||||
throw new IOException("[Config] Could not create config folder!");
|
throw new IOException("[Config] Could not create config folder!");
|
||||||
}
|
}
|
||||||
Files.copy(input, Paths.get(file.getAbsolutePath()));
|
Files.copy(input, Paths.get(file.getAbsolutePath()));
|
||||||
file = new File(StaticConfiguration.homeDir + "config/" + StaticConfiguration.CONFIG_FILENAME);
|
file = new File(StaticConfiguration.HOME_DIR + "config/" + StaticConfiguration.CONFIG_FILENAME);
|
||||||
}
|
}
|
||||||
config = Util.readJsonFromFile(file.getAbsolutePath());
|
config = Util.readJsonFromFile(file.getAbsolutePath());
|
||||||
int configVersion = getInt(ConfigurationPaths.CONFIG_VERSION);
|
int configVersion = getInt(ConfigurationPaths.CONFIG_VERSION);
|
||||||
@ -67,7 +67,7 @@ public class Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// write config to temp file, delete original config, rename temp file to original file to avoid conflicts if minosoft gets closed while saving the config
|
// write config to temp file, delete original config, rename temp file to original file to avoid conflicts if minosoft gets closed while saving the config
|
||||||
File tempFile = new File(StaticConfiguration.homeDir + "config/" + StaticConfiguration.CONFIG_FILENAME + ".tmp");
|
File tempFile = new File(StaticConfiguration.HOME_DIR + "config/" + StaticConfiguration.CONFIG_FILENAME + ".tmp");
|
||||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
FileWriter writer;
|
FileWriter writer;
|
||||||
try {
|
try {
|
||||||
|
@ -23,10 +23,11 @@ public class StaticConfiguration {
|
|||||||
public static final boolean COLORED_LOG = true; // the log should be colored with ANSI (does not affect base components)
|
public static final boolean COLORED_LOG = true; // the log should be colored with ANSI (does not affect base components)
|
||||||
public static final boolean LOG_RELATIVE_TIME = false; // prefix all log messages with the relative start time in milliseconds instead of the formatted time
|
public static final boolean LOG_RELATIVE_TIME = false; // prefix all log messages with the relative start time in milliseconds instead of the formatted time
|
||||||
|
|
||||||
public static String homeDir;
|
public static final String HOME_DIR;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Sets Config.homeDir to the correct folder per OS
|
// Sets Config.homeDir to the correct folder per OS
|
||||||
|
String homeDir;
|
||||||
homeDir = System.getProperty("user.home");
|
homeDir = System.getProperty("user.home");
|
||||||
if (!homeDir.endsWith(File.separator)) {
|
if (!homeDir.endsWith(File.separator)) {
|
||||||
homeDir += "/";
|
homeDir += "/";
|
||||||
@ -42,5 +43,6 @@ public class StaticConfiguration {
|
|||||||
// failed creating folder
|
// failed creating folder
|
||||||
throw new RuntimeException(String.format("Could not create home folder (%s)!", homeDir));
|
throw new RuntimeException(String.format("Could not create home folder (%s)!", homeDir));
|
||||||
}
|
}
|
||||||
|
HOME_DIR = folder.getAbsolutePath() + "/";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,6 +241,6 @@ public class AssetsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getAssetDiskPath(String hash) {
|
private static String getAssetDiskPath(String hash) {
|
||||||
return StaticConfiguration.homeDir + String.format("assets/objects/%s/%s.gz", hash.substring(0, 2), hash);
|
return StaticConfiguration.HOME_DIR + String.format("assets/objects/%s/%s.gz", hash.substring(0, 2), hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ public class Versions {
|
|||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
// check if mapping folder exist
|
// check if mapping folder exist
|
||||||
File mappingFolder = new File(StaticConfiguration.homeDir + "assets/mapping");
|
File mappingFolder = new File(StaticConfiguration.HOME_DIR + "assets/mapping");
|
||||||
if (!mappingFolder.exists()) {
|
if (!mappingFolder.exists()) {
|
||||||
if (mappingFolder.mkdirs()) {
|
if (mappingFolder.mkdirs()) {
|
||||||
Log.verbose("Created mappings folder.");
|
Log.verbose("Created mappings folder.");
|
||||||
@ -138,7 +138,7 @@ public class Versions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String fileName = StaticConfiguration.homeDir + String.format("assets/mapping/%s.tar.gz", version.getVersionName());
|
String fileName = StaticConfiguration.HOME_DIR + String.format("assets/mapping/%s.tar.gz", version.getVersionName());
|
||||||
HashMap<String, JsonObject> files;
|
HashMap<String, JsonObject> files;
|
||||||
try {
|
try {
|
||||||
files = Util.readJsonTarGzFile(fileName);
|
files = Util.readJsonTarGzFile(fileName);
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import de.bixilon.minosoft.util.Util;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ModDependency {
|
||||||
|
|
||||||
|
private final UUID uuid;
|
||||||
|
private Integer versionMinimum;
|
||||||
|
private Integer versionMaximum;
|
||||||
|
|
||||||
|
public ModDependency(UUID uuid, Integer versionMinimum, Integer versionMaximum) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.versionMinimum = versionMinimum;
|
||||||
|
this.versionMaximum = versionMaximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModDependency(UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModDependency serialize(JsonObject json) {
|
||||||
|
UUID uuid = Util.getUUIDFromString(json.get("uuid").getAsString());
|
||||||
|
Integer versionMinimum = null;
|
||||||
|
Integer versionMaximum = null;
|
||||||
|
|
||||||
|
if (json.has("version")) {
|
||||||
|
JsonObject version = json.getAsJsonObject("version");
|
||||||
|
if (version.has("minimum")) {
|
||||||
|
versionMinimum = version.get("minimum").getAsInt();
|
||||||
|
}
|
||||||
|
if (version.has("maximum")) {
|
||||||
|
versionMaximum = version.get("maximum").getAsInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ModDependency(uuid, versionMinimum, versionMaximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HashSet<ModDependency> serializeDependencyArray(JsonArray json) {
|
||||||
|
HashSet<ModDependency> result = new HashSet<>();
|
||||||
|
json.forEach((jsonElement -> result.add(serialize(jsonElement.getAsJsonObject()))));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersionMinimum() {
|
||||||
|
return versionMinimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersionMaximum() {
|
||||||
|
return versionMaximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = uuid.hashCode();
|
||||||
|
if (versionMinimum != null && versionMinimum > 0) {
|
||||||
|
result *= versionMinimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionMaximum != null && versionMaximum > 0) {
|
||||||
|
result *= versionMaximum;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (super.equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hashCode() != obj.hashCode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ModDependency their = (ModDependency) obj;
|
||||||
|
return getUUID().equals(their.getUUID()) && getVersionMaximum().equals(their.getVersionMaximum()) && getVersionMinimum().equals(their.getVersionMinimum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String result = uuid.toString();
|
||||||
|
if (versionMinimum != null) {
|
||||||
|
result += " >" + versionMinimum;
|
||||||
|
}
|
||||||
|
if (versionMaximum != null) {
|
||||||
|
result += " <" + versionMaximum;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import de.bixilon.minosoft.util.Util;
|
import de.bixilon.minosoft.util.Util;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@ -26,11 +27,14 @@ public class ModInfo {
|
|||||||
final String versionName;
|
final String versionName;
|
||||||
final String name;
|
final String name;
|
||||||
final String[] authors;
|
final String[] authors;
|
||||||
|
final int moddingAPIVersion;
|
||||||
final String identifier;
|
final String identifier;
|
||||||
final String mainClass;
|
final String mainClass;
|
||||||
|
final HashSet<ModDependency> hardDependencies = new HashSet<>();
|
||||||
|
final HashSet<ModDependency> softDependencies = new HashSet<>();
|
||||||
LoadingInfo loadingInfo;
|
LoadingInfo loadingInfo;
|
||||||
|
|
||||||
public ModInfo(JsonObject json) {
|
public ModInfo(JsonObject json) throws ModLoadingException {
|
||||||
this.uuid = Util.getUUIDFromString(json.get("uuid").getAsString());
|
this.uuid = Util.getUUIDFromString(json.get("uuid").getAsString());
|
||||||
this.versionId = json.get("versionId").getAsInt();
|
this.versionId = json.get("versionId").getAsInt();
|
||||||
this.versionName = json.get("versionName").getAsString();
|
this.versionName = json.get("versionName").getAsString();
|
||||||
@ -39,6 +43,10 @@ public class ModInfo {
|
|||||||
this.authors = new String[authors.size()];
|
this.authors = new String[authors.size()];
|
||||||
AtomicInteger i = new AtomicInteger();
|
AtomicInteger i = new AtomicInteger();
|
||||||
authors.forEach((authorElement) -> this.authors[i.getAndIncrement()] = authorElement.getAsString());
|
authors.forEach((authorElement) -> this.authors[i.getAndIncrement()] = authorElement.getAsString());
|
||||||
|
moddingAPIVersion = json.get("moddingAPIVersion").getAsInt();
|
||||||
|
if (moddingAPIVersion > ModLoader.CURRENT_MODDING_API_VERSION) {
|
||||||
|
throw new ModLoadingException(String.format("Mod was written with for a newer version of minosoft (moddingAPIVersion=%d, expected=%d)", moddingAPIVersion, ModLoader.CURRENT_MODDING_API_VERSION));
|
||||||
|
}
|
||||||
this.identifier = json.get("identifier").getAsString();
|
this.identifier = json.get("identifier").getAsString();
|
||||||
this.mainClass = json.get("mainClass").getAsString();
|
this.mainClass = json.get("mainClass").getAsString();
|
||||||
if (json.has("loading")) {
|
if (json.has("loading")) {
|
||||||
@ -48,6 +56,15 @@ public class ModInfo {
|
|||||||
this.loadingInfo.setLoadingPriority(Priorities.valueOf(loading.get("priority").getAsString()));
|
this.loadingInfo.setLoadingPriority(Priorities.valueOf(loading.get("priority").getAsString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (json.has("dependencies")) {
|
||||||
|
JsonObject dependencies = json.getAsJsonObject("dependencies");
|
||||||
|
if (dependencies.has("hard")) {
|
||||||
|
hardDependencies.addAll(ModDependency.serializeDependencyArray(dependencies.getAsJsonArray("hard")));
|
||||||
|
}
|
||||||
|
if (dependencies.has("soft")) {
|
||||||
|
softDependencies.addAll(ModDependency.serializeDependencyArray(dependencies.getAsJsonArray("soft")));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getAuthors() {
|
public String[] getAuthors() {
|
||||||
|
@ -24,63 +24,78 @@ import org.xeustechnologies.jcl.JclObjectFactory;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
public class ModLoader {
|
public class ModLoader {
|
||||||
static final LinkedList<MinosoftMod> mods = new LinkedList<>();
|
public static final int CURRENT_MODDING_API_VERSION = 1;
|
||||||
|
public static final LinkedList<MinosoftMod> mods = new LinkedList<>();
|
||||||
|
|
||||||
public static void loadMods(CountUpAndDownLatch progress) throws Exception {
|
public static void loadMods(CountUpAndDownLatch progress) throws Exception {
|
||||||
Log.verbose("Start loading mods...");
|
Log.verbose("Start loading mods...");
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), Util.getThreadFactory("ModLoader"));
|
||||||
|
|
||||||
// load all jars, parse the mod.json
|
// load all jars, parse the mod.json
|
||||||
// sort the list and prioritize
|
// sort the list and prioritize
|
||||||
// load all lists and dependencies async
|
// load all lists and dependencies async
|
||||||
HashSet<Callable<MinosoftMod>> callables = new HashSet<>();
|
File[] files = new File(StaticConfiguration.HOME_DIR + "mods").listFiles();
|
||||||
File[] files = new File(StaticConfiguration.homeDir + "mods").listFiles();
|
|
||||||
if (files == null) {
|
if (files == null) {
|
||||||
// no mods to load
|
// no mods to load
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CountDownLatch latch = new CountDownLatch(files.length);
|
||||||
for (File modFile : files) {
|
for (File modFile : files) {
|
||||||
if (modFile.isDirectory()) {
|
if (modFile.isDirectory()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
callables.add(() -> {
|
executor.execute(() -> {
|
||||||
MinosoftMod mod = loadMod(progress, modFile);
|
MinosoftMod mod = loadMod(progress, modFile);
|
||||||
if (mod != null) {
|
if (mod != null) {
|
||||||
mods.add(mod);
|
mods.add(mod);
|
||||||
}
|
}
|
||||||
return mod;
|
latch.countDown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
latch.await();
|
||||||
|
|
||||||
Util.executeInThreadPool("ModLoader", callables);
|
if (mods.size() == 0) {
|
||||||
|
Log.info("No mods to load.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
progress.addCount(mods.size() * ModPhases.values().length); // count * mod phases
|
progress.addCount(mods.size() * ModPhases.values().length); // count * mod phases
|
||||||
|
|
||||||
|
// sort for priority
|
||||||
mods.sort((a, b) -> {
|
mods.sort((a, b) -> {
|
||||||
if (a == null || b == null) {
|
if (a == null || b == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return -(getLoadingPriorityOrDefault(b.getInfo()).ordinal() - getLoadingPriorityOrDefault(a.getInfo()).ordinal());
|
return -(getLoadingPriorityOrDefault(b.getInfo()).ordinal() - getLoadingPriorityOrDefault(a.getInfo()).ordinal());
|
||||||
});
|
});
|
||||||
|
// ToDo: check dependencies
|
||||||
|
|
||||||
for (ModPhases phase : ModPhases.values()) {
|
for (ModPhases phase : ModPhases.values()) {
|
||||||
Log.verbose(String.format("Map loading phase changed: %s", phase));
|
Log.verbose(String.format("Map loading phase changed: %s", phase));
|
||||||
HashSet<Callable<MinosoftMod>> phaseLoaderCallables = new HashSet<>();
|
CountDownLatch modLatch = new CountDownLatch(mods.size());
|
||||||
mods.forEach((instance) -> phaseLoaderCallables.add(() -> {
|
mods.forEach((instance) -> {
|
||||||
if (!instance.isEnabled()) {
|
executor.execute(() -> {
|
||||||
return instance;
|
if (!instance.isEnabled()) {
|
||||||
}
|
modLatch.countDown();
|
||||||
if (!instance.start(phase)) {
|
progress.countDown();
|
||||||
Log.warn(String.format("An error occurred while loading %s", instance.getInfo()));
|
return;
|
||||||
instance.setEnabled(false);
|
}
|
||||||
}
|
if (!instance.start(phase)) {
|
||||||
progress.countDown();
|
Log.warn(String.format("An error occurred while loading %s", instance.getInfo()));
|
||||||
return instance;
|
instance.setEnabled(false);
|
||||||
}));
|
}
|
||||||
Util.executeInThreadPool("ModLoader", phaseLoaderCallables);
|
modLatch.countDown();
|
||||||
|
progress.countDown();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
modLatch.await();
|
||||||
}
|
}
|
||||||
mods.forEach((instance) -> {
|
mods.forEach((instance) -> {
|
||||||
if (instance.isEnabled()) {
|
if (instance.isEnabled()) {
|
||||||
@ -94,6 +109,7 @@ public class ModLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MinosoftMod loadMod(CountUpAndDownLatch progress, File file) {
|
public static MinosoftMod loadMod(CountUpAndDownLatch progress, File file) {
|
||||||
|
MinosoftMod instance;
|
||||||
try {
|
try {
|
||||||
Log.verbose(String.format("[MOD] Loading file %s", file.getAbsolutePath()));
|
Log.verbose(String.format("[MOD] Loading file %s", file.getAbsolutePath()));
|
||||||
progress.countUp();
|
progress.countUp();
|
||||||
@ -107,18 +123,17 @@ public class ModLoader {
|
|||||||
jcl.add(file.getAbsolutePath());
|
jcl.add(file.getAbsolutePath());
|
||||||
JclObjectFactory factory = JclObjectFactory.getInstance();
|
JclObjectFactory factory = JclObjectFactory.getInstance();
|
||||||
|
|
||||||
MinosoftMod instance = (MinosoftMod) factory.create(jcl, modInfo.getMainClass());
|
instance = (MinosoftMod) factory.create(jcl, modInfo.getMainClass());
|
||||||
instance.setInfo(modInfo);
|
instance.setInfo(modInfo);
|
||||||
Log.verbose(String.format("[MOD] Mod file loaded and added to classpath (%s)", modInfo));
|
Log.verbose(String.format("[MOD] Mod file loaded and added to classpath (%s)", modInfo));
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
progress.countDown();
|
} catch (IOException | ModLoadingException | NullPointerException e) {
|
||||||
return instance;
|
instance = null;
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.warn(String.format("Could not load mod: %s", file.getAbsolutePath()));
|
Log.warn(String.format("Could not load mod: %s", file.getAbsolutePath()));
|
||||||
}
|
}
|
||||||
progress.countDown(); // failed
|
progress.countDown(); // failed
|
||||||
return null;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Priorities getLoadingPriorityOrDefault(ModInfo info) {
|
private static Priorities getLoadingPriorityOrDefault(ModInfo info) {
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class ModLoadingException extends Exception {
|
||||||
|
public ModLoadingException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModLoadingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModLoadingException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModLoadingException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
@ -26,10 +26,9 @@ import java.net.URL;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.*;
|
import java.util.zip.*;
|
||||||
|
|
||||||
@ -246,17 +245,6 @@ public final class Util {
|
|||||||
return new BufferedInputStream(new URL(url).openStream());
|
return new BufferedInputStream(new URL(url).openStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> void executeInThreadPool(String name, Collection<Callable<T>> callables) throws InterruptedException {
|
|
||||||
ExecutorService phaseLoader = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), getThreadFactory(name));
|
|
||||||
phaseLoader.invokeAll(callables).forEach((tFuture -> {
|
|
||||||
try {
|
|
||||||
tFuture.get();
|
|
||||||
} catch (ExecutionException | InterruptedException ex) {
|
|
||||||
ex.getCause().printStackTrace();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ThreadFactory getThreadFactory(String threadName) {
|
public static ThreadFactory getThreadFactory(String threadName) {
|
||||||
return new ThreadFactoryBuilder().setNameFormat(threadName + "#%d").build();
|
return new ThreadFactoryBuilder().setNameFormat(threadName + "#%d").build();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user