Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93837a28fa | ||
|
|
678f3b8eb7 | ||
|
|
0833fa9e7d | ||
|
|
369b9bb6cf | ||
|
|
c9b6f2f62f | ||
|
|
e2787af044 | ||
|
|
30c27bbb67 | ||
|
|
7aee7e33d0 | ||
|
|
a26061c388 | ||
|
|
11e66ac13c | ||
|
|
a2ac3c1f18 | ||
|
|
e7142e4032 | ||
|
|
0dd9dc6c17 | ||
|
|
a53bfed060 | ||
|
|
c5919f2719 | ||
|
|
fb24516b90 | ||
|
|
60fa4eb0a1 | ||
|
|
536576084d | ||
|
|
c62cc499c5 | ||
|
|
c11bbf8836 | ||
|
|
71c0cede24 | ||
|
|
ad16bbc96d | ||
|
|
520ffe4c09 |
No files matched your search
@@ -22,6 +22,7 @@ import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.CrashReporter;
|
||||
@@ -77,6 +78,7 @@ public final class Launcher extends Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
|
||||
TaskExecutor.setUncaughtExceptionHandler(new CrashReporter(false));
|
||||
|
||||
try {
|
||||
LOG.info("*** " + Metadata.TITLE + " ***");
|
||||
@@ -142,5 +144,5 @@ public final class Launcher extends Application {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
|
||||
public static final CrashReporter CRASH_REPORTER = new CrashReporter(true);
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.CredentialExpiredException;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
import org.jackhuang.hmcl.download.game.LibrariesUniqueTask;
|
||||
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
|
||||
import org.jackhuang.hmcl.launch.NotDecompressingNativesException;
|
||||
import org.jackhuang.hmcl.launch.PermissionException;
|
||||
@@ -132,7 +131,7 @@ public final class LauncherHelper {
|
||||
private void launch0() {
|
||||
HMCLGameRepository repository = profile.getRepository();
|
||||
DefaultDependencyManager dependencyManager = profile.getDependency();
|
||||
Version version = MaintainTask.maintain(LibrariesUniqueTask.unique(repository.getResolvedVersion(selectedVersion)));
|
||||
Version version = MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion));
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
||||
|
||||
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -482,7 +483,7 @@ public final class VersionSetting {
|
||||
else if (isUsesCustomJavaDir()) {
|
||||
try {
|
||||
return JavaVersion.fromExecutable(Paths.get(getJavaDir()));
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
return null; // Custom Java Directory not found,
|
||||
}
|
||||
} else if (StringUtils.isNotBlank(getJava())) {
|
||||
@@ -537,6 +538,7 @@ public final class VersionSetting {
|
||||
.setGameDir(gameDir)
|
||||
.setJava(javaVersion)
|
||||
.setVersionName(Metadata.TITLE)
|
||||
.setVersionType(Metadata.TITLE)
|
||||
.setProfileName(Metadata.TITLE)
|
||||
.setMinecraftArgs(getMinecraftArgs())
|
||||
.setJavaArgs(getJavaArgs())
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -40,11 +41,16 @@ public class InstallerItem extends BorderPane {
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
|
||||
{
|
||||
if (version != null) {
|
||||
TwoLineListItem item = new TwoLineListItem();
|
||||
item.setTitle(artifact);
|
||||
item.setSubtitle(i18n("archive.version") + ": " + version);
|
||||
setCenter(item);
|
||||
} else {
|
||||
Label label = new Label(artifact);
|
||||
label.setStyle("-fx-font-size: 15px;");
|
||||
BorderPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||
setCenter(label);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -23,7 +23,12 @@ import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Insets;
|
||||
@@ -113,7 +118,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
launchButton.setPrefHeight(55);
|
||||
launchButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||
launchButton.getStyleClass().add("jfx-button-raised");
|
||||
launchButton.setOnMouseClicked(e -> launch());
|
||||
launchButton.setOnAction(e -> launch());
|
||||
launchButton.setDefaultButton(true);
|
||||
launchButton.setClip(new Rectangle(-100, -100, 310, 200));
|
||||
{
|
||||
VBox graphic = new VBox();
|
||||
|
||||
@@ -26,11 +26,14 @@ import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameInstallTask;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.ModpackUpdateTask;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseInstallTask;
|
||||
import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask;
|
||||
@@ -86,6 +89,8 @@ public final class TaskListPane extends StackPane {
|
||||
|
||||
if (task instanceof GameAssetDownloadTask) {
|
||||
task.setName(i18n("assets.download_all"));
|
||||
} else if (task instanceof GameInstallTask) {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.game")));
|
||||
} else if (task instanceof ForgeInstallTask) {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.forge")));
|
||||
} else if (task instanceof LiteLoaderInstallTask) {
|
||||
|
||||
@@ -107,7 +107,7 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
|
||||
public void onNavigate(Map<String, Object> settings) {
|
||||
lblGameVersion.setText(i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(provider.getVersion().resolve(provider.getProfile().getRepository()));
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(provider.getVersion().resolvePreservingPatches(provider.getProfile().getRepository()));
|
||||
String fabric = analyzer.getVersion(FABRIC).orElse(null);
|
||||
String forge = analyzer.getVersion(FORGE).orElse(null);
|
||||
String liteLoader = analyzer.getVersion(LITELOADER).orElse(null);
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -38,7 +39,7 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
||||
private final String libraryId;
|
||||
private final String oldLibraryVersion;
|
||||
|
||||
public UpdateInstallerWizardProvider(Profile profile, String gameVersion, Version version, String libraryId, String oldLibraryVersion) {
|
||||
public UpdateInstallerWizardProvider(@NotNull Profile profile, @NotNull String gameVersion, @NotNull Version version, @NotNull String libraryId, @NotNull String oldLibraryVersion) {
|
||||
this.profile = profile;
|
||||
this.gameVersion = gameVersion;
|
||||
this.version = version;
|
||||
|
||||
@@ -28,11 +28,13 @@ import javafx.scene.image.Image;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
|
||||
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
|
||||
@@ -57,12 +59,15 @@ public class GameItem extends Control {
|
||||
CompletableFuture.supplyAsync(() -> GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse(i18n("message.unknown")), POOL_VERSION_RESOLVE)
|
||||
.thenAcceptAsync(game -> {
|
||||
StringBuilder libraries = new StringBuilder(game);
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedVersion(id));
|
||||
for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values())
|
||||
analyzer.getVersion(type).ifPresent(library ->
|
||||
libraries
|
||||
.append(", ").append(i18n("install.installer." + type.name().toLowerCase()))
|
||||
.append(": ").append(modifyVersion(game, library.replaceAll("(?i)" + type.name().toLowerCase(), ""))));
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(id));
|
||||
analyzer.forEachLibrary((libraryId, libraryVersion) -> {
|
||||
if (libraryId.equals(MINECRAFT.getPatchId())) return;
|
||||
if (I18n.hasKey("install.installer." + libraryId)) {
|
||||
libraries.append(", ").append(i18n("install.installer." + libraryId));
|
||||
if (libraryVersion != null)
|
||||
libraries.append(": ").append(modifyVersion("", libraryVersion.replaceAll("(?i)" + libraryId, "")));
|
||||
}
|
||||
});
|
||||
|
||||
subtitle.set(libraries.toString());
|
||||
}, Platform::runLater)
|
||||
|
||||
@@ -22,7 +22,9 @@ import com.jfoenix.controls.JFXPopup;
|
||||
import com.jfoenix.controls.JFXRadioButton;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -49,22 +51,24 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
chkSelected.setToggleGroup(skinnable.getToggleGroup());
|
||||
root.setLeft(chkSelected);
|
||||
|
||||
root.setCenter(new GameItem(skinnable.getProfile(), skinnable.getVersion()));
|
||||
GameItem gameItem = new GameItem(skinnable.getProfile(), skinnable.getVersion());
|
||||
gameItem.setMouseTransparent(true);
|
||||
root.setCenter(gameItem);
|
||||
|
||||
PopupMenu menu = new PopupMenu();
|
||||
JFXPopup popup = new JFXPopup(menu);
|
||||
|
||||
menu.getContent().setAll(
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.launch(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.launch.test"), FXUtils.withJFXPopupClosing(skinnable::launch, popup)),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.script(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.launch_script"), FXUtils.withJFXPopupClosing(skinnable::generateLaunchScript, popup)),
|
||||
new MenuSeparator(),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.gear(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.manage.manage"), FXUtils.withJFXPopupClosing(skinnable::modifyGameSettings, popup)),
|
||||
new MenuSeparator(),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.pencil(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.manage.rename"), FXUtils.withJFXPopupClosing(skinnable::rename, popup)),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.delete(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.manage.remove"), FXUtils.withJFXPopupClosing(skinnable::remove, popup)),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.export(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("modpack.export"), FXUtils.withJFXPopupClosing(skinnable::export, popup)),
|
||||
new MenuSeparator(),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.folderOpen(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("folder.game"), FXUtils.withJFXPopupClosing(skinnable::browse, popup)),
|
||||
new MenuSeparator(),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.launch(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.launch.test"), FXUtils.withJFXPopupClosing(skinnable::launch, popup)),
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.script(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("version.launch_script"), FXUtils.withJFXPopupClosing(skinnable::generateLaunchScript, popup)));
|
||||
new IconedMenuItem(FXUtils.limitingSize(SVG.folderOpen(Theme.blackFillBinding(), 14, 14), 14, 14), i18n("folder.game"), FXUtils.withJFXPopupClosing(skinnable::browse, popup)));
|
||||
|
||||
HBox right = new HBox();
|
||||
right.setAlignment(Pos.CENTER_RIGHT);
|
||||
@@ -91,5 +95,16 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
|
||||
JFXDepthManager.setDepth(root, 1);
|
||||
|
||||
getChildren().setAll(root);
|
||||
|
||||
root.setCursor(Cursor.HAND);
|
||||
root.setOnMouseClicked(e -> {
|
||||
if (e.getButton() == MouseButton.PRIMARY) {
|
||||
if (e.getClickCount() == 1) {
|
||||
skinnable.modifyGameSettings();
|
||||
}
|
||||
} else if (e.getButton() == MouseButton.SECONDARY) {
|
||||
popup.show(root, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, e.getX(), e.getY());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.ToolbarListPageSkin;
|
||||
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
||||
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -74,7 +76,7 @@ public class InstallerListPage extends ListPageBase<InstallerItem> {
|
||||
Task.supplyAsync(() -> {
|
||||
gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse(null);
|
||||
|
||||
return LibraryAnalyzer.analyze(profile.getRepository().getResolvedVersion(versionId));
|
||||
return LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(versionId));
|
||||
}).thenAcceptAsync(Schedulers.javafx(), analyzer -> {
|
||||
Function<String, Consumer<InstallerItem>> removeAction = libraryId -> x -> {
|
||||
profile.getDependency().removeLibraryAsync(version.getId(), libraryId)
|
||||
@@ -84,13 +86,17 @@ public class InstallerListPage extends ListPageBase<InstallerItem> {
|
||||
};
|
||||
|
||||
itemsProperty().clear();
|
||||
for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) {
|
||||
String libraryId = type.getPatchId();
|
||||
analyzer.getVersion(type).ifPresent(libraryVersion -> itemsProperty().add(
|
||||
new InstallerItem(i18n("install.installer." + libraryId), libraryVersion, () -> {
|
||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
|
||||
}, removeAction.apply(libraryId))));
|
||||
}
|
||||
analyzer.forEachLibrary((libraryId, libraryVersion) -> {
|
||||
String title = I18n.hasKey("install.installer." + libraryId) ? i18n("install.installer." + libraryId) : libraryId;
|
||||
Consumer<InstallerItem> action = "game".equals(libraryId) ? null : removeAction.apply(libraryId);
|
||||
if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId)))
|
||||
itemsProperty().add(
|
||||
new InstallerItem(title, libraryVersion, () -> {
|
||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
|
||||
}, action));
|
||||
else
|
||||
itemsProperty().add(new InstallerItem(title, libraryVersion, null, action));
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
}
|
||||
|
||||
public void loadVersion(Profile profile, String id) {
|
||||
libraryAnalyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedVersion(id));
|
||||
libraryAnalyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedPreservingPatchesVersion(id));
|
||||
modded.set(libraryAnalyzer.hasModLoader());
|
||||
loadMods(profile.getRepository().getModManager(id));
|
||||
}
|
||||
@@ -142,6 +142,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
public void removeSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
|
||||
try {
|
||||
modManager.removeMods(selectedItems.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(TreeItem::getValue)
|
||||
.filter(Objects::nonNull)
|
||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||
|
||||
@@ -21,7 +21,6 @@ import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
@@ -77,8 +76,7 @@ public class Versions {
|
||||
}
|
||||
|
||||
public static void updateGameAssets(Profile profile, String version) {
|
||||
Version resolvedVersion = profile.getRepository().getResolvedVersion(version);
|
||||
TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), resolvedVersion, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY)
|
||||
TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY)
|
||||
.executor();
|
||||
Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"));
|
||||
executor.start();
|
||||
|
||||
@@ -55,6 +55,7 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
put("java.lang.VerifyError", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.NoSuchMethodError", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.NoSuchFieldError", i18n("crash.NoClassDefFound"));
|
||||
put("javax.imageio.IIOException", i18n("crash.NoClassDefFound"));
|
||||
put("netscape.javascript.JSException", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.IncompatibleClassChangeError", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.ClassFormatError", i18n("crash.NoClassDefFound"));
|
||||
@@ -86,6 +87,12 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static Set<String> CAUGHT_EXCEPTIONS = newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
private final boolean showCrashWindow;
|
||||
|
||||
public CrashReporter(boolean showCrashWindow) {
|
||||
this.showCrashWindow = showCrashWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
LOG.log(Level.SEVERE, "Uncaught exception in thread " + t.getName(), e);
|
||||
@@ -116,7 +123,9 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
LOG.log(Level.SEVERE, text);
|
||||
|
||||
if (checkThrowable(e)) {
|
||||
Platform.runLater(() -> new CrashWindow(text).show());
|
||||
if (showCrashWindow) {
|
||||
Platform.runLater(() -> new CrashWindow(text).show());
|
||||
}
|
||||
if (!UpdateChecker.isOutdated() && IntegrityChecker.isSelfVerified()) {
|
||||
reportToServer(text);
|
||||
}
|
||||
|
||||
@@ -23,14 +23,12 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Note: This class has no state.
|
||||
@@ -70,7 +68,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<?> checkGameCompletionAsync(Version version) {
|
||||
public Task<?> checkGameCompletionAsync(Version original) {
|
||||
Version version = original.resolve(repository);
|
||||
return Task.allOf(
|
||||
Task.composeAsync(() -> {
|
||||
if (!repository.getVersionJar(version).exists())
|
||||
@@ -85,7 +84,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
@Override
|
||||
public Task<?> checkLibraryCompletionAsync(Version version) {
|
||||
return new GameLibrariesTask(this, version);
|
||||
return new GameLibrariesTask(this, version, version.getLibraries());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,34 +137,16 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
* Will try to remove libraries and patches.
|
||||
*
|
||||
* @param versionId version id
|
||||
* @param libraryId forge/liteloader/optifine
|
||||
* @param libraryId forge/liteloader/optifine/fabric
|
||||
* @return task to remove the specified library
|
||||
*/
|
||||
public Task<Version> removeLibraryWithoutSavingAsync(String versionId, String libraryId) {
|
||||
Version version = repository.getVersion(versionId); // to ensure version is not resolved
|
||||
// MaintainTask requires version that does not inherits from any version.
|
||||
// If we want to remove a library in dependent version, we should keep the dependents not changed
|
||||
// So resolving this game version to preserve all information in this version.json is necessary.
|
||||
Version version = repository.getResolvedPreservingPatchesVersion(versionId);
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
|
||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
||||
|
||||
switch (libraryId) {
|
||||
case "forge":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.FORGE, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById(LibraryAnalyzer.LibraryType.FORGE.getPatchId());
|
||||
break;
|
||||
case "liteloader":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.LITELOADER, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId());
|
||||
break;
|
||||
case "optifine":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.OPTIFINE, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId());
|
||||
break;
|
||||
case "fabric":
|
||||
analyzer.ifPresent(LibraryAnalyzer.LibraryType.FABRIC, (library, libraryVersion) -> newList.remove(library));
|
||||
version = version.removePatchById(LibraryAnalyzer.LibraryType.FABRIC.getPatchId());
|
||||
break;
|
||||
}
|
||||
return new MaintainTask(version.setLibraries(newList));
|
||||
return Task.supplyAsync(() -> LibraryAnalyzer.analyze(version).removeLibrary(libraryId).build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +154,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
* Will try to remove libraries and patches.
|
||||
*
|
||||
* @param versionId version id
|
||||
* @param libraryId forge/liteloader/optifine
|
||||
* @param libraryId forge/liteloader/optifine/fabric
|
||||
* @return task to remove the specified library
|
||||
*/
|
||||
public Task<Version> removeLibraryAsync(String versionId, String libraryId) {
|
||||
|
||||
@@ -17,14 +17,9 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -52,24 +47,16 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
|
||||
@Override
|
||||
public Task<?> buildAsync() {
|
||||
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenComposeAsync(rawJson -> {
|
||||
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
|
||||
Version version = original.setId(name).setJar(null);
|
||||
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenComposeAsync(Task.allOf(
|
||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
|
||||
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
|
||||
).withComposeAsync(dependencyManager.getGameRepository().save(version))); // using [with] because download failure here are tolerant.
|
||||
Task<Version> libraryTask = Task.supplyAsync(() -> new Version(name));
|
||||
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, "game", gameVersion));
|
||||
|
||||
Task<Version> libraryTask = vanillaTask.thenSupplyAsync(() -> version);
|
||||
for (Map.Entry<String, String> entry : toolVersions.entrySet())
|
||||
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue()));
|
||||
|
||||
for (Map.Entry<String, String> entry : toolVersions.entrySet())
|
||||
libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue()));
|
||||
for (RemoteVersion remoteVersion : remoteVersions)
|
||||
libraryTask = libraryTask.thenComposeAsync(dependencyManager.installLibraryAsync(remoteVersion));
|
||||
|
||||
for (RemoteVersion remoteVersion : remoteVersions)
|
||||
libraryTask = libraryTask.thenComposeAsync(dependencyManager.installLibraryAsync(remoteVersion));
|
||||
|
||||
return libraryTask;
|
||||
}).whenComplete(exception -> {
|
||||
return libraryTask.whenComplete(exception -> {
|
||||
if (exception != null)
|
||||
dependencyManager.getGameRepository().removeVersionFromDisk(name);
|
||||
});
|
||||
@@ -78,9 +65,4 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) {
|
||||
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, libraryVersion);
|
||||
}
|
||||
|
||||
protected Task<Void> downloadGameAsync(String gameVersion, Version version) {
|
||||
return new GameDownloadTask(dependencyManager, gameVersion, version);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,6 +44,7 @@ public interface DownloadProvider {
|
||||
*
|
||||
* @param id the id of specific version list that this download provider provides. i.e. "forge", "liteloader", "game", "optifine"
|
||||
* @return the version list
|
||||
* @throws IllegalArgumentException if the version list does not exist
|
||||
*/
|
||||
VersionList<?> getVersionListById(String id);
|
||||
}
|
||||
@@ -20,70 +20,124 @@ package org.jackhuang.hmcl.download;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class LibraryAnalyzer {
|
||||
private final Map<LibraryType, Pair<Library, String>> libraries;
|
||||
private Version version;
|
||||
private final Map<String, Pair<Library, String>> libraries;
|
||||
|
||||
private LibraryAnalyzer(Map<LibraryType, Pair<Library, String>> libraries) {
|
||||
private LibraryAnalyzer(Version version, Map<String, Pair<Library, String>> libraries) {
|
||||
this.version = version;
|
||||
this.libraries = libraries;
|
||||
}
|
||||
|
||||
public Optional<String> getVersion(LibraryType type) {
|
||||
return getVersion(type.getPatchId());
|
||||
}
|
||||
|
||||
public Optional<String> getVersion(String type) {
|
||||
return Optional.ofNullable(libraries.get(type)).map(Pair::getValue);
|
||||
}
|
||||
|
||||
public void ifPresent(LibraryType type, BiConsumer<Library, String> consumer) {
|
||||
if (libraries.containsKey(type)) {
|
||||
Pair<Library, String> value = libraries.get(type);
|
||||
consumer.accept(value.getKey(), value.getValue());
|
||||
}
|
||||
public void forEachLibrary(BiConsumer<String, @Nullable String> callback) {
|
||||
for (Map.Entry<String, Pair<Library, String>> entry : libraries.entrySet())
|
||||
callback.accept(entry.getKey(), entry.getValue().getValue());
|
||||
}
|
||||
|
||||
public boolean has(LibraryType type) {
|
||||
return has(type.getPatchId());
|
||||
}
|
||||
|
||||
public boolean has(String type) {
|
||||
return libraries.containsKey(type);
|
||||
}
|
||||
|
||||
public boolean hasModLoader() {
|
||||
return Arrays.stream(LibraryType.values())
|
||||
.filter(LibraryType::isModLoader)
|
||||
.anyMatch(this::has);
|
||||
return libraries.keySet().stream().map(LibraryType::fromPatchId)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(LibraryType::isModLoader);
|
||||
}
|
||||
|
||||
public boolean hasModLauncher() {
|
||||
final String modLauncher = "cpw.mods.modlauncher.Launcher";
|
||||
return modLauncher.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(patch -> modLauncher.equals(patch.getMainClass()));
|
||||
}
|
||||
|
||||
private Version removingMatchedLibrary(Version version, String libraryId) {
|
||||
LibraryType type = LibraryType.fromPatchId(libraryId);
|
||||
if (type == null) return version;
|
||||
|
||||
List<Library> libraries = new ArrayList<>();
|
||||
for (Library library : version.getLibraries()) {
|
||||
String groupId = library.getGroupId();
|
||||
String artifactId = library.getArtifactId();
|
||||
|
||||
if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) {
|
||||
// skip
|
||||
} else {
|
||||
libraries.add(library);
|
||||
}
|
||||
}
|
||||
return version.setLibraries(libraries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove library by library id
|
||||
* @param libraryId patch id or "forge"/"optifine"/"liteloader"/"fabric"
|
||||
* @return this
|
||||
*/
|
||||
public LibraryAnalyzer removeLibrary(String libraryId) {
|
||||
if (!has(libraryId)) return this;
|
||||
version = removingMatchedLibrary(version, libraryId)
|
||||
.setPatches(version.getPatches().stream()
|
||||
.filter(patch -> !libraryId.equals(patch.getId()))
|
||||
.map(patch -> removingMatchedLibrary(patch, libraryId))
|
||||
.collect(Collectors.toList()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Version build() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public static LibraryAnalyzer analyze(Version version) {
|
||||
Map<LibraryType, Pair<Library, String>> libraries = new EnumMap<>(LibraryType.class);
|
||||
if (version.getInheritsFrom() != null)
|
||||
throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version");
|
||||
|
||||
for (Library library : version.getLibraries()) {
|
||||
Map<String, Pair<Library, String>> libraries = new HashMap<>();
|
||||
|
||||
for (Library library : version.resolve(null).getLibraries()) {
|
||||
String groupId = library.getGroupId();
|
||||
String artifactId = library.getArtifactId();
|
||||
|
||||
for (LibraryType type : LibraryType.values()) {
|
||||
if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) {
|
||||
libraries.put(type, Pair.pair(library, library.getVersion()));
|
||||
libraries.put(type.getPatchId(), Pair.pair(library, library.getVersion()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Version patch : version.getPatches()) {
|
||||
for (LibraryType type : LibraryType.values()) {
|
||||
if (type.patchId.equals(patch.getId())) {
|
||||
libraries.put(type, Pair.pair(null, patch.getVersion()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (patch.isHidden()) continue;
|
||||
libraries.put(patch.getId(), Pair.pair(null, patch.getVersion()));
|
||||
}
|
||||
|
||||
return new LibraryAnalyzer(libraries);
|
||||
return new LibraryAnalyzer(version, libraries);
|
||||
}
|
||||
|
||||
public enum LibraryType {
|
||||
MINECRAFT(true, "game", Pattern.compile("^$"), Pattern.compile("^$")),
|
||||
FABRIC(true, "fabric", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader")),
|
||||
FORGE(true, "forge", Pattern.compile("net\\.minecraftforge"), Pattern.compile("forge")),
|
||||
LITELOADER(true, "liteloader", Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")),
|
||||
@@ -107,5 +161,12 @@ public final class LibraryAnalyzer {
|
||||
public String getPatchId() {
|
||||
return patchId;
|
||||
}
|
||||
|
||||
public static LibraryType fromPatchId(String patchId) {
|
||||
for (LibraryType type : values())
|
||||
if (type.getPatchId().equals(patchId))
|
||||
return type;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,37 +17,63 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||
|
||||
public class MaintainTask extends Task<Version> {
|
||||
|
||||
private final GameRepository repository;
|
||||
private final Version version;
|
||||
|
||||
public MaintainTask(Version version) {
|
||||
public MaintainTask(GameRepository repository, Version version) {
|
||||
this.repository = repository;
|
||||
this.version = version;
|
||||
|
||||
if (version.getInheritsFrom() != null)
|
||||
throw new IllegalArgumentException("MaintainTask requires independent game version");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
setResult(maintain(version));
|
||||
setResult(maintain(repository, version));
|
||||
}
|
||||
|
||||
public static Version maintain(Version version) {
|
||||
if (version.getMainClass().contains("launchwrapper")) {
|
||||
return maintainGameWithLaunchWrapper(version);
|
||||
public static Version maintain(GameRepository repository, Version version) {
|
||||
if (version.getInheritsFrom() != null)
|
||||
throw new IllegalArgumentException("MaintainTask requires independent game version");
|
||||
|
||||
String mainClass = version.resolve(null).getMainClass();
|
||||
|
||||
if (mainClass != null && mainClass.contains("launchwrapper")) {
|
||||
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version)));
|
||||
} else {
|
||||
// Vanilla Minecraft does not need maintain
|
||||
// Forge 1.13 support not implemented, not compatible with OptiFine currently.
|
||||
// Fabric does not need maintain, nothing compatible with fabric now.
|
||||
return version;
|
||||
return maintainOptiFineLibrary(repository, unique(version));
|
||||
}
|
||||
}
|
||||
|
||||
private static Version maintainGameWithLaunchWrapper(Version version) {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
|
||||
String mainClass = null;
|
||||
|
||||
if (!libraryAnalyzer.has(FORGE)) {
|
||||
builder.removeTweakClass("forge");
|
||||
@@ -56,7 +82,7 @@ public class MaintainTask extends Task<Version> {
|
||||
// Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added.
|
||||
|
||||
builder.removeTweakClass("liteloader");
|
||||
if (libraryAnalyzer.has(LITELOADER)) {
|
||||
if (libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.hasModLauncher()) {
|
||||
builder.addArgument("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker");
|
||||
}
|
||||
|
||||
@@ -65,11 +91,101 @@ public class MaintainTask extends Task<Version> {
|
||||
if (!libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.has(FORGE)) {
|
||||
builder.addArgument("--tweakClass", "optifine.OptiFineTweaker");
|
||||
} else {
|
||||
// If forge or LiteLoader installed, OptiFine Forge Tweaker is needed.
|
||||
builder.addArgument("--tweakClass", "optifine.OptiFineForgeTweaker");
|
||||
if (libraryAnalyzer.hasModLauncher()) {
|
||||
// If ModLauncher installed, we use ModLauncher in place of LaunchWrapper.
|
||||
mainClass = "cpw.mods.modlauncher.Launcher";
|
||||
} else {
|
||||
// If forge or LiteLoader installed, OptiFine Forge Tweaker is needed.
|
||||
builder.addArgument("--tweakClass", "optifine.OptiFineForgeTweaker");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
Version ret = builder.build();
|
||||
return mainClass == null ? ret : ret.setMainClass(mainClass);
|
||||
}
|
||||
|
||||
private static Version maintainOptiFineLibrary(GameRepository repository, Version version) {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
||||
|
||||
if (libraryAnalyzer.has(OPTIFINE)) {
|
||||
if (libraryAnalyzer.has(LITELOADER) || libraryAnalyzer.has(FORGE)) {
|
||||
// If forge or LiteLoader installed, OptiFine Forge Tweaker is needed.
|
||||
// And we should load the installer jar instead of patch jar.
|
||||
if (repository != null)
|
||||
for (int i = 0; i < version.getLibraries().size(); ++i) {
|
||||
Library library = libraries.get(i);
|
||||
if (library.is("optifine", "OptiFine")) {
|
||||
Library newLibrary = new Library(new Artifact("optifine", "OptiFine", library.getVersion(), "installer"));
|
||||
if (repository.getLibraryFile(version, newLibrary).exists()) {
|
||||
libraries.set(i, null);
|
||||
// OptiFine should be loaded after Forge in classpath.
|
||||
// Although we have altered priority of OptiFine higher than Forge,
|
||||
// there still exists a situation that Forge is installed without patch.
|
||||
// Here we manually alter the position of OptiFine library in classpath.
|
||||
libraries.add(newLibrary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static Version unique(Version version) {
|
||||
List<Library> libraries = new ArrayList<>();
|
||||
|
||||
SimpleMultimap<String, Integer> multimap = new SimpleMultimap<String, Integer>(HashMap::new, LinkedList::new);
|
||||
|
||||
for (Library library : version.getLibraries()) {
|
||||
String id = library.getGroupId() + ":" + library.getArtifactId();
|
||||
VersionNumber number = VersionNumber.asVersion(library.getVersion());
|
||||
String serialized = JsonUtils.GSON.toJson(library);
|
||||
|
||||
if (multimap.containsKey(id)) {
|
||||
boolean duplicate = false;
|
||||
for (int otherLibraryIndex : multimap.get(id)) {
|
||||
Library otherLibrary = libraries.get(otherLibraryIndex);
|
||||
VersionNumber otherNumber = VersionNumber.asVersion(otherLibrary.getVersion());
|
||||
if (CompatibilityRule.equals(library.getRules(), otherLibrary.getRules())) { // rules equal, ignore older version.
|
||||
boolean flag = true;
|
||||
if (number.compareTo(otherNumber) > 0) { // if this library is newer
|
||||
// replace [otherLibrary] with [library]
|
||||
libraries.set(otherLibraryIndex, library);
|
||||
} else if (number.compareTo(otherNumber) == 0) { // same library id.
|
||||
// prevent from duplicated libraries
|
||||
if (library.equals(otherLibrary)) {
|
||||
String otherSerialized = JsonUtils.GSON.toJson(otherLibrary);
|
||||
// A trick, the library that has more information is better, which can be
|
||||
// considered whose serialized JSON text will be longer.
|
||||
if (serialized.length() > otherSerialized.length()) {
|
||||
libraries.set(otherLibraryIndex, library);
|
||||
}
|
||||
} else {
|
||||
// for text2speech, which have same library id as well as version number,
|
||||
// but its library and native library does not equal
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicate) {
|
||||
multimap.put(id, libraries.size());
|
||||
libraries.add(library);
|
||||
}
|
||||
} else {
|
||||
multimap.put(id, libraries.size());
|
||||
libraries.add(library);
|
||||
}
|
||||
}
|
||||
|
||||
return version.setLibraries(libraries);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
@@ -65,7 +66,7 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
|
||||
@Override
|
||||
public void preExecute() {
|
||||
if (!Objects.equals("net.minecraft.client.main.Main", version.getMainClass()))
|
||||
if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass()))
|
||||
throw new UnsupportedFabricInstallationException();
|
||||
}
|
||||
|
||||
@@ -121,8 +122,8 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
libraries.add(new Library("net.fabricmc", "intermediary", gameVersion, null, "https://maven.fabricmc.net/", null));
|
||||
libraries.add(new Library("net.fabricmc", "fabric-loader", loaderVersion, null, "https://maven.fabricmc.net/", null));
|
||||
libraries.add(new Library(new Artifact("net.fabricmc", "intermediary", gameVersion), "https://maven.fabricmc.net/", null));
|
||||
libraries.add(new Library(new Artifact("net.fabricmc", "fabric-loader", loaderVersion), "https://maven.fabricmc.net/", null));
|
||||
|
||||
return new Version("net.fabricmc", loaderVersion, 30000, arguments, mainClass, libraries);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ public final class ForgeInstall {
|
||||
|
||||
private final String profileName;
|
||||
private final String target;
|
||||
private final String path;
|
||||
private final Artifact path;
|
||||
private final String version;
|
||||
private final String filePath;
|
||||
private final String welcome;
|
||||
@@ -40,7 +41,7 @@ public final class ForgeInstall {
|
||||
this(null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public ForgeInstall(String profileName, String target, String path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
|
||||
public ForgeInstall(String profileName, String target, Artifact path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) {
|
||||
this.profileName = profileName;
|
||||
this.target = target;
|
||||
this.path = path;
|
||||
@@ -60,7 +61,7 @@ public final class ForgeInstall {
|
||||
return target;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
public Artifact getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
@@ -39,6 +38,9 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
@@ -80,10 +82,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
Files.deleteIfExists(installer);
|
||||
setResult(dependency.getResult()
|
||||
.setPriority(30000)
|
||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||
.setVersion(remote.getSelfVersion()));
|
||||
setResult(dependency.getResult());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,9 +98,9 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0)
|
||||
dependency = new ForgeNewInstallTask(dependencyManager, version, installer);
|
||||
dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer);
|
||||
else
|
||||
dependency = new ForgeOldInstallTask(dependencyManager, version, installer);
|
||||
dependency = new ForgeOldInstallTask(dependencyManager, version, remote.getSelfVersion(), installer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,15 +124,19 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getMinecraft()))
|
||||
throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get());
|
||||
return new ForgeNewInstallTask(dependencyManager, version, installer);
|
||||
return new ForgeNewInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getPath().getVersion().replaceAll("(?i)forge", "")), installer);
|
||||
} else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) {
|
||||
ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getInstall().getMinecraft()))
|
||||
throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get());
|
||||
return new ForgeOldInstallTask(dependencyManager, version, installer);
|
||||
return new ForgeOldInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getInstall().getPath().getVersion().replaceAll("(?i)forge", "")), installer);
|
||||
} else {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String modifyVersion(String gameVersion, String version) {
|
||||
return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_");
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
@@ -34,11 +35,10 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -70,12 +70,14 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
|
||||
private ForgeNewInstallProfile profile;
|
||||
private Version forgeVersion;
|
||||
private final String selfVersion;
|
||||
|
||||
ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
|
||||
ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameRepository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
this.installer = installer;
|
||||
this.selfVersion = selfVersion;
|
||||
|
||||
setSignificance(TaskSignificance.MINOR);
|
||||
}
|
||||
@@ -86,7 +88,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
else if (StringUtils.isSurrounded(literal, "'", "'"))
|
||||
return StringUtils.removeSurrounding(literal, "'");
|
||||
else if (StringUtils.isSurrounded(literal, "[", "]"))
|
||||
return gameRepository.getArtifactFile(version, new Artifact(StringUtils.removeSurrounding(literal, "[", "]"))).toString();
|
||||
return gameRepository.getArtifactFile(version, Artifact.fromDescriptor(StringUtils.removeSurrounding(literal, "[", "]"))).toString();
|
||||
else
|
||||
return plainConverter.apply(literal);
|
||||
}
|
||||
@@ -238,13 +240,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
command.addAll(args);
|
||||
|
||||
LOG.info("Executing external processor " + processor.getJar().toString() + ", command line: " + new CommandBuilder().addAll(command).toString());
|
||||
Process process = new ProcessBuilder(command).start();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
for (String line; (line = reader.readLine()) != null;) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
int exitCode = process.waitFor();
|
||||
int exitCode = SystemUtils.callExternalProcess(command);
|
||||
if (exitCode != 0)
|
||||
throw new IllegalStateException("Game processor exited abnormally");
|
||||
|
||||
@@ -268,7 +264,10 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
setResult(forgeVersion);
|
||||
setResult(forgeVersion
|
||||
.setPriority(30000)
|
||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion));
|
||||
|
||||
FileUtils.deleteDirectory(temp.toFile());
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.download.forge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -41,12 +42,14 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
private final Version version;
|
||||
private final Path installer;
|
||||
private final String selfVersion;
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
|
||||
ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
|
||||
ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
this.installer = installer;
|
||||
this.selfVersion = selfVersion;
|
||||
|
||||
setSignificance(TaskSignificance.MINOR);
|
||||
}
|
||||
@@ -71,7 +74,7 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
ForgeInstallProfile installProfile = JsonUtils.fromNonNullJson(json, ForgeInstallProfile.class);
|
||||
|
||||
// unpack the universal jar in the installer file.
|
||||
Library forgeLibrary = Library.fromName(installProfile.getInstall().getPath());
|
||||
Library forgeLibrary = new Library(installProfile.getInstall().getPath());
|
||||
File forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary);
|
||||
if (!FileUtils.makeFile(forgeFile))
|
||||
throw new IOException("Cannot make directory " + forgeFile.getParent());
|
||||
@@ -81,7 +84,10 @@ public class ForgeOldInstallTask extends Task<Version> {
|
||||
IOUtils.copyTo(is, os);
|
||||
}
|
||||
|
||||
setResult(installProfile.getVersionInfo());
|
||||
setResult(installProfile.getVersionInfo()
|
||||
.setPriority(30000)
|
||||
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,16 +51,16 @@ public final class GameAssetDownloadTask extends Task<Void> {
|
||||
* Constructor.
|
||||
*
|
||||
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
|
||||
* @param version the <b>resolved</b> version
|
||||
* @param version the game version
|
||||
*/
|
||||
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
this.assetIndexInfo = version.getAssetIndex();
|
||||
this.version = version.resolve(dependencyManager.getGameRepository());
|
||||
this.assetIndexInfo = this.version.getAssetIndex();
|
||||
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
|
||||
|
||||
if (!assetIndexFile.exists() || forceDownloadingIndex)
|
||||
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, version));
|
||||
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class GameDownloadTask extends Task<Void> {
|
||||
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameVersion = gameVersion;
|
||||
this.version = version;
|
||||
this.version = version.resolve(dependencyManager.getGameRepository());
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 huangyuhui <[email protected]> and contributors
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||
|
||||
public class GameInstallTask extends Task<Version> {
|
||||
|
||||
private final DefaultGameRepository gameRepository;
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
private final Version version;
|
||||
private final GameRemoteVersion remote;
|
||||
private final VersionJsonDownloadTask downloadTask;
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
|
||||
public GameInstallTask(DefaultDependencyManager dependencyManager, Version version, GameRemoteVersion remoteVersion) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameRepository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
this.remote = remoteVersion;
|
||||
this.downloadTask = new VersionJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return Collections.singleton(downloadTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelyingOnDependencies() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
Version patch = JsonUtils.GSON.fromJson(downloadTask.getResult(), Version.class)
|
||||
.setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(0);
|
||||
setResult(patch);
|
||||
|
||||
Version version = new Version(this.version.getId()).addPatch(patch);
|
||||
dependencies.add(new GameDownloadTask(dependencyManager, remote.getGameVersion(), version)
|
||||
.thenComposeAsync(Task.allOf(
|
||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
|
||||
new GameLibrariesTask(dependencyManager, version)
|
||||
).withComposeAsync(gameRepository.save(version))));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,17 +43,17 @@ public final class GameLibrariesTask extends Task<Void> {
|
||||
* Constructor.
|
||||
*
|
||||
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
|
||||
* @param version the <b>resolved</b> version
|
||||
* @param version the game version
|
||||
*/
|
||||
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||
this(dependencyManager, version, version.getLibraries());
|
||||
this(dependencyManager, version, version.resolve(dependencyManager.getGameRepository()).getLibraries());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
|
||||
* @param version the <b>resolved</b> version
|
||||
* @param version the game version
|
||||
*/
|
||||
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, List<Library> libraries) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.ReleaseType;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -47,6 +50,11 @@ public final class GameRemoteVersion extends RemoteVersion {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Version> getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) {
|
||||
return new GameInstallTask(dependencyManager, baseVersion, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RemoteVersion o) {
|
||||
if (!(o instanceof GameRemoteVersion))
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
@@ -48,7 +47,7 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
|
||||
protected Collection<GameRemoteVersion> getVersionsImpl(String gameVersion) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return StringUtils.isBlank(gameVersion) ? versions.values() : versions.get(gameVersion);
|
||||
return versions.values();
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 huangyuhui <[email protected]> and contributors
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.game;
|
||||
|
||||
import org.jackhuang.hmcl.game.CompatibilityRule;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.SimpleMultimap;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LibrariesUniqueTask extends Task<Version> {
|
||||
private final Version version;
|
||||
|
||||
public LibrariesUniqueTask(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
setResult(unique(version));
|
||||
}
|
||||
|
||||
public static Version unique(Version version) {
|
||||
List<Library> libraries = new ArrayList<>(version.getLibraries());
|
||||
|
||||
SimpleMultimap<String, Library> multimap = new SimpleMultimap<String, Library>(HashMap::new, LinkedList::new);
|
||||
|
||||
for (Library library : libraries) {
|
||||
String id = library.getGroupId() + ":" + library.getArtifactId();
|
||||
VersionNumber number = VersionNumber.asVersion(library.getVersion());
|
||||
String serialized = JsonUtils.GSON.toJson(library);
|
||||
|
||||
if (multimap.containsKey(id)) {
|
||||
boolean duplicate = false;
|
||||
for (Library otherLibrary : multimap.get(id)) {
|
||||
VersionNumber otherNumber = VersionNumber.asVersion(otherLibrary.getVersion());
|
||||
if (CompatibilityRule.equals(library.getRules(), otherLibrary.getRules())) { // rules equal, ignore older version.
|
||||
boolean flag = true;
|
||||
if (number.compareTo(otherNumber) > 0) { // if this library is newer
|
||||
multimap.removeValue(otherLibrary);
|
||||
multimap.put(id, library);
|
||||
break;
|
||||
} else if (number.compareTo(otherNumber) == 0) { // same library id.
|
||||
// prevent from duplicated libraries
|
||||
if (library.equals(otherLibrary)) {
|
||||
String otherSerialized = JsonUtils.GSON.toJson(otherLibrary);
|
||||
// A trick, the library that has more information is better, which can be
|
||||
// considered whose serialized JSON text will be longer.
|
||||
if (serialized.length() > otherSerialized.length()) {
|
||||
multimap.removeValue(id, otherLibrary);
|
||||
multimap.put(id, library);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// for text2speech, which have same library id as well as version number,
|
||||
// but its library and native library does not equal
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicate) {
|
||||
multimap.put(id, library);
|
||||
}
|
||||
} else {
|
||||
multimap.put(id, library);
|
||||
}
|
||||
}
|
||||
|
||||
return version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -62,7 +62,7 @@ public final class VersionJsonDownloadTask extends Task<String> {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
||||
RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion)
|
||||
.orElseThrow(() -> new IllegalStateException("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));
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.liteloader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||
@@ -64,7 +65,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void execute() {
|
||||
Library library = new Library(
|
||||
"com.mumfrey", "liteloader", remote.getSelfVersion(), null,
|
||||
new Artifact("com.mumfrey", "liteloader", remote.getSelfVersion()),
|
||||
"http://dl.liteloader.com/versions/",
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrl()))
|
||||
);
|
||||
@@ -78,7 +79,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
|
||||
.setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration
|
||||
);
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(getResult().getLibraries())));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult()));
|
||||
}
|
||||
|
||||
}
|
||||
+46
-23
@@ -21,6 +21,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
@@ -31,6 +33,8 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantPool;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner;
|
||||
import org.jenkinsci.constant_pool_scanner.ConstantType;
|
||||
@@ -42,7 +46,6 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -57,15 +60,17 @@ import static org.jackhuang.hmcl.util.Lang.getOrDefault;
|
||||
*/
|
||||
public final class OptiFineInstallTask extends Task<Version> {
|
||||
|
||||
private final DefaultGameRepository gameRepository;
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
private final Version version;
|
||||
private final OptiFineRemoteVersion remote;
|
||||
private final Path installer;
|
||||
private final List<Task<?>> dependents = new LinkedList<>();
|
||||
private final List<Task<?>> dependencies = new LinkedList<>();
|
||||
private final File dest;
|
||||
private Path dest;
|
||||
|
||||
private final Library optiFineLibrary;
|
||||
private final Library optiFineInstallerLibrary;
|
||||
|
||||
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
|
||||
this(dependencyManager, version, remoteVersion, null);
|
||||
@@ -73,20 +78,21 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
|
||||
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion, Path installer) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameRepository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
this.remote = remoteVersion;
|
||||
this.installer = installer;
|
||||
|
||||
String mavenVersion = remote.getGameVersion() + "_" + remote.getSelfVersion();
|
||||
|
||||
optiFineLibrary = new Library(
|
||||
"optifine", "OptiFine", mavenVersion, null, null,
|
||||
optiFineLibrary = new Library(new Artifact("optifine", "OptiFine", mavenVersion));
|
||||
|
||||
optiFineInstallerLibrary = new Library(
|
||||
new Artifact("optifine", "OptiFine", mavenVersion, "installer"), null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo(
|
||||
"optifine/OptiFine/" + mavenVersion + "/OptiFine-" + mavenVersion + ".jar",
|
||||
"optifine/OptiFine/" + mavenVersion + "/OptiFine-" + mavenVersion + "-installer.jar",
|
||||
remote.getUrl()))
|
||||
);
|
||||
|
||||
dest = dependencyManager.getGameRepository().getLibraryFile(version, optiFineLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,18 +102,14 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
if (!Arrays.asList("net.minecraft.client.main.Main",
|
||||
"net.minecraft.launchwrapper.Launch")
|
||||
.contains(version.resolve(dependencyManager.getGameRepository()).getMainClass()))
|
||||
throw new UnsupportedOptiFineInstallationException();
|
||||
|
||||
dest = Files.createTempFile("optifine-installer", ".jar");
|
||||
|
||||
if (installer == null) {
|
||||
dependents.add(new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), dest)
|
||||
dependents.add(new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), dest.toFile())
|
||||
.setCacheRepository(dependencyManager.getCacheRepository())
|
||||
.setCaching(true));
|
||||
} else {
|
||||
FileUtils.copyFile(installer, dest.toPath());
|
||||
FileUtils.copyFile(installer, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,45 +129,63 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
public void execute() throws Exception {
|
||||
List<Library> libraries = new LinkedList<>();
|
||||
libraries.add(optiFineLibrary);
|
||||
|
||||
FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath());
|
||||
|
||||
// Install launch wrapper modified by OptiFine
|
||||
boolean hasLaunchWrapper = false;
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest.toPath())) {
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) {
|
||||
if (Files.exists(fs.getPath("optifine/Patcher.class"))) {
|
||||
int exitCode = SystemUtils.callExternalProcess(
|
||||
JavaVersion.fromCurrentEnvironment().getBinary().toString(),
|
||||
"-cp",
|
||||
dest.toString(),
|
||||
"optifine.Patcher",
|
||||
gameRepository.getVersionJar(version).getAbsolutePath(),
|
||||
dest.toString(),
|
||||
gameRepository.getLibraryFile(version, optiFineLibrary).toString()
|
||||
);
|
||||
if (exitCode != 0)
|
||||
throw new IllegalStateException("OptiFine patcher failed");
|
||||
} else {
|
||||
FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineLibrary).toPath());
|
||||
}
|
||||
|
||||
Path launchWrapperVersionText = fs.getPath("launchwrapper-of.txt");
|
||||
if (Files.exists(launchWrapperVersionText)) {
|
||||
String launchWrapperVersion = FileUtils.readText(launchWrapperVersionText).trim();
|
||||
Path launchWrapperJar = fs.getPath("launchwrapper-of-" + launchWrapperVersion + ".jar");
|
||||
|
||||
Library launchWrapper = new Library("optifine", "launchwrapper-of", launchWrapperVersion);
|
||||
Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper-of", launchWrapperVersion));
|
||||
|
||||
if (Files.exists(launchWrapperJar)) {
|
||||
File launchWrapperFile = dependencyManager.getGameRepository().getLibraryFile(version, launchWrapper);
|
||||
File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper);
|
||||
FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile());
|
||||
FileUtils.copyFile(launchWrapperJar, launchWrapperFile.toPath());
|
||||
|
||||
hasLaunchWrapper = true;
|
||||
libraries.add(0, launchWrapper);
|
||||
libraries.add(launchWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasLaunchWrapper) {
|
||||
libraries.add(0, new Library("net.minecraft", "launchwrapper", "1.12"));
|
||||
libraries.add(new Library(new Artifact("net.minecraft", "launchwrapper", "1.12")));
|
||||
}
|
||||
|
||||
setResult(new Version(
|
||||
LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(),
|
||||
remote.getSelfVersion(),
|
||||
90000,
|
||||
10000,
|
||||
new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"),
|
||||
"net.minecraft.launchwrapper.Launch",
|
||||
libraries
|
||||
));
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(libraries)));
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult()));
|
||||
}
|
||||
|
||||
public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException {
|
||||
@@ -186,7 +206,10 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(jar);
|
||||
if (!gameVersion.isPresent()) throw new IOException();
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||
ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(fs.getPath("Config.class")), ConstantType.UTF8);
|
||||
Path configClass = fs.getPath("Config.class");
|
||||
if (!Files.exists(configClass)) configClass = fs.getPath("net/optifine/Config.class");
|
||||
if (!Files.exists(configClass)) throw new IOException("Unrecognized installer");
|
||||
ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(configClass), ConstantType.UTF8);
|
||||
List<String> constants = new ArrayList<>();
|
||||
pool.list(Utf8Constant.class).forEach(utf8 -> constants.add(utf8.get()));
|
||||
String mcVersion = getOrDefault(constants, constants.indexOf("MC_VERSION") + 1, null);
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
@@ -38,9 +45,34 @@ public final class Artifact {
|
||||
private final String fileName;
|
||||
private final String path;
|
||||
|
||||
public Artifact(String descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
public Artifact(String group, String name, String version) {
|
||||
this(group, name, version, null);
|
||||
}
|
||||
|
||||
public Artifact(String group, String name, String version, String classifier) {
|
||||
this(group, name, version, classifier, null);
|
||||
}
|
||||
|
||||
public Artifact(String group, String name, String version, String classifier, String extension) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.classifier = classifier;
|
||||
this.extension = extension == null ? "jar" : extension;
|
||||
|
||||
String fileName = this.name + "-" + this.version;
|
||||
if (classifier != null) fileName += "-" + this.classifier;
|
||||
this.fileName = fileName + "." + this.extension;
|
||||
this.path = String.format("%s/%s/%s/%s", this.group.replace(".", "/"), this.name, this.version, this.fileName);
|
||||
|
||||
// group:name:version:classifier@extension
|
||||
String descriptor = String.format("%s:%s:%s", group, name, version);
|
||||
if (classifier != null) descriptor += ":" + classifier;
|
||||
if (!"jar".equals(this.extension)) descriptor += "@" + this.extension;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public static Artifact fromDescriptor(String descriptor) {
|
||||
String[] arr = descriptor.split(":", 4);
|
||||
if (arr.length != 3 && arr.length != 4)
|
||||
throw new IllegalArgumentException("Artifact name is malformed");
|
||||
@@ -55,16 +87,7 @@ public final class Artifact {
|
||||
throw new IllegalArgumentException("Artifact name is malformed");
|
||||
}
|
||||
|
||||
this.group = arr[0].replace("\\", "/");
|
||||
this.name = arr[1];
|
||||
this.version = arr[2];
|
||||
this.classifier = arr.length >= 4 ? arr[3] : null;
|
||||
this.extension = ext == null ? "jar" : ext;
|
||||
|
||||
String fileName = this.name + "-" + this.version;
|
||||
if (classifier != null) fileName += "-" + this.classifier;
|
||||
this.fileName = fileName + "." + this.extension;
|
||||
this.path = String.format("%s/%s/%s/%s", this.group.replace(".", "/"), this.name, this.version, this.fileName);
|
||||
return new Artifact(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, ext);
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
@@ -83,6 +106,10 @@ public final class Artifact {
|
||||
return classifier;
|
||||
}
|
||||
|
||||
public Artifact setClassifier(String classifier) {
|
||||
return new Artifact(group, name, version, classifier, extension);
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
@@ -91,6 +118,8 @@ public final class Artifact {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getPath() { return path; }
|
||||
|
||||
public Path getPath(Path root) {
|
||||
return root.resolve(path);
|
||||
}
|
||||
@@ -108,7 +137,7 @@ public final class Artifact {
|
||||
|
||||
@Override
|
||||
public Artifact deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return json.isJsonPrimitive() ? new Artifact(json.getAsJsonPrimitive().getAsString()) : null;
|
||||
return json.isJsonPrimitive() ? fromDescriptor(json.getAsJsonPrimitive().getAsString()) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,15 +32,15 @@ public class ClassicVersion extends Version {
|
||||
super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}",
|
||||
null, "net.minecraft.client.Minecraft", null, null, null, null,
|
||||
Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")),
|
||||
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false, null);
|
||||
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false, false, null);
|
||||
}
|
||||
|
||||
private static class ClassicLibrary extends Library {
|
||||
|
||||
public ClassicLibrary(String name) {
|
||||
super("", "", "", null, null,
|
||||
super(new Artifact("", "", ""), null,
|
||||
new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null),
|
||||
null, null, null, null);
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,10 @@ public class DefaultGameRepository implements GameRepository {
|
||||
|
||||
@Override
|
||||
public File getLibraryFile(Version version, Library lib) {
|
||||
return new File(getBaseDirectory(), "libraries/" + lib.getPath());
|
||||
if ("local".equals(lib.getHint()))
|
||||
return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName());
|
||||
else
|
||||
return new File(getBaseDirectory(), "libraries/" + lib.getPath());
|
||||
}
|
||||
|
||||
public Path getArtifactFile(Version version, Artifact artifact) {
|
||||
@@ -160,7 +163,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||
}
|
||||
|
||||
if (fromVersion.getId().equals(fromVersion.getJar()))
|
||||
fromVersion = fromVersion.setJar(to);
|
||||
fromVersion = fromVersion.setJar(null);
|
||||
FileUtils.writeText(toJson, JsonUtils.GSON.toJson(fromVersion.setId(to)));
|
||||
return true;
|
||||
} catch (IOException | JsonParseException | VersionNotFoundException e) {
|
||||
|
||||
@@ -56,6 +56,10 @@ public interface GameRepository extends VersionProvider {
|
||||
return getVersion(id).resolve(this);
|
||||
}
|
||||
|
||||
default Version getResolvedPreservingPatchesVersion(String id) throws VersionNotFoundException {
|
||||
return getVersion(id).resolvePreservingPatches(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* How many version are there?
|
||||
*/
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
@@ -31,6 +31,7 @@ public class LaunchOptions implements Serializable {
|
||||
private File gameDir;
|
||||
private JavaVersion java;
|
||||
private String versionName;
|
||||
private String versionType;
|
||||
private String profileName;
|
||||
private String minecraftArgs;
|
||||
private String javaArgs;
|
||||
@@ -71,6 +72,14 @@ public class LaunchOptions implements Serializable {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will shown in the left bottom corner of the main menu of Minecraft.
|
||||
* null if use Version.versionType.
|
||||
*/
|
||||
public String getVersionType() {
|
||||
return versionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't know what the hell this is.
|
||||
*/
|
||||
@@ -215,6 +224,11 @@ public class LaunchOptions implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVersionType(String versionType) {
|
||||
options.versionType = versionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setProfileName(String profileName) {
|
||||
options.profileName = profileName;
|
||||
return this;
|
||||
|
||||
@@ -34,7 +34,7 @@ public final class LibrariesDownloadInfo {
|
||||
private final Map<String, LibraryDownloadInfo> classifiers;
|
||||
|
||||
public LibrariesDownloadInfo(LibraryDownloadInfo artifact) {
|
||||
this(artifact, Collections.emptyMap());
|
||||
this(artifact, null);
|
||||
}
|
||||
|
||||
public LibrariesDownloadInfo(LibraryDownloadInfo artifact, Map<String, LibraryDownloadInfo> classifiers) {
|
||||
|
||||
@@ -17,15 +17,16 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -36,83 +37,68 @@ import java.util.Optional;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
@JsonAdapter(Library.Serializer.class)
|
||||
public class Library implements Comparable<Library> {
|
||||
@Immutable
|
||||
public class Library implements Comparable<Library>, Validation {
|
||||
|
||||
private final String groupId;
|
||||
private final String artifactId;
|
||||
private final String version;
|
||||
private final String classifier;
|
||||
@SerializedName("name")
|
||||
private final Artifact artifact;
|
||||
private final String url;
|
||||
private final LibrariesDownloadInfo downloads;
|
||||
private transient final LibraryDownloadInfo download;
|
||||
private final ExtractRules extract;
|
||||
private final Map<OperatingSystem, String> natives;
|
||||
private final List<CompatibilityRule> rules;
|
||||
private final List<String> checksums;
|
||||
|
||||
private transient final String path;
|
||||
@SerializedName(value = "hint", alternate = {"MMC-hint"})
|
||||
private final String hint;
|
||||
|
||||
public Library(String groupId, String artifactId, String version) {
|
||||
this(groupId, artifactId, version, null, null, null);
|
||||
@SerializedName(value = "filename", alternate = {"MMC-filename"})
|
||||
private final String fileName;
|
||||
|
||||
public Library(Artifact artifact) {
|
||||
this(artifact, null, null);
|
||||
}
|
||||
|
||||
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads) {
|
||||
this(groupId, artifactId, version, classifier, url, downloads, null, null, null, null);
|
||||
public Library(Artifact artifact, String url, LibrariesDownloadInfo downloads) {
|
||||
this(artifact, url, downloads, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public Library(String groupId, String artifactId, String version, String classifier, String url, LibrariesDownloadInfo downloads, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
|
||||
this.groupId = groupId;
|
||||
this.artifactId = artifactId;
|
||||
this.version = version;
|
||||
if (classifier == null)
|
||||
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS))
|
||||
this.classifier = natives.get(OperatingSystem.CURRENT_OS).replace("${arch}", Platform.PLATFORM.getBit());
|
||||
else
|
||||
this.classifier = null;
|
||||
else
|
||||
this.classifier = classifier;
|
||||
public Library(Artifact artifact, String url, LibrariesDownloadInfo downloads, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules, String hint, String filename) {
|
||||
this.artifact = artifact;
|
||||
this.url = url;
|
||||
this.downloads = downloads;
|
||||
this.extract = extract;
|
||||
this.natives = natives;
|
||||
this.rules = rules;
|
||||
this.checksums = checksums;
|
||||
|
||||
LibraryDownloadInfo temp = null;
|
||||
if (downloads != null)
|
||||
if (isNative())
|
||||
temp = downloads.getClassifiers().get(this.classifier);
|
||||
else
|
||||
temp = downloads.getArtifact();
|
||||
|
||||
if (temp != null && temp.getPath() != null)
|
||||
path = temp.getPath();
|
||||
else
|
||||
path = String.format("%s/%s/%s/%s-%s", groupId.replace(".", "/"), artifactId, version, artifactId, version)
|
||||
+ (this.classifier == null ? "" : "-" + this.classifier) + ".jar";
|
||||
|
||||
download = new LibraryDownloadInfo(path,
|
||||
Optional.ofNullable(temp).map(LibraryDownloadInfo::getUrl).orElse(Optional.ofNullable(url).orElse(Constants.DEFAULT_LIBRARY_URL) + path),
|
||||
temp != null ? temp.getSha1() : null,
|
||||
temp != null ? temp.getSize() : 0
|
||||
);
|
||||
this.hint = hint;
|
||||
this.fileName = filename;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
return artifact.getGroup();
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
return artifact.getName();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return groupId + ":" + artifactId + ":" + version;
|
||||
return artifact.toString();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
return artifact.getVersion();
|
||||
}
|
||||
|
||||
public String getClassifier() {
|
||||
if (artifact.getClassifier() == null)
|
||||
if (natives != null && natives.containsKey(OperatingSystem.CURRENT_OS))
|
||||
return natives.get(OperatingSystem.CURRENT_OS).replace("${arch}", Platform.PLATFORM.getBit());
|
||||
else
|
||||
return null;
|
||||
else
|
||||
return artifact.getClassifier();
|
||||
}
|
||||
|
||||
public ExtractRules getExtract() {
|
||||
@@ -127,12 +113,33 @@ public class Library implements Comparable<Library> {
|
||||
return natives != null && appliesToCurrentEnvironment();
|
||||
}
|
||||
|
||||
protected LibraryDownloadInfo getRawDownloadInfo() {
|
||||
if (downloads != null) {
|
||||
if (isNative())
|
||||
return downloads.getClassifiers().get(getClassifier());
|
||||
else
|
||||
return downloads.getArtifact();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||
if (temp != null && temp.getPath() != null)
|
||||
return temp.getPath();
|
||||
else
|
||||
return artifact.setClassifier(getClassifier()).getPath();
|
||||
}
|
||||
|
||||
public LibraryDownloadInfo getDownload() {
|
||||
return download;
|
||||
LibraryDownloadInfo temp = getRawDownloadInfo();
|
||||
String path = getPath();
|
||||
return new LibraryDownloadInfo(path,
|
||||
Optional.ofNullable(temp).map(LibraryDownloadInfo::getUrl).orElse(Optional.ofNullable(url).orElse(Constants.DEFAULT_LIBRARY_URL) + path),
|
||||
temp != null ? temp.getSha1() : null,
|
||||
temp != null ? temp.getSize() : 0
|
||||
);
|
||||
}
|
||||
|
||||
public List<String> getChecksums() {
|
||||
@@ -143,8 +150,24 @@ public class Library implements Comparable<Library> {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hint for how to locate the library file.
|
||||
* @return null for default, "local" for location in version/<version>/libraries/filename
|
||||
*/
|
||||
public String getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Available when hint is "local"
|
||||
* @return the filename of the local library in version/<version>/libraries/$filename
|
||||
*/
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public boolean is(String groupId, String artifactId) {
|
||||
return this.groupId.equals(groupId) && this.artifactId.equals(artifactId);
|
||||
return getGroupId().equals(groupId) && getArtifactId().equals(artifactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,58 +198,12 @@ public class Library implements Comparable<Library> {
|
||||
}
|
||||
|
||||
public Library setClassifier(String classifier) {
|
||||
return new Library(groupId, artifactId, version, classifier, url, downloads, checksums, extract, natives, rules);
|
||||
return new Library(artifact.setClassifier(classifier), url, downloads, checksums, extract, natives, rules, hint, fileName);
|
||||
}
|
||||
|
||||
public static Library fromName(String name) {
|
||||
return fromName(name, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public static Library fromName(String name, String url, LibrariesDownloadInfo downloads, List<String> checksums, ExtractRules extract, Map<OperatingSystem, String> natives, List<CompatibilityRule> rules) {
|
||||
String[] arr = name.split(":", 4);
|
||||
if (arr.length != 3 && arr.length != 4)
|
||||
throw new IllegalArgumentException("Library name is malformed. Correct example: group:artifact:version(:classifier).");
|
||||
|
||||
return new Library(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, url, downloads, checksums, extract, natives, rules);
|
||||
}
|
||||
|
||||
public static class Serializer implements JsonDeserializer<Library>, JsonSerializer<Library> {
|
||||
@Override
|
||||
public Library deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (json == null || json == JsonNull.INSTANCE)
|
||||
return null;
|
||||
JsonObject jsonObject = json.getAsJsonObject();
|
||||
if (!jsonObject.has("name"))
|
||||
throw new JsonParseException("Library name not found.");
|
||||
return fromName(
|
||||
jsonObject.get("name").getAsString(),
|
||||
jsonObject.has("url") ? jsonObject.get("url").getAsString() : null,
|
||||
context.deserialize(jsonObject.get("downloads"), LibrariesDownloadInfo.class),
|
||||
context.deserialize(jsonObject.get("checksums"), new TypeToken<List<String>>() {
|
||||
}.getType()),
|
||||
context.deserialize(jsonObject.get("extract"), ExtractRules.class),
|
||||
context.deserialize(jsonObject.get("natives"), new TypeToken<Map<OperatingSystem, String>>() {
|
||||
}.getType()),
|
||||
context.deserialize(jsonObject.get("rules"), new TypeToken<List<CompatibilityRule>>() {
|
||||
}.getType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Library src, Type type, JsonSerializationContext context) {
|
||||
if (src == null)
|
||||
return JsonNull.INSTANCE;
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("name", src.getName());
|
||||
obj.addProperty("url", src.url);
|
||||
obj.add("downloads", context.serialize(src.downloads));
|
||||
obj.add("checksums", context.serialize(src.getChecksums()));
|
||||
obj.add("extract", context.serialize(src.extract));
|
||||
obj.add("natives", context.serialize(src.natives, new TypeToken<Map<OperatingSystem, String>>() {
|
||||
}.getType()));
|
||||
obj.add("rules", context.serialize(src.rules, new TypeToken<List<CompatibilityRule>>() {
|
||||
}.getType()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException, TolerableValidationException {
|
||||
if (artifact == null)
|
||||
throw new JsonParseException("Library.name cannot be null");
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,12 @@ import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -66,16 +66,30 @@ public class Version implements Comparable<Version>, Validation {
|
||||
private final Date time;
|
||||
private final Date releaseTime;
|
||||
private final Integer minimumLauncherVersion;
|
||||
private final Boolean root;
|
||||
private final Boolean hidden;
|
||||
private final List<Version> patches;
|
||||
|
||||
private transient final boolean resolved;
|
||||
|
||||
public Version(String id, String version, int priority, Arguments arguments, String mainClass, List<Library> libraries) {
|
||||
this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, libraries, null, null, null, null, null, null, null, null, null);
|
||||
public Version(String id) {
|
||||
this(false, id, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false, true, null);
|
||||
}
|
||||
|
||||
public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Boolean hidden, List<Version> patches) {
|
||||
/**
|
||||
* Constructor for patch
|
||||
* @param id patch id
|
||||
* @param version patch version
|
||||
* @param priority patch priority
|
||||
* @param arguments patch additional arguments
|
||||
* @param mainClass main class to override
|
||||
* @param libraries additional libraries
|
||||
*/
|
||||
public Version(String id, String version, int priority, Arguments arguments, String mainClass, List<Library> libraries) {
|
||||
this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, libraries, null, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Boolean hidden, Boolean root, List<Version> patches) {
|
||||
this.resolved = resolved;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
@@ -87,8 +101,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this.jar = jar;
|
||||
this.assetIndex = assetIndex;
|
||||
this.assets = assets;
|
||||
this.libraries = libraries == null ? Collections.emptyList() : new LinkedList<>(libraries);
|
||||
this.compatibilityRules = compatibilityRules == null ? null : new LinkedList<>(compatibilityRules);
|
||||
this.libraries = Lang.copyList(libraries);
|
||||
this.compatibilityRules = Lang.copyList(compatibilityRules);
|
||||
this.downloads = downloads == null ? null : new HashMap<>(downloads);
|
||||
this.logging = logging == null ? null : new HashMap<>(logging);
|
||||
this.type = type;
|
||||
@@ -96,7 +110,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this.releaseTime = releaseTime == null ? null : (Date) releaseTime.clone();
|
||||
this.minimumLauncherVersion = minimumLauncherVersion;
|
||||
this.hidden = hidden;
|
||||
this.patches = patches == null ? null : patches.isEmpty() ? null : new LinkedList<>(patches);
|
||||
this.root = root;
|
||||
this.patches = Lang.copyList(patches);
|
||||
}
|
||||
|
||||
public Optional<String> getMinecraftArguments() {
|
||||
@@ -157,10 +172,16 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return hidden == null ? false : hidden;
|
||||
}
|
||||
|
||||
public boolean isRoot() { return root == null ? false : root; }
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public boolean isResolvedPreservingPatches() {
|
||||
return inheritsFrom == null && !resolved;
|
||||
}
|
||||
|
||||
public List<Version> getPatches() {
|
||||
return patches == null ? Collections.emptyList() : patches;
|
||||
}
|
||||
@@ -202,7 +223,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return resolve(provider, new HashSet<>()).setResolved();
|
||||
}
|
||||
|
||||
protected Version merge(Version parent) {
|
||||
protected Version merge(Version parent, boolean isPatch) {
|
||||
return new Version(
|
||||
true,
|
||||
id,
|
||||
@@ -224,7 +245,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
releaseTime,
|
||||
Lang.merge(minimumLauncherVersion, parent.minimumLauncherVersion, Math::max),
|
||||
hidden,
|
||||
Lang.merge(parent.patches, patches));
|
||||
false,
|
||||
isPatch ? parent.patches : Lang.merge(Lang.merge(parent.patches, Collections.singleton(toPatch())), patches));
|
||||
}
|
||||
|
||||
protected Version resolve(VersionProvider provider, Set<String> resolvedSoFar) throws VersionNotFoundException {
|
||||
@@ -239,7 +261,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
thisVersion = this.jar == null ? this.setJar(id) : this;
|
||||
} else {
|
||||
// It is supposed to auto install an version in getVersion.
|
||||
thisVersion = merge(provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar));
|
||||
thisVersion = merge(provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +271,41 @@ public class Version implements Comparable<Version>, Validation {
|
||||
.sorted(Comparator.comparing(Version::getPriority))
|
||||
.collect(Collectors.toList());
|
||||
for (Version patch : sortedPatches) {
|
||||
thisVersion = patch.setJar(null).merge(thisVersion);
|
||||
thisVersion = patch.setJar(null).merge(thisVersion, true);
|
||||
}
|
||||
}
|
||||
|
||||
return thisVersion.setId(id);
|
||||
}
|
||||
|
||||
private Version toPatch() {
|
||||
return this.clearPatches().setHidden(true).setId("resolved." + getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the version preserving all dependencies and patches.
|
||||
*/
|
||||
public Version resolvePreservingPatches(VersionProvider provider) throws VersionNotFoundException {
|
||||
return resolvePreservingPatches(provider, new HashSet<>());
|
||||
}
|
||||
|
||||
protected Version mergePreservingPatches(Version parent) {
|
||||
return parent.addPatch(toPatch()).addPatches(patches);
|
||||
}
|
||||
|
||||
protected Version resolvePreservingPatches(VersionProvider provider, Set<String> resolvedSoFar) throws VersionNotFoundException {
|
||||
Version thisVersion = isRoot() ? this : new Version(id).addPatch(toPatch()).addPatches(getPatches());
|
||||
|
||||
if (inheritsFrom == null) {
|
||||
// keep thisVersion
|
||||
} else {
|
||||
// To maximize the compatibility.
|
||||
if (!resolvedSoFar.add(id)) {
|
||||
Logging.LOG.log(Level.WARNING, "Found circular dependency versions: " + resolvedSoFar);
|
||||
// keep thisVersion
|
||||
} else {
|
||||
// It is supposed to auto install an version in getVersion.
|
||||
thisVersion = mergePreservingPatches(provider.getVersion(inheritsFrom).resolvePreservingPatches(provider, resolvedSoFar));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,58 +313,78 @@ public class Version implements Comparable<Version>, Validation {
|
||||
}
|
||||
|
||||
private Version setResolved() {
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
private Version setHidden(Boolean hidden) {
|
||||
return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setId(String id) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setVersion(String version) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setPriority(Integer priority) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setMinecraftArguments(String minecraftArguments) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setArguments(Arguments arguments) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setMainClass(String mainClass) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setInheritsFrom(String inheritsFrom) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setJar(String jar) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setLibraries(List<Library> libraries) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version setLogging(Map<DownloadType, LoggingInfo> logging) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, patches);
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version addPatch(Version patch) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, Lang.merge(patches, Collections.singleton(patch)));
|
||||
public Version setPatches(List<Version> patches) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches);
|
||||
}
|
||||
|
||||
public Version addPatch(Version... additional) {
|
||||
return addPatches(Arrays.asList(additional));
|
||||
}
|
||||
|
||||
public Version addPatches(List<Version> additional) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, Lang.merge(patches, additional));
|
||||
}
|
||||
|
||||
public Version clearPatches() {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root,null);
|
||||
}
|
||||
|
||||
public Version removePatchById(String patchId) {
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden,
|
||||
return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root,
|
||||
patches == null ? null : patches.stream().filter(patch -> !patchId.equals(patch.getId())).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public boolean hasPatch(String patchId) {
|
||||
return patches != null && patches.stream().anyMatch(patch -> patchId.equals(patch.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
|
||||
@@ -259,7 +259,7 @@ public class DefaultLauncher extends Launcher {
|
||||
pair("${auth_uuid}", UUIDTypeAdapter.fromUUID(authInfo.getUUID())),
|
||||
pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())),
|
||||
pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")),
|
||||
pair("${version_type}", version.getType().getId()),
|
||||
pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(version.getType().getId())),
|
||||
pair("${game_directory}", repository.getRunDirectory(version.getId()).getAbsolutePath()),
|
||||
pair("${user_type}", "mojang"),
|
||||
pair("${assets_index_name}", version.getAssetIndex().getId()),
|
||||
|
||||
@@ -72,6 +72,8 @@ public class ModpackUpdateTask extends Task<Void> {
|
||||
repository.removeVersionFromDisk(id);
|
||||
|
||||
FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id).toPath());
|
||||
|
||||
repository.refreshVersionsAsync().start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class MultiMCModpackExportTask extends Task<Void> {
|
||||
return false;
|
||||
});
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedVersion(versionId));
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId));
|
||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(versionId))
|
||||
.orElseThrow(() -> new IllegalStateException("Cannot parse the version of " + versionId));
|
||||
List<MultiMCManifest.MultiMCManifestComponent> components = new ArrayList<>();
|
||||
|
||||
+25
-11
@@ -21,16 +21,14 @@ import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.VersionLibraryBuilder;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@@ -41,6 +39,7 @@ import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -84,6 +83,12 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("liteloader", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> fabric = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.fabricmc.fabric-loader")).findAny();
|
||||
fabric.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("fabric", c.getVersion());
|
||||
});
|
||||
}
|
||||
|
||||
dependents.add(builder.buildAsync());
|
||||
@@ -145,20 +150,29 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
||||
for (Path patchJson : Files.newDirectoryStream(patches)) {
|
||||
if (patchJson.toString().endsWith(".json")) {
|
||||
// If json is malformed, we should stop installing this modpack instead of skipping it.
|
||||
MultiMCInstancePatch patch = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(Files.newInputStream(patchJson)), MultiMCInstancePatch.class);
|
||||
MultiMCInstancePatch multiMCPatch = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(Files.newInputStream(patchJson)), MultiMCInstancePatch.class);
|
||||
|
||||
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
|
||||
for (String arg : patch.getTweakers())
|
||||
builder.addArgument("--tweakClass", arg);
|
||||
List<String> arguments = new ArrayList<>();
|
||||
for (String arg : multiMCPatch.getTweakers()) {
|
||||
arguments.add("--tweakClass");
|
||||
arguments.add(arg);
|
||||
}
|
||||
|
||||
version = builder.build()
|
||||
.setLibraries(Lang.merge(version.getLibraries(), patch.getLibraries()))
|
||||
.setMainClass(patch.getMainClass());
|
||||
Version patch = new Version(multiMCPatch.getName(), multiMCPatch.getVersion(), 1, new Arguments().addGameArguments(arguments), multiMCPatch.getMainClass(), multiMCPatch.getLibraries());
|
||||
version = version.addPatch(patch);
|
||||
}
|
||||
}
|
||||
|
||||
Path libraries = root.resolve("libraries");
|
||||
if (Files.exists(libraries))
|
||||
FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries"));
|
||||
|
||||
Path jarmods = root.resolve("jarmods");
|
||||
if (Files.exists(jarmods))
|
||||
FileUtils.copyDirectory(jarmods, repository.getVersionRoot(name).toPath().resolve("jarmods"));
|
||||
}
|
||||
|
||||
dependencies.add(new MaintainTask(version).thenComposeAsync(repository::save));
|
||||
dependencies.add(repository.save(version));
|
||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ public abstract class Task<T> {
|
||||
*/
|
||||
public Task<T> storeTo(Consumer<T> action) {
|
||||
this.resultConsumer = action;
|
||||
action.accept(getResult());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,13 @@ public final class TaskExecutor {
|
||||
if (!success) {
|
||||
// We log exception stacktrace because some of exceptions occurred because of bugs.
|
||||
Logging.LOG.log(Level.WARNING, "An exception occurred in task execution", exception);
|
||||
|
||||
Throwable resolvedException = resolveException(exception);
|
||||
if (resolvedException instanceof RuntimeException) {
|
||||
// Track uncaught RuntimeException which are thrown mostly by our mistake
|
||||
if (uncaughtExceptionHandler != null)
|
||||
uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), resolvedException);
|
||||
}
|
||||
}
|
||||
|
||||
taskListeners.forEach(it -> it.onStop(success, this));
|
||||
@@ -253,4 +260,10 @@ public final class TaskExecutor {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null;
|
||||
|
||||
public static void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
|
||||
TaskExecutor.uncaughtExceptionHandler = uncaughtExceptionHandler;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||
|
||||
import java.util.*;
|
||||
@@ -57,6 +58,15 @@ public final class Lang {
|
||||
return Collections.unmodifiableList(Arrays.asList(elements));
|
||||
}
|
||||
|
||||
public static boolean test(ExceptionalRunnable<?> r) {
|
||||
try {
|
||||
r.run();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends Exception> boolean test(ExceptionalSupplier<Boolean, E> r) {
|
||||
try {
|
||||
return r.get();
|
||||
@@ -119,6 +129,10 @@ public final class Lang {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> List<T> copyList(List<T> list) {
|
||||
return list == null ? null : list.isEmpty() ? null : new ArrayList<>(list);
|
||||
}
|
||||
|
||||
public static void executeDelayed(Runnable runnable, TimeUnit timeUnit, long timeout, boolean isDaemon) {
|
||||
thread(() -> {
|
||||
try {
|
||||
|
||||
@@ -199,7 +199,7 @@ public final class FileUtils {
|
||||
Files.walkFileTree(src, new SimpleFileVisitor<Path>(){
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Path destFile = dest.resolve(src.relativize(file));
|
||||
Path destFile = dest.resolve(src.relativize(file).toString());
|
||||
Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
@@ -207,7 +207,7 @@ public final class FileUtils {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
Path destDir = dest.resolve(src.relativize(dir));
|
||||
Path destDir = dest.resolve(src.relativize(dir).toString());
|
||||
Files.createDirectories(destDir);
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -35,8 +34,9 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
/**
|
||||
* Represents a Java installation.
|
||||
@@ -115,11 +115,6 @@ public final class JavaVersion {
|
||||
Platform platform = Platform.BIT_32;
|
||||
String version = null;
|
||||
|
||||
// javaw is only used on windows
|
||||
if ("javaw.exe".equalsIgnoreCase(executable.getFileName().toString())) {
|
||||
executable = executable.resolveSibling("java.exe");
|
||||
}
|
||||
|
||||
executable = executable.toRealPath();
|
||||
|
||||
Process process = new ProcessBuilder(executable.toString(), "-version").start();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class SystemUtils {
|
||||
private SystemUtils() {}
|
||||
|
||||
public static int callExternalProcess(String... command) throws IOException, InterruptedException {
|
||||
return callExternalProcess(Arrays.asList(command));
|
||||
}
|
||||
|
||||
public static int callExternalProcess(List<String> command) throws IOException, InterruptedException {
|
||||
Process process = new ProcessBuilder(command).start();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
for (String line; (line = reader.readLine()) != null;) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
return process.waitFor();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
@@ -57,7 +74,6 @@ public class TaskTest {
|
||||
Assert.assertTrue("withRunAsync should be executed", bool.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThenAccept() {
|
||||
new JFXPanel(); // init JavaFX Toolkit
|
||||
AtomicBoolean flag = new AtomicBoolean();
|
||||
@@ -89,7 +105,6 @@ public class TaskTest {
|
||||
Assert.assertNull("CancellationException should not be recorded.", task.getException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectedExecutionException() {
|
||||
Schedulers.defaultScheduler();
|
||||
Schedulers.shutdown();
|
||||
|
||||
Reference in new issue
Block a user