Compare commits

...
7 Commits
32 changed files with 360 additions and 226 deletions

No files matched your search

@@ -87,6 +87,8 @@ public final class Main extends Application {
public static void stopWithoutPlatform() {
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
Logging.LOG.info("Shutting down executor services.");
@@ -530,8 +530,8 @@ public class Settings {
}
private void onProfileChanged() {
getSelectedProfile().getRepository().refreshVersionsAsync().subscribe(() ->
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile())));
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
getSelectedProfile().getRepository().refreshVersionsAsync().start();
}
private void profileNameChanged(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
@@ -487,6 +487,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
}
private void setContent(Node content, AnimationProducer animation) {
isWizardPageNow = false;
animationHandler.setContent(content, animation);
if (content instanceof Region) {
@@ -510,6 +511,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private String category;
private Node nowPage;
private boolean isWizardPageNow;
public Node getNowPage() {
return nowPage;
@@ -573,6 +575,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
contentPlaceHolder.getStyleClass().removeAll("gray-background", "white-background");
contentPlaceHolder.getStyleClass().add("white-background");
setContent(page, nav.getAnimation().getAnimationProducer());
isWizardPageNow = true;
}
@FXML
@@ -588,7 +591,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
@FXML
private void onBack() {
if (wizardController.canPrev())
if (isWizardPageNow && wizardController.canPrev())
wizardController.onPrev(true);
else
onCloseNav();
@@ -17,11 +17,11 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXMasonryPane;
import com.jfoenix.controls.JFXPopup;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
@@ -33,7 +33,6 @@ import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
@@ -79,7 +78,11 @@ public final class MainPage extends StackPane implements DecoratorPage {
{
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(() -> Platform.runLater(this::loadVersions));
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> {
if (event.getSource() == profile.getRepository())
loadVersions((HMCLGameRepository) event.getSource());
});
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> this.profile = event.getProfile());
versionPopup = new JFXPopup(versionList);
getChildren().remove(versionList);
@@ -90,14 +93,15 @@ public final class MainPage extends StackPane implements DecoratorPage {
FXUtils.installTooltip(btnRefresh, Main.i18n("button.refresh"));
}
private Node buildNode(Profile profile, String version, String game) {
private Node buildNode(HMCLGameRepository repository, Version version, String game) {
String id = version.getId();
VersionItem item = new VersionItem();
item.setUpdate(profile.getRepository().isModpack(version));
item.setUpdate(repository.isModpack(id));
item.setGameVersion(game);
item.setVersionName(version);
item.setVersionName(id);
StringBuilder libraries = new StringBuilder();
for (Library library : profile.getRepository().getVersion(version).getLibraries()) {
for (Library library : version.getLibraries()) {
if (library.getGroupId().equalsIgnoreCase("net.minecraftforge") && library.getArtifactId().equalsIgnoreCase("forge")) {
libraries.append(Main.i18n("install.installer.forge")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)forge", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
@@ -114,26 +118,26 @@ public final class MainPage extends StackPane implements DecoratorPage {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
else
LauncherHelper.INSTANCE.launch(version, null);
LauncherHelper.INSTANCE.launch(id, null);
});
item.setOnScriptButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
else {
FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(profile.getRepository().getRunDirectory(version));
chooser.setInitialDirectory(repository.getRunDirectory(id));
chooser.setTitle(Main.i18n("version.launch_script.save"));
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
? new FileChooser.ExtensionFilter(Main.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Main.i18n("extension.sh"), "*.sh"));
File file = chooser.showSaveDialog(Controllers.getStage());
if (file != null)
LauncherHelper.INSTANCE.launch(version, file);
LauncherHelper.INSTANCE.launch(id, file);
}
});
item.setOnSettingsButtonClicked(e -> {
Controllers.getDecorator().showPage(Controllers.getVersionPage());
Controllers.getVersionPage().load(version, profile);
Controllers.getVersionPage().load(id, profile);
});
item.setOnUpdateButtonClicked(event -> {
FileChooser chooser = new FileChooser();
@@ -143,7 +147,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
if (selectedFile != null) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(null);
try {
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, version, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(version)))
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id)))
.then(Task.of(Schedulers.javafx(), Controllers::closeDialog)).executor();
pane.setExecutor(executor);
pane.setTitle(Main.i18n("modpack.update"));
@@ -160,33 +164,28 @@ public final class MainPage extends StackPane implements DecoratorPage {
});
item.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.SECONDARY) {
rightClickedVersion = version;
rightClickedVersion = id;
versionList.getSelectionModel().select(-1);
versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY());
} else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
else
LauncherHelper.INSTANCE.launch(version, null);
LauncherHelper.INSTANCE.launch(id, null);
}
});
File iconFile = profile.getRepository().getVersionIcon(version);
File iconFile = repository.getVersionIcon(id);
if (iconFile.exists())
item.setImage(new Image("file:" + iconFile.getAbsolutePath()));
return item;
}
private void loadVersions() {
loadVersions(Settings.INSTANCE.getSelectedProfile());
}
private void loadVersions(Profile profile) {
this.profile = profile;
private void loadVersions(HMCLGameRepository repository) {
List<Node> children = new LinkedList<>();
for (Version version : profile.getRepository().getVersions()) {
children.add(buildNode(profile, version.getId(), GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())).orElse("Unknown")));
for (Version version : repository.getVersions()) {
children.add(buildNode(repository, version, GameVersion.minecraftVersion(repository.getVersionJar(version.getId())).orElse("Unknown")));
}
FXUtils.resetChildren(masonryPane, children);
JFXUtilities.runInFX(() -> FXUtils.resetChildren(masonryPane, children));
}
@FXML
@@ -51,15 +51,16 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
*/
public ProfilePage(Profile profile) {
this.profile = profile;
String profileDisplayName = Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse("");
title = new SimpleStringProperty(this, "title",
profile == null ? Main.i18n("profile.new") : Main.i18n("profile") + " - " + profile.getName());
profile == null ? Main.i18n("profile.new") : Main.i18n("profile") + " - " + profileDisplayName);
location = new SimpleStringProperty(this, "location",
Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse(""));
FXUtils.loadFXML(this, "/assets/fxml/profile.fxml");
txtProfileName.setText(Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse(""));
txtProfileName.setText(profileDisplayName);
FXUtils.onChangeAndOperate(txtProfileName.textProperty(), it -> {
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
});
@@ -24,6 +24,7 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -31,6 +32,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.Lang;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.Main.i18n;
@@ -101,24 +103,28 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
return Main.i18n("settings.tabs.installers");
}
private String getVersion(String id) {
return Optional.ofNullable(controller.getSettings().get(id)).map(it -> (RemoteVersion<?>) it).map(RemoteVersion::getSelfVersion).orElse(null);
}
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
btnForge.setDisable(provider.getForge() != null);
if (provider.getForge() != null || controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), controller.getSettings().get("forge")));
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), getVersion("forge")));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
btnLiteLoader.setDisable(provider.getLiteLoader() != null);
if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), controller.getSettings().get("liteloader")));
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), getVersion("liteloader")));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
btnOptiFine.setDisable(provider.getOptiFine() != null);
if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), controller.getSettings().get("optifine")));
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), getVersion("optifine")));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
@@ -21,6 +21,7 @@ import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Profile;
@@ -48,16 +49,16 @@ public final class DownloadWizardProvider implements WizardProvider {
GameBuilder builder = profile.getDependency().gameBuilder();
builder.name((String) settings.get("name"));
builder.gameVersion((String) settings.get("game"));
builder.gameVersion(((RemoteVersion<?>) settings.get("game")).getGameVersion());
if (settings.containsKey("forge"))
builder.version("forge", (String) settings.get("forge"));
builder.version((RemoteVersion<?>) settings.get("forge"));
if (settings.containsKey("liteloader"))
builder.version("liteloader", (String) settings.get("liteloader"));
builder.version((RemoteVersion<?>) settings.get("liteloader"));
if (settings.containsKey("optifine"))
builder.version("optifine", (String) settings.get("optifine"));
builder.version((RemoteVersion<?>) settings.get("optifine"));
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions());
}
@@ -37,6 +37,8 @@ public final class InstallTypePage extends StackPane implements WizardPage {
FXUtils.loadFXML(this, "/assets/fxml/download/dltype.fxml");
list.setOnMouseClicked(e -> {
if (list.getSelectionModel().getSelectedIndex() < 0)
return;
controller.getSettings().put(INSTALL_TYPE, list.getSelectionModel().getSelectedIndex());
controller.onNext();
});
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Task;
@@ -85,13 +86,13 @@ public final class InstallerWizardProvider implements WizardProvider {
Task ret = Task.empty();
if (settings.containsKey("forge"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "forge", (String) settings.get("forge")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("forge")));
if (settings.containsKey("liteloader"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "liteloader", (String) settings.get("liteloader")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("liteloader")));
if (settings.containsKey("optifine"))
ret = ret.then(profile.getDependency().installLibraryAsync(gameVersion, version, "optifine", (String) settings.get("optifine")));
ret = ret.then(profile.getDependency().installLibraryAsync(version, (RemoteVersion<?>) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync());
}
@@ -25,6 +25,7 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.Validator;
@@ -33,6 +34,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.StringUtils;
import java.util.Map;
import java.util.Optional;
public class InstallersPage extends StackPane implements WizardPage {
private final WizardController controller;
@@ -76,7 +78,7 @@ public class InstallersPage extends StackPane implements WizardPage {
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
String gameVersion = (String) controller.getSettings().get("game");
String gameVersion = ((RemoteVersion<?>) controller.getSettings().get("game")).getGameVersion();
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
hasVersion.setMessage(Main.i18n("install.new_game.already_exists"));
txtName.getValidators().add(hasVersion);
@@ -104,21 +106,25 @@ public class InstallersPage extends StackPane implements WizardPage {
return Main.i18n("install.new_game");
}
private String getVersion(String id) {
return ((RemoteVersion<?>) controller.getSettings().get(id)).getSelfVersion();
}
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + controller.getSettings().get("game"));
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + getVersion("game"));
if (controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + controller.getSettings().get("forge"));
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + getVersion("forge"));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
if (controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + controller.getSettings().get("liteloader"));
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + getVersion("liteloader"));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
if (controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + controller.getSettings().get("optifine"));
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + getVersion("optifine"));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
}
@@ -40,6 +40,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
@@ -93,7 +94,9 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
chkOld.selectedProperty().addListener(listener);
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion());
if (newValue == null)
return;
controller.getSettings().put(libraryId, newValue.getRemoteVersion());
callback.run();
});
refresh();
@@ -114,6 +117,7 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
}
else return true;
})
.filter(Objects::nonNull)
.sorted()
.map(VersionsPageItem::new).collect(Collectors.toList());
}
@@ -30,6 +30,8 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersionTag;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Objects;
/**
* @author huangyuhui
*/
@@ -47,7 +49,7 @@ public final class VersionsPageItem extends StackPane {
private StackPane imageViewContainer;
public VersionsPageItem(RemoteVersion<?> remoteVersion) {
this.remoteVersion = remoteVersion;
this.remoteVersion = Objects.requireNonNull(remoteVersion);
FXUtils.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
lblSelfVersion.setText(remoteVersion.getSelfVersion());
@@ -15,7 +15,7 @@
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblVersionName" style="-fx-font-size: 15;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblGameVersion" style="-fx-font-size: 11;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 11; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 10; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
</VBox>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40" pickOnBounds="false">
@@ -18,9 +18,12 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
@@ -80,25 +83,31 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
@Override
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
switch (libraryId) {
case "forge":
return new ForgeInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "liteloader":
return new LiteLoaderInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
case "optifine":
return new OptiFineInstallTask(this, gameVersion, version, libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
default:
throw new IllegalArgumentException("Library id " + libraryId + " is unrecognized.");
}
VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(getDownloadProvider())
.then(variables -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
}
@Override
public Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion) {
if (libraryVersion instanceof ForgeRemoteVersion)
return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
return new LiteLoaderInstallTask(this, version, (LiteLoaderRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else if (libraryVersion instanceof OptiFineRemoteVersion)
return new OptiFineInstallTask(this, version, (OptiFineRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
else
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
}
}
@@ -67,6 +67,10 @@ public class DefaultGameBuilder extends GameBuilder {
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
for (RemoteVersion<?> remoteVersion : remoteVersions)
result = result.then(var -> dependencyManager.installLibraryAsync(var.get("version"), remoteVersion));
return result;
}).finalized((variables, isDependentsSucceeded) -> {
if (!isDependentsSucceeded)
@@ -66,6 +66,16 @@ public interface DependencyManager {
*/
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/**
* Install a library to a version.
* **Note**: Installing a library may change the version.json.
*
* @param version the version.json.\
* @param libraryVersion the remote version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(Version version, RemoteVersion<?> libraryVersion);
/**
* Get registered version list.
*
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.task.Task;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.*;
/**
* The builder which provide a task to build Minecraft environment.
@@ -33,6 +31,7 @@ public abstract class GameBuilder {
protected String name = "";
protected String gameVersion = "";
protected final Map<String, String> toolVersions = new HashMap<>();
protected final Set<RemoteVersion<?>> remoteVersions = new HashSet<>();
public String getName() {
return name;
@@ -65,6 +64,11 @@ public abstract class GameBuilder {
return this;
}
public GameBuilder version(RemoteVersion<?> remoteVersion) {
remoteVersions.add(remoteVersion);
return this;
}
/**
* @return the task that can build thw whole Minecraft environment
*/
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.VersionNumber;
import java.util.Objects;
@@ -74,6 +75,15 @@ public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
return selfVersion.hashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("selfVersion", selfVersion)
.append("gameVersion", gameVersion)
.append("tag", tag)
.toString();
}
@Override
public int compareTo(RemoteVersion<T> o) {
// newer versions are smaller than older versions
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* The remote version list.
@@ -45,15 +46,36 @@ public abstract class VersionList<T> {
return !versions.isEmpty();
}
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public abstract Task refreshAsync(DownloadProvider downloadProvider);
public Task loadAsync(DownloadProvider downloadProvider) {
return Task.ofThen(variables -> {
lock.readLock().lock();
boolean loaded;
try {
loaded = isLoaded();
} finally {
lock.readLock().unlock();
}
return loaded ? null : refreshAsync(downloadProvider);
});
}
private Collection<RemoteVersion<T>> getVersionsImpl(String gameVersion) {
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
return ans.isEmpty() ? versions.values() : ans;
lock.readLock().lock();
try {
Collection<RemoteVersion<T>> ans = versions.get(gameVersion);
return ans.isEmpty() ? versions.values() : ans;
} finally {
lock.readLock().unlock();
}
}
/**
@@ -74,10 +96,15 @@ public abstract class VersionList<T> {
* @return the specific remote version, null if it is not found.
*/
public final Optional<RemoteVersion<T>> getVersion(String gameVersion, String remoteVersion) {
RemoteVersion<T> result = null;
for (RemoteVersion<T> it : versions.get(gameVersion))
if (remoteVersion.equals(it.getSelfVersion()))
result = it;
return Optional.ofNullable(result);
lock.readLock().lock();
try {
RemoteVersion<T> result = null;
for (RemoteVersion<T> it : versions.get(gameVersion))
if (remoteVersion.equals(it.getSelfVersion()))
result = it;
return Optional.ofNullable(result);
} finally {
lock.readLock().unlock();
}
}
}
@@ -18,8 +18,6 @@
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
@@ -47,34 +45,22 @@ import java.util.zip.ZipFile;
public final class ForgeInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> forgeVersionList;
private final File installer = new File("forge-installer.jar").getAbsoluteFile();
private RemoteVersion<?> remote;
private ForgeRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private Task downloadFileTask() {
remote = forgeVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote forge version " + gameVersion + ", " + remoteVersion + " not found"));
return new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer);
}
public ForgeInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
this.remote = remoteVersion;
forgeVersionList = dependencyManager.getVersionList("forge");
if (!forgeVersionList.isLoaded())
dependents.add(forgeVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(s -> downloadFileTask()));
else
dependents.add(downloadFileTask());
dependents.add(downloadFileTask());
}
@Override
@@ -0,0 +1,33 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.RemoteVersion;
public class ForgeRemoteVersion extends RemoteVersion<Void> {
/**
* Constructor.
*
* @param gameVersion the Minecraft version that this remote version suits.
* @param selfVersion the version string of the remote version.
* @param url the installer or universal jar URL.
*/
public ForgeRemoteVersion(String gameVersion, String selfVersion, String url) {
super(gameVersion, selfVersion, url, null);
}
}
@@ -53,34 +53,40 @@ public final class ForgeVersionList extends VersionList<Void> {
@Override
public void execute() {
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
if (root == null)
return;
versions.clear();
lock.writeLock().lock();
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
Optional<String> gameVersion = VersionNumber.parseVersion(entry.getKey());
if (!gameVersion.isPresent())
continue;
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
continue;
String jar = null;
for (String[] file : version.getFiles())
if (file.length > 1 && "installer".equals(file[1])) {
String classifier = version.getGameVersion() + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
}
try {
ForgeVersionRoot root = Constants.GSON.fromJson(task.getResult(), ForgeVersionRoot.class);
if (root == null)
return;
versions.clear();
if (jar == null)
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
Optional<String> gameVersion = VersionNumber.parseVersion(entry.getKey());
if (!gameVersion.isPresent())
continue;
versions.put(gameVersion.get(), new RemoteVersion<>(
version.getGameVersion(), version.getVersion(), jar, null
));
for (int v : entry.getValue()) {
ForgeVersion version = root.getNumber().get(v);
if (version == null)
continue;
String jar = null;
for (String[] file : version.getFiles())
if (file.length > 1 && "installer".equals(file[1])) {
String classifier = version.getGameVersion() + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0];
jar = downloadProvider.injectURL(root.getWebPath() + classifier + "/" + fileName);
}
if (jar == null)
continue;
versions.put(gameVersion.get(), new ForgeRemoteVersion(
version.getGameVersion(), version.getVersion(), jar
));
}
}
} finally {
lock.writeLock().unlock();
}
}
@@ -50,16 +50,22 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
@Override
public void execute() {
versions.clear();
lock.writeLock().lock();
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
);
try {
versions.clear();
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
new GameRemoteVersionTag(remoteVersion.getType(), remoteVersion.getReleaseTime()))
);
}
} finally {
lock.writeLock().unlock();
}
}
};
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
@@ -41,32 +40,15 @@ import java.util.List;
public final class LiteLoaderInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final LiteLoaderVersionList liteLoaderVersionList;
private RemoteVersion<LiteLoaderRemoteVersionTag> remote;
private LiteLoaderRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote LiteLoader version " + gameVersion + ", " + remoteVersion + " not found"));
}
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
liteLoaderVersionList = (LiteLoaderVersionList) dependencyManager.getVersionList("liteloader");
if (!liteLoaderVersionList.isLoaded())
dependents.add(liteLoaderVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(Task.of(this::doRemote)));
else
doRemote();
this.remote = remoteVersion;
}
@Override
@@ -0,0 +1,36 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Library;
import java.util.Collection;
public class LiteLoaderRemoteVersion extends RemoteVersion<LiteLoaderRemoteVersionTag> {
/**
* Constructor.
*
* @param gameVersion the Minecraft version that this remote version suits.
* @param selfVersion the version string of the remote version.
* @param url the installer or universal jar URL.
*/
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, String url, String tweakClass, Collection<Library> libraries) {
super(gameVersion, selfVersion, url, new LiteLoaderRemoteVersionTag(tweakClass, libraries));
}
}
@@ -58,17 +58,23 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
@Override
public void execute() {
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
versions.clear();
lock.writeLock().lock();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
if (!gg.isPresent())
continue;
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
try {
LiteLoaderVersionsRoot root = Constants.GSON.fromJson(task.getResult(), LiteLoaderVersionsRoot.class);
versions.clear();
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
String gameVersion = entry.getKey();
LiteLoaderGameVersions liteLoader = entry.getValue();
Optional<String> gg = VersionNumber.parseVersion(gameVersion);
if (!gg.isPresent())
continue;
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
doBranch(gg.get(), gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
}
} finally {
lock.writeLock().unlock();
}
}
@@ -92,9 +98,9 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
}
}
versions.put(key, new RemoteVersion<>(gameVersion,
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
version, downloadProvider.injectURL(url),
new LiteLoaderRemoteVersionTag(v.getTweakClass(), v.getLibraries())
v.getTweakClass(), v.getLibraries()
));
}
}
@@ -67,7 +67,7 @@ public final class OptiFineBMCLVersionList extends VersionList<Void> {
if (StringUtils.isBlank(element.getGameVersion()))
continue;
VersionNumber.parseVersion(element.getGameVersion())
.ifPresent(gameVersion -> versions.put(gameVersion, new RemoteVersion<>(gameVersion, version, mirror, null)));
.ifPresent(gameVersion -> versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, () -> mirror)));
}
}
};
@@ -18,8 +18,6 @@
package org.jackhuang.hmcl.download.optifine;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
@@ -39,32 +37,15 @@ import java.util.List;
public final class OptiFineInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final String remoteVersion;
private final VersionList<?> optiFineVersionList;
private RemoteVersion<?> remote;
private OptiFineRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private void doRemote() {
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion)
.orElseThrow(() -> new IllegalArgumentException("Remote OptiFine version " + gameVersion + ", " + remoteVersion + " not found"));
}
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version, String remoteVersion) {
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
this.gameVersion = gameVersion;
this.version = version;
this.remoteVersion = remoteVersion;
optiFineVersionList = dependencyManager.getVersionList("optifine");
if (!optiFineVersionList.isLoaded())
dependents.add(optiFineVersionList.refreshAsync(dependencyManager.getDownloadProvider())
.then(Task.of(this::doRemote)));
else
doRemote();
this.remote = remoteVersion;
}
@Override
@@ -89,6 +70,8 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
@Override
public void execute() {
String remoteVersion = remote.getSelfVersion();
Library library = new Library(
"net.optifine", "optifine", remoteVersion, null, null,
new LibrariesDownloadInfo(new LibraryDownloadInfo(
@@ -24,8 +24,8 @@ import java.util.function.Supplier;
public class OptiFineRemoteVersion extends RemoteVersion<Void> {
private final Supplier<String> url;
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url, Void tag) {
super(gameVersion, selfVersion, "", tag);
public OptiFineRemoteVersion(String gameVersion, String selfVersion, Supplier<String> url) {
super(gameVersion, selfVersion, "", null);
this.url = url;
}
@@ -61,48 +61,54 @@ public final class OptiFineVersionList extends VersionList<Void> {
@Override
public void execute() throws Exception {
versions.clear();
lock.writeLock().lock();
String html = task.getResult().replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
try {
versions.clear();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
String url = null, version = null, gameVersion = null;
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
String html = task.getResult().replace("&nbsp;", " ").replace("&gt;", ">").replace("&lt;", "<").replace("<br>", "<br />");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(html.getBytes("UTF-8")));
Element r = doc.getDocumentElement();
NodeList tables = r.getElementsByTagName("table");
for (int i = 0; i < tables.getLength(); i++) {
Element e = (Element) tables.item(i);
if ("downloadTable".equals(e.getAttribute("class"))) {
NodeList tr = e.getElementsByTagName("tr");
for (int k = 0; k < tr.getLength(); k++) {
NodeList downloadLine = ((Element) tr.item(k)).getElementsByTagName("td");
String url = null, version = null, gameVersion = null;
for (int j = 0; j < downloadLine.getLength(); j++) {
Element td = (Element) downloadLine.item(j);
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineMirror"))
url = ((Element) td.getElementsByTagName("a").item(0)).getAttribute("href");
if (td.getAttribute("class") != null && td.getAttribute("class").startsWith("downloadLineFile"))
version = td.getTextContent();
}
if (version == null || url == null)
continue;
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null)
continue;
String finalURL = url;
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Lang.hideException(() -> getLink(finalURL))));
}
if (version == null || url == null)
continue;
Matcher matcher = PATTERN.matcher(version);
while (matcher.find())
gameVersion = matcher.group(1);
if (gameVersion == null)
continue;
String finalURL = url;
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Lang.hideException(() -> getLink(finalURL)), null));
}
}
} finally {
lock.writeLock().unlock();
}
}
};
}
public static String getLink(String url) throws IOException {
private static String getLink(String url) throws IOException {
String result = null;
String content = NetworkUtils.doGet(NetworkUtils.toURL(url));
Matcher m = LINK_PATTERN.matcher(content);
@@ -33,7 +33,6 @@ import java.util.List;
final class CoupleTask<P extends Task> extends Task {
private final boolean relyingOnDependents;
private final boolean failIfDependentsFail;
private final Collection<Task> dependents;
private final List<Task> dependencies = new LinkedList<>();
private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ;
@@ -45,11 +44,10 @@ final class CoupleTask<P extends Task> extends Task {
* @param succ a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
*/
public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents, boolean failIfDependentsFail) {
this.dependents = Collections.singleton(pred);
public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents) {
this.dependents = pred == null ? Collections.emptySet() : Collections.singleton(pred);
this.succ = succ;
this.relyingOnDependents = relyingOnDependents;
this.failIfDependentsFail = failIfDependentsFail;
setSignificance(TaskSignificance.MODERATE);
setName(succ.toString());
@@ -61,9 +59,6 @@ final class CoupleTask<P extends Task> extends Task {
Task task = succ.apply(getVariables());
if (task != null)
dependencies.add(task);
if (failIfDependentsFail && !isDependentsSucceeded())
throw new SilentException();
}
@Override
@@ -244,7 +244,7 @@ public abstract class Task {
}
public final Task then(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(this, b, true, false);
return new CoupleTask<>(this, b, true);
}
public final Task with(Task b) {
@@ -252,7 +252,7 @@ public abstract class Task {
}
public final <E extends Exception> Task with(ExceptionalFunction<AutoTypingMap<String>, Task, E> b) {
return new CoupleTask<>(this, b, false, false);
return new CoupleTask<>(this, b, false);
}
public final Task finalized(FinalizedCallback b) {
@@ -312,6 +312,10 @@ public abstract class Task {
return of(ReflectionHelper.getCaller().toString(), scheduler, closure);
}
public static Task ofThen(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(null, b, true);
}
public static <V> TaskResult<V> ofResult(String id, Callable<V> callable) {
return new TaskCallable<>(id, callable);
}