Cached Minecraft jars

This commit is contained in:
huangyuhui 2018-01-27 14:05:39 +08:00
parent 1a572be52e
commit 418be689f2
17 changed files with 133 additions and 38 deletions

View File

@ -69,10 +69,10 @@ public class HMCLGameDownloadTask extends Task {
dependencies.add(new FileDownloadTask( dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())), NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
jar, cache,
profile.getDependency().getProxy(), profile.getDependency().getProxy(),
version.getDownloadInfo().getSha1() version.getDownloadInfo().getSha1()
)); ).then(Task.of(v -> FileUtils.copyFile(cache, jar))));
} }
} }

View File

@ -22,6 +22,7 @@ import com.google.gson.GsonBuilder;
import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.setting.VersionSetting;
@ -63,7 +64,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
@Override @Override
public File getRunDirectory(String id) { public File getRunDirectory(String id) {
if (beingModpackVersions.contains(id)) if (beingModpackVersions.contains(id) || isModpack(id))
return getVersionRoot(id); return getVersionRoot(id);
else { else {
VersionSetting vs = profile.getVersionSetting(id); VersionSetting vs = profile.getVersionSetting(id);
@ -155,7 +156,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
if (!hasVersion(id)) if (!hasVersion(id))
return null; return null;
if (versionSettings.containsKey(id)) if (versionSettings.containsKey(id))
return versionSettings.get(id); return getVersionSetting(id);
else else
return initVersionSetting(id, new VersionSetting()); return initVersionSetting(id, new VersionSetting());
} }
@ -176,7 +177,10 @@ public class HMCLGameRepository extends DefaultGameRepository {
public VersionSetting getVersionSetting(String id) { public VersionSetting getVersionSetting(String id) {
if (!versionSettings.containsKey(id)) if (!versionSettings.containsKey(id))
loadVersionSetting(id); loadVersionSetting(id);
return versionSettings.get(id); VersionSetting setting = versionSettings.get(id);
if (setting != null && isModpack(id))
setting.setGameDirType(EnumGameDirectory.VERSION_FOLDER);
return setting;
} }
public File getVersionIcon(String id) { public File getVersionIcon(String id) {
@ -195,7 +199,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
@Override @Override
public File getModpackConfiguration(String version) { public File getModpackConfiguration(String version) {
return new File(getRunDirectory(version), "modpack.cfg"); return new File(getVersionRoot(version), "modpack.cfg");
} }
public void markVersionAsModpack(String id) { public void markVersionAsModpack(String id) {

View File

@ -115,6 +115,11 @@ public final class LauncherHelper {
launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks()); launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks());
}); });
} }
@Override
public void onTerminate() {
Controllers.closeDialog();
}
}); });
executor.start(); executor.start();

View File

@ -44,7 +44,6 @@ public final class MultiMCInstallVersionSettingTask extends Task {
@Override @Override
public void execute() { public void execute() {
profile.getRepository().refreshVersions();
VersionSetting vs = Objects.requireNonNull(profile.specializeVersionSetting(version)); VersionSetting vs = Objects.requireNonNull(profile.specializeVersionSetting(version));
ModpackHelper.toVersionSetting(manifest, vs); ModpackHelper.toVersionSetting(manifest, vs);
} }

View File

@ -426,11 +426,9 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
} }
public JFXDialog showDialog(Region content) { public JFXDialog showDialog(Region content) {
if (dialog.getContent() != content) { dialog.setContent(content);
dialog.setContent(content); if (!dialogShown)
if (!dialogShown) dialog.show();
dialog.show();
}
return dialog; return dialog;
} }

View File

@ -18,7 +18,9 @@
package org.jackhuang.hmcl.ui; package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXMasonryPane; import com.jfoenix.controls.JFXMasonryPane;
import com.jfoenix.controls.JFXPopup;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@ -26,6 +28,7 @@ import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main; import org.jackhuang.hmcl.Main;
@ -61,6 +64,9 @@ public final class MainPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("main_page")); private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("main_page"));
private Profile profile;
private String rightClickedVersion;
@FXML @FXML
private JFXButton btnRefresh; private JFXButton btnRefresh;
@ -70,6 +76,11 @@ public final class MainPage extends StackPane implements DecoratorPage {
@FXML @FXML
private JFXMasonryPane masonryPane; private JFXMasonryPane masonryPane;
@FXML
private JFXListView versionList;
private JFXPopup versionPopup;
{ {
FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
@ -77,6 +88,9 @@ public final class MainPage extends StackPane implements DecoratorPage {
EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading); EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading);
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged); EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
versionPopup = new JFXPopup(versionList);
getChildren().remove(versionList);
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install"))); btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install")));
FXUtils.installTooltip(btnAdd, 0, 5000, 0, new Tooltip(Main.i18n("install"))); FXUtils.installTooltip(btnAdd, 0, 5000, 0, new Tooltip(Main.i18n("install")));
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start()); btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start());
@ -136,6 +150,13 @@ public final class MainPage extends StackPane implements DecoratorPage {
} }
} }
}); });
item.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.SECONDARY) {
rightClickedVersion = version;
versionList.getSelectionModel().select(-1);
versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY());
}
});
File iconFile = profile.getRepository().getVersionIcon(version); File iconFile = profile.getRepository().getVersionIcon(version);
if (iconFile.exists()) if (iconFile.exists())
item.setImage(new Image("file:" + iconFile.getAbsolutePath())); item.setImage(new Image("file:" + iconFile.getAbsolutePath()));
@ -155,6 +176,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
} }
private void loadVersions(Profile profile) { private void loadVersions(Profile profile) {
this.profile = profile;
List<Node> children = new LinkedList<>(); List<Node> children = new LinkedList<>();
for (Version version : profile.getRepository().getVersions()) { for (Version version : profile.getRepository().getVersions()) {
children.add(buildNode(profile, version.getId(), Lang.nonNull(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())), "Unknown"))); children.add(buildNode(profile, version.getId(), Lang.nonNull(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())), "Unknown")));
@ -162,6 +184,21 @@ public final class MainPage extends StackPane implements DecoratorPage {
FXUtils.resetChildren(masonryPane, children); FXUtils.resetChildren(masonryPane, children);
} }
public void onVersionManagement() {
versionPopup.hide();
switch (versionList.getSelectionModel().getSelectedIndex()) {
case 0:
VersionPage.renameVersion(profile, rightClickedVersion);
break;
case 1:
VersionPage.deleteVersion(profile, rightClickedVersion);
break;
case 2:
VersionPage.exportVersion(profile, rightClickedVersion);
break;
}
}
public String getTitle() { public String getTitle() {
return title.get(); return title.get();
} }

View File

@ -36,6 +36,7 @@ import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.FileUtils;
import java.io.File; import java.io.File;
import java.lang.reflect.Proxy;
import java.util.Optional; import java.util.Optional;
public final class VersionPage extends StackPane implements DecoratorPage { public final class VersionPage extends StackPane implements DecoratorPage {
@ -112,16 +113,11 @@ public final class VersionPage extends StackPane implements DecoratorPage {
} }
public void onDelete() { public void onDelete() {
Controllers.confirmDialog(Main.i18n("version.manage.remove.confirm", version), Main.i18n("message.confirm"), () -> { deleteVersion(profile, version);
if (profile.getRepository().removeVersionFromDisk(version)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
}
}, null);
} }
public void onExport() { public void onExport() {
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Main.i18n("modpack.wizard")); exportVersion(profile, version);
} }
public void onBrowse() { public void onBrowse() {
@ -157,16 +153,10 @@ public final class VersionPage extends StackPane implements DecoratorPage {
public void onManagement() { public void onManagement() {
switch (managementList.getSelectionModel().getSelectedIndex()) { switch (managementList.getSelectionModel().getSelectedIndex()) {
case 0: // rename a version case 0: // rename a version
Optional<String> res = FXUtils.inputDialog("Input", Main.i18n("version.manage.rename.message"), null, version); renameVersion(profile, version);
if (res.isPresent()) {
if (profile.getRepository().renameVersion(version, res.get())) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
}
}
break; break;
case 1: // remove a version case 1: // remove a version
onDelete(); deleteVersion(profile, version);
break; break;
case 2: // redownload asset index case 2: // redownload asset index
new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version).resolve(profile.getRepository())).start(); new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version).resolve(profile.getRepository())).start();
@ -191,4 +181,26 @@ public final class VersionPage extends StackPane implements DecoratorPage {
public void setTitle(String title) { public void setTitle(String title) {
this.title.set(title); this.title.set(title);
} }
public static void deleteVersion(Profile profile, String version) {
Controllers.confirmDialog(Main.i18n("version.manage.remove.confirm", version), Main.i18n("message.confirm"), () -> {
if (profile.getRepository().removeVersionFromDisk(version)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
}
}, null);
}
public static void renameVersion(Profile profile, String version) {
Controllers.inputDialog(Main.i18n("version.manage.rename.message"), res -> {
if (profile.getRepository().renameVersion(version, res)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
}
});
}
public static void exportVersion(Profile profile, String version) {
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Main.i18n("modpack.wizard"));
}
} }

View File

@ -126,6 +126,8 @@ public final class VersionSettingsController {
this.profile = profile; this.profile = profile;
this.versionId = versionId; this.versionId = versionId;
gameDirItem.setDisable(profile.getRepository().isModpack(versionId));
if (lastVersionSetting != null) { if (lastVersionSetting != null) {
lastVersionSetting.widthProperty().unbind(); lastVersionSetting.widthProperty().unbind();
lastVersionSetting.heightProperty().unbind(); lastVersionSetting.heightProperty().unbind();

View File

@ -92,6 +92,9 @@ public final class TransitionHandler implements AnimationHandler {
} else } else
previousNode = NULL; previousNode = NULL;
if (previousNode == currentNode)
previousNode = NULL;
currentNode = newView; currentNode = newView;
view.getChildren().setAll(previousNode, currentNode); view.getChildren().setAll(previousNode, currentNode);

View File

@ -78,7 +78,7 @@ public final class DownloadWizardProvider implements WizardProvider {
profile.getRepository().markVersionAsModpack(name); profile.getRepository().markVersionAsModpack(name);
Task finalizeTask = Task.of(() -> { Task finalizeTask = Task.of(() -> {
profile.getRepository().refreshVersionsAsync().start(); 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)
@ -87,14 +87,14 @@ public final class DownloadWizardProvider implements WizardProvider {
if (modpack.getManifest() instanceof CurseManifest) if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), selected, ((CurseManifest) modpack.getManifest()), name) return new CurseInstallTask(profile.getDependency(), selected, ((CurseManifest) modpack.getManifest()), name)
.with(finalizeTask); .finalized(finalizeTask);
else if (modpack.getManifest() instanceof HMCLModpackManifest) else if (modpack.getManifest() instanceof HMCLModpackManifest)
return new HMCLModpackInstallTask(profile, selected, modpack, name) return new HMCLModpackInstallTask(profile, selected, modpack, name)
.with(finalizeTask); .finalized(finalizeTask);
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), selected, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) return new MultiMCModpackInstallTask(profile.getDependency(), selected, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
.with(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)) .finalized(finalizeTask)
.with(finalizeTask); .with(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack); else throw new IllegalStateException("Unrecognized modpack: " + modpack);
} }

View File

@ -77,7 +77,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
chooser.setTitle(Main.i18n("modpack.choose")); chooser.setTitle(Main.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage()); File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile == null) Platform.runLater(controller::onFinish); if (selectedFile == null) Platform.runLater(() -> Controllers.navigate(null));
else { else {
// TODO: original HMCL modpack support. // TODO: original HMCL modpack support.
controller.getSettings().put(MODPACK_FILE, selectedFile); controller.getSettings().put(MODPACK_FILE, selectedFile);

View File

@ -5,6 +5,8 @@
<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXListView?>
<?import javafx.scene.control.Label?>
<fx:root <fx:root
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
type="StackPane" pickOnBounds="false" type="StackPane" pickOnBounds="false"
@ -28,4 +30,11 @@
</JFXButton> </JFXButton>
</VBox> </VBox>
<JFXListView fx:id="versionList" styleClass="option-list-view" onMouseClicked="#onVersionManagement"
maxWidth="150.0" minWidth="150.0">
<Label text="%version.manage.rename"/>
<Label text="%version.manage.remove"/>
<Label text="%modpack.export"/>
</JFXListView>
</fx:root> </fx:root>

View File

@ -18,7 +18,7 @@
<Label fx:id="content" wrapText="true" /> <Label fx:id="content" wrapText="true" />
</body> </body>
<actions> <actions>
<HBox fx:id="actions"> <HBox fx:id="actions" alignment="CENTER_RIGHT">
<JFXButton fx:id="acceptButton" styleClass="dialog-accept" text="%button.ok" /> <JFXButton fx:id="acceptButton" styleClass="dialog-accept" text="%button.ok" />
<JFXButton fx:id="cancelButton" visible="false" styleClass="dialog-cancel" text="%button.cancel" /> <JFXButton fx:id="cancelButton" visible="false" styleClass="dialog-cancel" text="%button.cancel" />
</HBox> </HBox>

View File

@ -311,7 +311,7 @@ public class DefaultGameRepository implements GameRepository {
} }
public File getModpackConfiguration(String version) { public File getModpackConfiguration(String version) {
return new File(getRunDirectory(version), "modpack.json"); return new File(getVersionRoot(version), "modpack.json");
} }
public boolean isModpack(String version) { public boolean isModpack(String version) {

View File

@ -34,6 +34,7 @@ import java.util.function.Function;
final class CoupleTask<P extends Task> extends Task { final class CoupleTask<P extends Task> extends Task {
private final boolean relyingOnDependents; private final boolean relyingOnDependents;
private final boolean failIfDependentsFail;
private final Collection<Task> dependents; private final Collection<Task> dependents;
private final List<Task> dependencies = new LinkedList<>(); private final List<Task> dependencies = new LinkedList<>();
private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ; private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ;
@ -45,10 +46,11 @@ 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 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. * @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) { public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents, boolean failIfDependentsFail) {
this.dependents = Collections.singleton(pred); this.dependents = Collections.singleton(pred);
this.succ = succ; this.succ = succ;
this.relyingOnDependents = relyingOnDependents; this.relyingOnDependents = relyingOnDependents;
this.failIfDependentsFail = failIfDependentsFail;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }
@ -58,6 +60,9 @@ final class CoupleTask<P extends Task> extends Task {
Task task = succ.apply(getVariables()); Task task = succ.apply(getVariables());
if (task != null) if (task != null)
dependencies.add(task); dependencies.add(task);
if (failIfDependentsFail && !isDependentsSucceeded())
throw new SilentException();
} }
@Override @Override

View File

@ -69,6 +69,16 @@ public abstract class Task {
return Schedulers.defaultScheduler(); return Schedulers.defaultScheduler();
} }
private boolean dependentsSucceeded = false;
public boolean isDependentsSucceeded() {
return dependentsSucceeded;
}
void setDependentsSucceeded() {
dependentsSucceeded = true;
}
/** /**
* True if requires all {@link #getDependents} finishing successfully. * True if requires all {@link #getDependents} finishing successfully.
* <p> * <p>
@ -239,7 +249,7 @@ public abstract class Task {
} }
public final Task then(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) { public final Task then(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(this, b, true); return new CoupleTask<>(this, b, true, false);
} }
public final Task with(Task b) { public final Task with(Task b) {
@ -247,7 +257,15 @@ public abstract class Task {
} }
public final Task with(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) { public final Task with(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(this, b, false); return new CoupleTask<>(this, b, false, false);
}
public final Task finalized(Task b) {
return finalized(s -> b);
}
public final Task finalized(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(this, b, false, true);
} }
public static Task empty() { public static Task empty() {

View File

@ -159,6 +159,9 @@ public final class TaskExecutor {
if (!doDependentsSucceeded && task.isRelyingOnDependents() || canceled) if (!doDependentsSucceeded && task.isRelyingOnDependents() || canceled)
throw new SilentException(); throw new SilentException();
if (doDependentsSucceeded)
task.setDependentsSucceeded();
task.setVariables(variables); task.setVariables(variables);
task.execute(); task.execute();