show retry link when failed to fetch version list

This commit is contained in:
huangyuhui 2018-02-14 13:29:08 +08:00
parent 7732b21ca2
commit c76bcb5d15
11 changed files with 152 additions and 69 deletions

View File

@ -23,6 +23,7 @@ import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.task.FinalizedCallback;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.FileUtils;
@ -84,13 +85,13 @@ public final class ModpackHelper {
public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) { public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
profile.getRepository().markVersionAsModpack(name); profile.getRepository().markVersionAsModpack(name);
Task finalizeTask = Task.of(() -> { FinalizedCallback finalizeTask = (variables, isDependentsSucceeded) -> {
profile.getRepository().refreshVersions(); profile.getRepository().refreshVersions();
VersionSetting vs = profile.specializeVersionSetting(name); VersionSetting vs = profile.specializeVersionSetting(name);
profile.getRepository().undoMark(name); profile.getRepository().undoMark(name);
if (vs != null) if (vs != null)
vs.setGameDirType(EnumGameDirectory.VERSION_FOLDER); vs.setGameDirType(EnumGameDirectory.VERSION_FOLDER);
}); };
if (modpack.getManifest() instanceof CurseManifest) if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), zipFile, ((CurseManifest) modpack.getManifest()), name) return new CurseInstallTask(profile.getDependency(), zipFile, ((CurseManifest) modpack.getManifest()), name)

View File

@ -19,12 +19,15 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXSpinner; import com.jfoenix.controls.JFXSpinner;
import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
@ -33,7 +36,9 @@ import org.jackhuang.hmcl.ui.wizard.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.ui.wizard.WizardPage;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
public final class VersionsPage extends StackPane implements WizardPage, Refreshable { public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
private final WizardController controller; private final WizardController controller;
@ -45,7 +50,10 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
@FXML @FXML
private JFXListView<VersionsPageItem> list; private JFXListView<VersionsPageItem> list;
@FXML private JFXSpinner spinner; @FXML
private JFXSpinner spinner;
@FXML
private StackPane failedPane;
private final TransitionHandler transitionHandler = new TransitionHandler(this); private final TransitionHandler transitionHandler = new TransitionHandler(this);
private final VersionList<?> versionList; private final VersionList<?> versionList;
@ -62,7 +70,6 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
this.versionList = downloadProvider.getVersionListById(libraryId); this.versionList = downloadProvider.getVersionListById(libraryId);
FXUtils.loadFXML(this, "/assets/fxml/download/versions.fxml"); FXUtils.loadFXML(this, "/assets/fxml/download/versions.fxml");
getChildren().setAll(spinner);
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> { list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion()); controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion());
callback.run(); callback.run();
@ -72,15 +79,23 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
@Override @Override
public void refresh() { public void refresh() {
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx(), () -> { getChildren().setAll(spinner);
versionList.getVersions(gameVersion).stream() executor = versionList.refreshAsync(downloadProvider).finalized((variables, isDependentsSucceeded) -> {
.sorted(RemoteVersion.RemoteVersionComparator.INSTANCE) if (isDependentsSucceeded) {
.forEach(version -> { List<VersionsPageItem> items = versionList.getVersions(gameVersion).stream()
list.getItems().add(new VersionsPageItem(version)); .sorted(RemoteVersion.RemoteVersionComparator.INSTANCE)
}); .map(VersionsPageItem::new).collect(Collectors.toList());
transitionHandler.setContent(list, ContainerAnimations.FADE.getAnimationProducer()); Platform.runLater(() -> {
}); list.getItems().setAll(items);
transitionHandler.setContent(list, ContainerAnimations.FADE.getAnimationProducer());
});
} else {
Platform.runLater(() -> {
transitionHandler.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
});
}
}).executor().start();
} }
@Override @Override
@ -94,4 +109,9 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
if (executor != null) if (executor != null)
executor.cancel(); executor.cancel();
} }
@FXML
private void onRefresh() {
refresh();
}
} }

View File

@ -899,6 +899,7 @@
.jfx-spinner > .arc { .jfx-spinner > .arc {
-fx-stroke-width: 3.0; -fx-stroke-width: 3.0;
-fx-fill: transparent; -fx-fill: transparent;
-fx-stroke: -fx-base-color;
} }
.first-spinner { .first-spinner {
@ -957,50 +958,6 @@
-fx-stroke-width: 5.0; -fx-stroke-width: 5.0;
} }
.blue-spinner .arc {
-fx-stroke: #4285f4;
}
.red-spinner .arc {
-fx-stroke: #db4437;
}
.green-spinner .arc {
-fx-stroke: #f4b400;
}
.yellow-spinner .arc {
-fx-stroke: -fx-base-check-color;
}
.materialDesign-purple .arc {
-fx-stroke: #ab47bc;
}
.materialDesign-blue .arc {
-fx-stroke: #2962ff;
}
.materialDesign-cyan .arc {
-fx-stroke: #00b8d4;
}
.materialDesign-green .arc {
-fx-stroke: #00c853;
}
.materialDesign-yellow .arc {
-fx-stroke: #ffd600;
}
.materialDesign-orange .arc {
-fx-stroke: #ff6d00;
}
.materialDesign-red .arc {
-fx-stroke: #d50000;
}
/******************************************************************************* /*******************************************************************************
* * * *
* JFX Combo Box * * JFX Combo Box *

View File

@ -29,7 +29,7 @@
</JFXListView> </JFXListView>
</StackPane> </StackPane>
<HBox alignment="BOTTOM_CENTER" style="-fx-padding: 20;"> <HBox alignment="BOTTOM_CENTER" style="-fx-padding: 20;" pickOnBounds="false">
<Label text="%modpack.introduction" /> <Label text="%modpack.introduction" />
</HBox> </HBox>
</fx:root> </fx:root>

View File

@ -3,11 +3,15 @@
<?import com.jfoenix.controls.JFXListView?> <?import com.jfoenix.controls.JFXListView?>
<?import com.jfoenix.controls.JFXSpinner?> <?import com.jfoenix.controls.JFXSpinner?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Label?>
<fx:root xmlns="http://javafx.com/javafx" <fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
type="StackPane" type="StackPane"
prefHeight="400.0" prefWidth="600.0"> prefHeight="400.0" prefWidth="600.0">
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" /> <JFXSpinner fx:id="spinner" styleClass="first-spinner" />
<JFXListView fx:id="list" styleClass="jfx-list-view"> <JFXListView fx:id="list" styleClass="jfx-list-view">
</JFXListView> </JFXListView>
<StackPane fx:id="failedPane">
<Label onMouseClicked="#onRefresh" style="-fx-text-fill: #0079FF; -fx-font-size: 20;" text="%download.failed.refresh" />
</StackPane>
</fx:root> </fx:root>

View File

@ -105,6 +105,7 @@ crash.user_fault=Your OS or Java environment may not be properly installed resul
download=Download download=Download
download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/) download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/)
download.failed=Failed to download download.failed=Failed to download
download.failed.refresh=Unable to load version list. Click here to retry.
download.mojang=Mojang download.mojang=Mojang
download.not_200=Failed to download, the response code is %s. download.not_200=Failed to download, the response code is %s.
download.source=Download Source download.source=Download Source

View File

@ -105,6 +105,7 @@ crash.user_fault=您的系统或Java环境可能安装不当导致本软件崩
download=下载 download=下载
download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/) download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/)
download.failed=下载失败 download.failed=下载失败
download.failed.refresh=加载版本列表失败,点击此处重试。
download.mojang=官方 download.mojang=官方
download.not_200=下载失败HTTP状态码%s. download.not_200=下载失败HTTP状态码%s.
download.source=下载源 download.source=下载源

View File

@ -62,7 +62,7 @@ public class DefaultGameBuilder extends GameBuilder {
downloadGameAsync(gameVersion, version), downloadGameAsync(gameVersion, version),
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries. new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant. ).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant.
if (toolVersions.containsKey("forge")) if (toolVersions.containsKey("forge"))
result = result.then(libraryTaskHelper(gameVersion, "forge")); result = result.then(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader")) if (toolVersions.containsKey("liteloader"))
@ -70,12 +70,9 @@ public class DefaultGameBuilder extends GameBuilder {
if (toolVersions.containsKey("optifine")) if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine")); result = result.then(libraryTaskHelper(gameVersion, "optifine"));
return result; return result;
}).finalized(new Task() { }).finalized((variables, isDependentsSucceeded) -> {
@Override if (!isDependentsSucceeded)
public void execute() { dependencyManager.getGameRepository().getVersionRoot(name).delete();
if (!isDependentsSucceeded())
dependencyManager.getGameRepository().getVersionRoot(name).delete();
}
}); });
} }

View File

@ -0,0 +1,24 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
public interface FinalizedCallback {
void execute(AutoTypingMap<String> variables, boolean isDependentsSucceeded) throws Exception;
}

View File

@ -0,0 +1,78 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.ExceptionalFunction;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class FinalizedTask extends Task {
private final Collection<Task> dependents;
private final FinalizedCallback callback;
private final Scheduler scheduler;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before succ.
* @param callback 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.
*/
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback) {
this.dependents = Collections.singleton(pred);
this.scheduler = scheduler;
this.callback = callback;
setSignificance(TaskSignificance.MODERATE);
setName(callback.toString());
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
setName(callback.toString());
callback.execute(getVariables(), isDependentsSucceeded());
if (!isDependentsSucceeded())
throw new SilentException();
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
}

View File

@ -260,12 +260,12 @@ public abstract class Task {
return new CoupleTask<>(this, b, false, false); return new CoupleTask<>(this, b, false, false);
} }
public final Task finalized(Task b) { public final Task finalized(FinalizedCallback b) {
return finalized(convert(b)); return finalized(Schedulers.defaultScheduler(), b);
} }
public final Task finalized(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) { public final Task finalized(Scheduler scheduler, FinalizedCallback b) {
return new CoupleTask<>(this, b, false, true); return new FinalizedTask(this, scheduler, b);
} }
public static Task empty() { public static Task empty() {