diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java index 5b99aa60c..1b56b8f1b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackExportTask.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.Zipper; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -75,7 +76,7 @@ public class HMCLModpackExportTask extends Task { Version mv = repository.getResolvedVersion(version); String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)) - .orElseThrow(() -> new IllegalStateException("Cannot parse the version of " + version)); + .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); zip.putTextFile(JsonUtils.GSON.toJson(mv.setJar(gameVersion)), "minecraft/pack.json"); // Making "jar" to gameVersion is to be compatible with old HMCL. zip.putTextFile(JsonUtils.GSON.toJson(modpack.setGameVersion(gameVersion)), "modpack.json"); // Newer HMCL only reads 'gameVersion' field. } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 88f458b75..816390e44 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -123,7 +123,7 @@ public final class ModpackHelper { return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) .whenComplete(Schedulers.defaultScheduler(), success, failure) .thenComposeAsync(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)); - else throw new IllegalStateException("Unrecognized modpack: " + modpack); + else throw new IllegalArgumentException("Unrecognized modpack: " + modpack); } public static Task getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration configuration) throws UnsupportedModpackException, MismatchedModpackTypeException { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java index 0c9208654..5aa2f2564 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java @@ -18,9 +18,11 @@ package org.jackhuang.hmcl.ui.download; import javafx.scene.Node; +import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.VersionMismatchException; +import org.jackhuang.hmcl.download.fabric.FabricInstallTask; import org.jackhuang.hmcl.download.game.LibraryDownloadException; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.Version; @@ -114,9 +116,10 @@ public final class InstallerWizardProvider implements WizardProvider { } else { Controllers.dialog(i18n("install.failed.downloading.detail", ((DownloadException) exception).getUrl()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageType.ERROR, next); } - } else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException) { + } else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException || + exception instanceof FabricInstallTask.UnsupportedFabricInstallationException) { Controllers.dialog(i18n("install.failed.optifine_conflict"), i18n("install.failed"), MessageType.ERROR, next); - } else if (exception instanceof UnsupportedOperationException) { + } else if (exception instanceof DefaultDependencyManager.UnsupportedLibraryInstallerException) { Controllers.dialog(i18n("install.failed.install_online"), i18n("install.failed"), MessageType.ERROR, next); } else if (exception instanceof VersionMismatchException) { VersionMismatchException e = ((VersionMismatchException) exception); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 4e750e6d5..7fea8ad54 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -132,7 +132,7 @@ install.failed.downloading=Failed to install due to some files not downloaded su install.failed.downloading.detail=Failed to download file: %s install.failed.downloading.timeout=Download timed out: %s install.failed.install_online=Unable to recognize what you provided installer file is -install.failed.optifine_conflict=OptiFine and Forge are both installed simultaneously on Minecraft 1.13 +install.failed.optifine_conflict=Fabric, OptiFine and Forge are both installed simultaneously on Minecraft 1.13 install.failed.version_mismatch=The library requires game version %s, but actual version is %s. install.installer.choose=Choose a %s version install.installer.fabric=Fabric diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java index 9debbf58c..dea2eacf3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -94,7 +94,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager { VersionList versionList = getVersionList(libraryId); return versionList.loadAsync(gameVersion, getDownloadProvider()) .thenComposeAsync(() -> installLibraryAsync(baseVersion, versionList.getVersion(gameVersion, libraryVersion) - .orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion)))); + .orElseThrow(() -> new IOException("Remote library " + libraryId + " has no version " + libraryVersion)))); } @Override @@ -126,12 +126,15 @@ public class DefaultDependencyManager extends AbstractDependencyManager { } catch (IOException ignore) { } - throw new UnsupportedOperationException("Library cannot be recognized"); + throw new UnsupportedLibraryInstallerException(); }) .thenApplyAsync(oldVersion::addPatch) .thenComposeAsync(repository::save); } + public static class UnsupportedLibraryInstallerException extends Exception { + } + /** * Remove installed library. * Will try to remove libraries and patches. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java index 832065e76..bcce2df39 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java @@ -65,7 +65,7 @@ public final class FabricInstallTask extends Task { } @Override - public void preExecute() { + public void preExecute() throws Exception { if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass())) throw new UnsupportedFabricInstallationException(); } @@ -128,6 +128,6 @@ public final class FabricInstallTask extends Task { return new Version("net.fabricmc", loaderVersion, 30000, arguments, mainClass, libraries); } - public static class UnsupportedFabricInstallationException extends UnsupportedOperationException { + public static class UnsupportedFabricInstallationException extends Exception { } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index 3b5eb6f92..16b71d71f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -38,6 +38,7 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.SystemUtils; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -233,7 +234,7 @@ public class ForgeNewInstallTask extends Task { List args = processor.getArgs().stream().map(arg -> { String parsed = parseLiteral(arg, data, ExceptionalFunction.identity()); if (parsed == null) - throw new IllegalStateException("Invalid forge installation configuration"); + throw new IOException("Invalid forge installation configuration"); return parsed; }).collect(Collectors.toList()); @@ -242,7 +243,7 @@ public class ForgeNewInstallTask extends Task { LOG.info("Executing external processor " + processor.getJar().toString() + ", command line: " + new CommandBuilder().addAll(command).toString()); int exitCode = SystemUtils.callExternalProcess(command); if (exitCode != 0) - throw new IllegalStateException("Game processor exited abnormally"); + throw new IOException("Game processor exited abnormally"); for (Map.Entry entry : outputs.entrySet()) { Path artifact = Paths.get(entry.getKey()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java index d165f96d5..4db5a45ac 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.NetworkUtils; +import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -61,9 +62,9 @@ public final class VersionJsonDownloadTask extends Task { } @Override - public void execute() { + public void execute() throws IOException { RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion) - .orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository")); + .orElseThrow(() -> new IOException("Cannot find specific version " + gameVersion + " in remote repository")); String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl()); dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult)); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java index ba9ec6e7c..87be4a3f5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java @@ -149,7 +149,7 @@ public final class OptiFineInstallTask extends Task { gameRepository.getLibraryFile(version, optiFineLibrary).toString() ); if (exitCode != 0) - throw new IllegalStateException("OptiFine patcher failed"); + throw new IOException("OptiFine patcher failed"); } else { FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineLibrary).toPath()); } @@ -188,7 +188,7 @@ public final class OptiFineInstallTask extends Task { dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult())); } - public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException { + public static class UnsupportedOptiFineInstallationException extends Exception { } /** diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventBus.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventBus.java index 652194b4e..c74fa7330 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventBus.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventBus.java @@ -20,6 +20,7 @@ package org.jackhuang.hmcl.event; import org.jackhuang.hmcl.util.Logging; import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; /** * @@ -27,12 +28,11 @@ import java.util.HashMap; */ public final class EventBus { - private final HashMap, EventManager> events = new HashMap<>(); + private final ConcurrentHashMap, EventManager> events = new ConcurrentHashMap<>(); @SuppressWarnings("unchecked") public EventManager channel(Class clazz) { - if (!events.containsKey(clazz)) - events.put(clazz, new EventManager<>()); + events.putIfAbsent(clazz, new EventManager<>()); return (EventManager) events.get(clazz); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java index f3963323c..7e12d96fb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/EventManager.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.util.SimpleMultimap; import java.lang.ref.WeakReference; import java.util.EnumMap; import java.util.HashSet; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Consumer; /** @@ -31,7 +32,7 @@ import java.util.function.Consumer; public final class EventManager { private final SimpleMultimap> handlers - = new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), HashSet::new); + = new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), CopyOnWriteArraySet::new); public Consumer registerWeak(Consumer consumer) { register(new WeakListener(consumer)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/AssetObject.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/AssetObject.java index 2a0b05725..00655b80e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/AssetObject.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/AssetObject.java @@ -54,6 +54,6 @@ public final class AssetObject implements Validation { @Override public void validate() throws JsonParseException { if (StringUtils.isBlank(hash) || hash.length() < 2) - throw new IllegalStateException("AssetObject hash cannot be blank."); + throw new JsonParseException("AssetObject hash cannot be blank."); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java index e71a406d0..f1a9809ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java @@ -91,7 +91,7 @@ public class World { private void loadFromZipImpl(Path root) throws IOException { Path levelDat = root.resolve("level.dat"); if (!Files.exists(levelDat)) - throw new IllegalArgumentException("Not a valid world zip file since level.dat cannot be found."); + throw new IOException("Not a valid world zip file since level.dat cannot be found."); getWorldName(levelDat); } @@ -116,6 +116,9 @@ public class World { CompoundTag nbt = parseLevelDat(levelDat); CompoundTag data = nbt.get("Data"); + if (data == null) + throw new IOException("level.dat missing Data"); + if (data.get("LevelName") instanceof StringTag) worldName = data.get("LevelName").getValue(); else @@ -209,7 +212,7 @@ public class World { return Files.list(savesDir).flatMap(world -> { try { return Stream.of(new World(world)); - } catch (IOException | IllegalArgumentException e) { + } catch (IOException e) { Logging.LOG.log(Level.WARNING, "Failed to read world " + world, e); return Stream.empty(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java index c2a72dfe7..3f35afa0e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.Zipper; import java.io.File; +import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; @@ -80,7 +81,7 @@ public class MultiMCModpackExportTask extends Task { LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId)); String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId)) - .orElseThrow(() -> new IllegalStateException("Cannot parse the version of " + versionId)); + .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); List components = new ArrayList<>(); components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, "net.minecraft", gameVersion)); analyzer.getVersion(FORGE).ifPresent(forgeVersion -> diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 8b3a7e9c7..7398f0345 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -292,7 +292,7 @@ public class FileDownloadTask extends Task { } if (downloaded != contentLength) - throw new IllegalStateException("Unexpected file size: " + downloaded + ", expected: " + contentLength); + throw new IOException("Unexpected file size: " + downloaded + ", expected: " + contentLength); // Integrity check if (integrityCheck != null) { @@ -312,7 +312,7 @@ public class FileDownloadTask extends Task { } return; - } catch (IOException | IllegalStateException e) { + } catch (IOException e) { if (temp != null) temp.toFile().delete(); exception = e; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index 4c4d35047..9a29c199c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -64,7 +64,7 @@ public final class TaskExecutor { Logging.LOG.log(Level.WARNING, "An exception occurred in task execution", exception); Throwable resolvedException = resolveException(exception); - if (resolvedException instanceof RuntimeException) { + if (resolvedException instanceof RuntimeException && !(resolvedException instanceof CancellationException)) { // Track uncaught RuntimeException which are thrown mostly by our mistake if (uncaughtExceptionHandler != null) uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), resolvedException);