alt: change UI of game installation wizard. Also UI performance improved.
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 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.ui.construct;
|
||||||
|
|
||||||
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
|
||||||
|
public abstract class FloatListCell<T> extends ListCell<T> {
|
||||||
|
protected final StackPane pane = new StackPane();
|
||||||
|
|
||||||
|
{
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
|
||||||
|
pane.setStyle("-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND");
|
||||||
|
setPadding(new Insets(5));
|
||||||
|
JFXDepthManager.setDepth(pane, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateItem(T item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty) {
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
updateControl(item);
|
||||||
|
setGraphic(pane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void updateControl(T dataItem);
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ public class DecoratorWizardDisplayer extends StackPane implements TaskExecutorD
|
|||||||
wizardController.setProvider(provider);
|
wizardController.setProvider(provider);
|
||||||
wizardController.onStart();
|
wizardController.onStart();
|
||||||
|
|
||||||
getStyleClass().add("white-background");
|
// getStyleClass().add("white-background");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -37,60 +37,20 @@ import java.util.Optional;
|
|||||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
class AdditionalInstallersPage extends StackPane implements WizardPage {
|
class AdditionalInstallersPage extends InstallersPage {
|
||||||
private final InstallerWizardProvider provider;
|
protected final InstallerWizardProvider provider;
|
||||||
private final WizardController controller;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private VBox list;
|
|
||||||
@FXML
|
|
||||||
private JFXButton btnFabric;
|
|
||||||
@FXML
|
|
||||||
private JFXButton btnForge;
|
|
||||||
@FXML
|
|
||||||
private JFXButton btnLiteLoader;
|
|
||||||
@FXML
|
|
||||||
private JFXButton btnOptiFine;
|
|
||||||
@FXML
|
|
||||||
private Label lblGameVersion;
|
|
||||||
@FXML
|
|
||||||
private Label lblVersionName;
|
|
||||||
@FXML
|
|
||||||
private Label lblFabric;
|
|
||||||
@FXML
|
|
||||||
private Label lblForge;
|
|
||||||
@FXML
|
|
||||||
private Label lblLiteLoader;
|
|
||||||
@FXML
|
|
||||||
private Label lblOptiFine;
|
|
||||||
@FXML
|
|
||||||
private JFXButton btnInstall;
|
|
||||||
|
|
||||||
public AdditionalInstallersPage(InstallerWizardProvider provider, WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
|
public AdditionalInstallersPage(InstallerWizardProvider provider, WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
|
||||||
|
super(controller, repository, provider.getGameVersion(), downloadProvider);
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.controller = controller;
|
|
||||||
|
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/download/additional-installers.fxml");
|
txtName.getValidators().clear();
|
||||||
|
txtName.setText(provider.getVersion().getId());
|
||||||
lblGameVersion.setText(provider.getGameVersion());
|
txtName.setEditable(false);
|
||||||
lblVersionName.setText(provider.getVersion().getId());
|
|
||||||
|
|
||||||
JFXButton[] buttons = new JFXButton[]{btnFabric, btnForge, btnLiteLoader, btnOptiFine};
|
|
||||||
String[] libraryIds = new String[]{"fabric", "forge", "liteloader", "optifine"};
|
|
||||||
|
|
||||||
for (int i = 0; i < libraryIds.length; ++i) {
|
|
||||||
String libraryId = libraryIds[i];
|
|
||||||
buttons[i].setOnMouseClicked(e -> {
|
|
||||||
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), provider.getGameVersion(), downloadProvider, libraryId, () -> {
|
|
||||||
controller.onPrev(false);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btnInstall.setOnMouseClicked(e -> onInstall());
|
@Override
|
||||||
}
|
protected void onInstall() {
|
||||||
|
|
||||||
private void onInstall() {
|
|
||||||
controller.onFinish();
|
controller.onFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,17 +65,16 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> settings) {
|
public void onNavigate(Map<String, Object> settings) {
|
||||||
lblGameVersion.setText(i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
|
|
||||||
|
|
||||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(provider.getVersion().resolvePreservingPatches(provider.getProfile().getRepository()));
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(provider.getVersion().resolvePreservingPatches(provider.getProfile().getRepository()));
|
||||||
|
String game = analyzer.getVersion(MINECRAFT).orElse(null);
|
||||||
String fabric = analyzer.getVersion(FABRIC).orElse(null);
|
String fabric = analyzer.getVersion(FABRIC).orElse(null);
|
||||||
String forge = analyzer.getVersion(FORGE).orElse(null);
|
String forge = analyzer.getVersion(FORGE).orElse(null);
|
||||||
String liteLoader = analyzer.getVersion(LITELOADER).orElse(null);
|
String liteLoader = analyzer.getVersion(LITELOADER).orElse(null);
|
||||||
String optiFine = analyzer.getVersion(OPTIFINE).orElse(null);
|
String optiFine = analyzer.getVersion(OPTIFINE).orElse(null);
|
||||||
|
|
||||||
Label[] labels = new Label[]{lblFabric, lblForge, lblLiteLoader, lblOptiFine};
|
Label[] labels = new Label[]{lblGame, lblFabric, lblForge, lblLiteLoader, lblOptiFine};
|
||||||
String[] libraryIds = new String[]{"fabric", "forge", "liteloader", "optifine"};
|
String[] libraryIds = new String[]{"game", "fabric", "forge", "liteloader", "optifine"};
|
||||||
String[] versions = new String[]{fabric, forge, liteLoader, optiFine};
|
String[] versions = new String[]{game, fabric, forge, liteLoader, optiFine};
|
||||||
|
|
||||||
for (int i = 0; i < libraryIds.length; ++i) {
|
for (int i = 0; i < libraryIds.length; ++i) {
|
||||||
String libraryId = libraryIds[i];
|
String libraryId = libraryIds[i];
|
||||||
|
|||||||
@@ -19,8 +19,12 @@ package org.jackhuang.hmcl.ui.download;
|
|||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import com.jfoenix.effects.JFXDepthManager;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||||
@@ -40,50 +44,52 @@ import java.util.Map;
|
|||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class InstallersPage extends StackPane implements WizardPage {
|
public class InstallersPage extends StackPane implements WizardPage {
|
||||||
private final WizardController controller;
|
protected final WizardController controller;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private VBox list;
|
protected VBox list;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnFabric;
|
protected Node btnGame;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnForge;
|
protected Node btnFabric;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnLiteLoader;
|
protected Node btnForge;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnOptiFine;
|
protected Node btnLiteLoader;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblGameVersion;
|
protected Node btnOptiFine;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblFabric;
|
protected Label lblGame;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblForge;
|
protected Label lblFabric;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblLiteLoader;
|
protected Label lblForge;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblOptiFine;
|
protected Label lblLiteLoader;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXTextField txtName;
|
protected Label lblOptiFine;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXButton btnInstall;
|
protected JFXTextField txtName;
|
||||||
|
|
||||||
public InstallersPage(WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
|
@FXML
|
||||||
|
protected JFXButton btnInstall;
|
||||||
|
|
||||||
|
public InstallersPage(WizardController controller, GameRepository repository, String gameVersion, DownloadProvider downloadProvider) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
|
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
|
||||||
|
|
||||||
String gameVersion = ((RemoteVersion) controller.getSettings().get("game")).getGameVersion();
|
|
||||||
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
|
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
|
||||||
hasVersion.setMessage(i18n("install.new_game.already_exists"));
|
hasVersion.setMessage(i18n("install.new_game.already_exists"));
|
||||||
Validator nameValidator = new Validator(OperatingSystem::isNameValid);
|
Validator nameValidator = new Validator(OperatingSystem::isNameValid);
|
||||||
@@ -92,11 +98,18 @@ public class InstallersPage extends StackPane implements WizardPage {
|
|||||||
txtName.textProperty().addListener(e -> btnInstall.setDisable(!txtName.validate()));
|
txtName.textProperty().addListener(e -> btnInstall.setDisable(!txtName.validate()));
|
||||||
txtName.setText(gameVersion);
|
txtName.setText(gameVersion);
|
||||||
|
|
||||||
JFXButton[] buttons = new JFXButton[]{btnFabric, btnForge, btnLiteLoader, btnOptiFine};
|
Label[] labels = new Label[]{lblGame, lblFabric, lblForge, lblLiteLoader, lblOptiFine};
|
||||||
String[] libraryIds = new String[]{"fabric", "forge", "liteloader", "optifine"};
|
Node[] buttons = new Node[]{btnGame, btnFabric, btnForge, btnLiteLoader, btnOptiFine};
|
||||||
|
String[] libraryIds = new String[]{"game", "fabric", "forge", "liteloader", "optifine"};
|
||||||
|
|
||||||
|
for (Node node : list.getChildren()) {
|
||||||
|
JFXDepthManager.setDepth(node, 1);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < libraryIds.length; ++i) {
|
for (int i = 0; i < libraryIds.length; ++i) {
|
||||||
String libraryId = libraryIds[i];
|
String libraryId = libraryIds[i];
|
||||||
|
BorderPane.setMargin(labels[i], new Insets(0, 0, 0, 8));
|
||||||
|
if (libraryId.equals("game")) continue;
|
||||||
buttons[i].setOnMouseClicked(e ->
|
buttons[i].setOnMouseClicked(e ->
|
||||||
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false))));
|
controller.onNext(new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, downloadProvider, libraryId, () -> controller.onPrev(false))));
|
||||||
}
|
}
|
||||||
@@ -113,10 +126,8 @@ public class InstallersPage extends StackPane implements WizardPage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNavigate(Map<String, Object> settings) {
|
public void onNavigate(Map<String, Object> settings) {
|
||||||
lblGameVersion.setText(i18n("install.new_game.current_game_version") + ": " + getVersion("game"));
|
Label[] labels = new Label[]{lblGame, lblFabric, lblForge, lblLiteLoader, lblOptiFine};
|
||||||
|
String[] libraryIds = new String[]{"game", "fabric", "forge", "liteloader", "optifine"};
|
||||||
Label[] labels = new Label[]{lblFabric, lblForge, lblLiteLoader, lblOptiFine};
|
|
||||||
String[] libraryIds = new String[]{"fabric", "forge", "liteloader", "optifine"};
|
|
||||||
|
|
||||||
for (int i = 0; i < libraryIds.length; ++i) {
|
for (int i = 0; i < libraryIds.length; ++i) {
|
||||||
String libraryId = libraryIds[i];
|
String libraryId = libraryIds[i];
|
||||||
@@ -132,7 +143,7 @@ public class InstallersPage extends StackPane implements WizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void onInstall() {
|
protected void onInstall() {
|
||||||
controller.getSettings().put("name", txtName.getText());
|
controller.getSettings().put("name", txtName.getText());
|
||||||
controller.onFinish();
|
controller.onFinish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
|
|||||||
DownloadProvider provider = profile.getDependency().getPrimaryDownloadProvider();
|
DownloadProvider provider = profile.getDependency().getPrimaryDownloadProvider();
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 0:
|
case 0:
|
||||||
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), provider)));
|
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer.game")), "", provider, "game", () -> controller.onNext(new InstallersPage(controller, profile.getRepository(), ((RemoteVersion) controller.getSettings().get("game")).getGameVersion(), provider)));
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages());
|
throw new IllegalStateException("error step " + step + ", settings: " + settings + ", pages: " + controller.getPages());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,16 +23,28 @@ import com.jfoenix.controls.JFXSpinner;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||||
|
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||||
import org.jackhuang.hmcl.download.VersionList;
|
import org.jackhuang.hmcl.download.VersionList;
|
||||||
|
import org.jackhuang.hmcl.download.fabric.FabricRemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||||
|
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||||
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;
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.FloatListCell;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
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;
|
||||||
@@ -43,6 +55,7 @@ import java.util.logging.Level;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class VersionsPage extends BorderPane implements WizardPage, Refreshable {
|
public final class VersionsPage extends BorderPane implements WizardPage, Refreshable {
|
||||||
private final String gameVersion;
|
private final String gameVersion;
|
||||||
@@ -51,7 +64,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
private final WizardController controller;
|
private final WizardController controller;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private JFXListView<VersionsPageItem> list;
|
private JFXListView<RemoteVersion> list;
|
||||||
@FXML
|
@FXML
|
||||||
private JFXSpinner spinner;
|
private JFXSpinner spinner;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -96,16 +109,65 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
chkSnapshot.selectedProperty().addListener(listener);
|
chkSnapshot.selectedProperty().addListener(listener);
|
||||||
chkOld.selectedProperty().addListener(listener);
|
chkOld.selectedProperty().addListener(listener);
|
||||||
|
|
||||||
|
list.setCellFactory(listView -> new FloatListCell<RemoteVersion>() {
|
||||||
|
ImageView imageView = new ImageView();
|
||||||
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
|
|
||||||
|
{
|
||||||
|
HBox container = new HBox(12);
|
||||||
|
container.setPadding(new Insets(0, 0, 0, 6));
|
||||||
|
container.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
pane.getChildren().add(container);
|
||||||
|
|
||||||
|
container.getChildren().setAll(imageView, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateControl(RemoteVersion remoteVersion) {
|
||||||
|
content.setTitle(remoteVersion.getSelfVersion());
|
||||||
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
|
||||||
|
if (remoteVersion instanceof GameRemoteVersion) {
|
||||||
|
switch (remoteVersion.getVersionType()) {
|
||||||
|
case RELEASE:
|
||||||
|
content.setSubtitle(i18n("version.game.release"));
|
||||||
|
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
|
||||||
|
break;
|
||||||
|
case SNAPSHOT:
|
||||||
|
content.setSubtitle(i18n("version.game.snapshot"));
|
||||||
|
imageView.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content.setSubtitle(i18n("version.game.old"));
|
||||||
|
imageView.setImage(new Image("/assets/img/craft_table.png", 32, 32, false, true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (remoteVersion instanceof LiteLoaderRemoteVersion) {
|
||||||
|
imageView.setImage(new Image("/assets/img/chicken.png", 32, 32, false, true));
|
||||||
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
} else if (remoteVersion instanceof OptiFineRemoteVersion) {
|
||||||
|
imageView.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
||||||
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
} else if (remoteVersion instanceof ForgeRemoteVersion) {
|
||||||
|
imageView.setImage(new Image("/assets/img/forge.png", 32, 32, false, true));
|
||||||
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
} else if (remoteVersion instanceof FabricRemoteVersion) {
|
||||||
|
imageView.setImage(new Image("/assets/img/fabric.png", 32, 32, false, true));
|
||||||
|
content.setSubtitle(remoteVersion.getGameVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
list.setOnMouseClicked(e -> {
|
list.setOnMouseClicked(e -> {
|
||||||
if (list.getSelectionModel().getSelectedIndex() < 0)
|
if (list.getSelectionModel().getSelectedIndex() < 0)
|
||||||
return;
|
return;
|
||||||
controller.getSettings().put(libraryId, list.getSelectionModel().getSelectedItem().getRemoteVersion());
|
controller.getSettings().put(libraryId, list.getSelectionModel().getSelectedItem());
|
||||||
callback.run();
|
callback.run();
|
||||||
});
|
});
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<VersionsPageItem> loadVersions() {
|
private List<RemoteVersion> loadVersions() {
|
||||||
return versionList.getVersions(gameVersion).stream()
|
return versionList.getVersions(gameVersion).stream()
|
||||||
.filter(it -> {
|
.filter(it -> {
|
||||||
switch (it.getVersionType()) {
|
switch (it.getVersionType()) {
|
||||||
@@ -119,8 +181,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sorted()
|
.sorted().collect(Collectors.toList());
|
||||||
.map(VersionsPageItem::new).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,7 +189,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
||||||
executor = versionList.refreshAsync(gameVersion).whenComplete(exception -> {
|
executor = versionList.refreshAsync(gameVersion).whenComplete(exception -> {
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
List<VersionsPageItem> items = loadVersions();
|
List<RemoteVersion> items = loadVersions();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
if (versionList.getVersions(gameVersion).isEmpty()) {
|
if (versionList.getVersions(gameVersion).isEmpty()) {
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2020 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.ui.download;
|
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
|
||||||
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
|
||||||
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
|
|
||||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
|
||||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
public final class VersionsPageItem extends StackPane {
|
|
||||||
private final RemoteVersion remoteVersion;
|
|
||||||
@FXML
|
|
||||||
private Label lblSelfVersion;
|
|
||||||
@FXML
|
|
||||||
private Label lblGameVersion;
|
|
||||||
@FXML
|
|
||||||
private ImageView imageView;
|
|
||||||
@FXML
|
|
||||||
private HBox leftPane;
|
|
||||||
@FXML
|
|
||||||
private StackPane imageViewContainer;
|
|
||||||
|
|
||||||
public VersionsPageItem(RemoteVersion remoteVersion) {
|
|
||||||
this.remoteVersion = Objects.requireNonNull(remoteVersion);
|
|
||||||
|
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
|
|
||||||
lblSelfVersion.setText(remoteVersion.getSelfVersion());
|
|
||||||
|
|
||||||
if (remoteVersion instanceof GameRemoteVersion) {
|
|
||||||
switch (remoteVersion.getVersionType()) {
|
|
||||||
case RELEASE:
|
|
||||||
lblGameVersion.setText(i18n("version.game.release"));
|
|
||||||
imageView.setImage(new Image("/assets/img/icon.png", 32, 32, false, true));
|
|
||||||
break;
|
|
||||||
case SNAPSHOT:
|
|
||||||
lblGameVersion.setText(i18n("version.game.snapshot"));
|
|
||||||
imageView.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
lblGameVersion.setText(i18n("version.game.old"));
|
|
||||||
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (remoteVersion instanceof LiteLoaderRemoteVersion) {
|
|
||||||
imageView.setImage(new Image("/assets/img/chicken.png", 32, 32, false, true));
|
|
||||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
|
||||||
} else if (remoteVersion instanceof OptiFineRemoteVersion) {
|
|
||||||
// optifine has no icon.
|
|
||||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
|
||||||
} else if (remoteVersion instanceof ForgeRemoteVersion) {
|
|
||||||
imageView.setImage(new Image("/assets/img/forge.png", 32, 32, false, true));
|
|
||||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
leftPane.getChildren().remove(imageViewContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemoteVersion getRemoteVersion() {
|
|
||||||
return remoteVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,7 +53,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
super(skinnable);
|
super(skinnable);
|
||||||
|
|
||||||
StackPane pane = new StackPane();
|
StackPane pane = new StackPane();
|
||||||
pane.getStyleClass().addAll("notice-pane", "white-background");
|
pane.getStyleClass().addAll("notice-pane");
|
||||||
|
|
||||||
BorderPane root = new BorderPane();
|
BorderPane root = new BorderPane();
|
||||||
JFXTreeTableView<ModInfoObject> tableView = new JFXTreeTableView<>();
|
JFXTreeTableView<ModInfoObject> tableView = new JFXTreeTableView<>();
|
||||||
|
|||||||
@@ -137,7 +137,6 @@
|
|||||||
|
|
||||||
.sponsor-pane {
|
.sponsor-pane {
|
||||||
-fx-padding: 16;
|
-fx-padding: 16;
|
||||||
-fx-background-color: white;
|
|
||||||
-fx-cursor: hand;
|
-fx-cursor: hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,6 +521,12 @@
|
|||||||
-fx-cursor: hand;
|
-fx-cursor: hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jfx-button .jfx-rippler {
|
||||||
|
-jfx-rippler-fill: -fx-base-check-color;
|
||||||
|
-jfx-mask-type: CIRCLE;
|
||||||
|
-fx-padding: 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
.jfx-button-raised {
|
.jfx-button-raised {
|
||||||
-fx-background-color: -fx-base-color;
|
-fx-background-color: -fx-base-color;
|
||||||
}
|
}
|
||||||
@@ -614,7 +619,8 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
.jfx-list-cell, .list-cell {
|
.jfx-list-cell, .list-cell {
|
||||||
-fx-background-color: WHITE;
|
/*-fx-background-color: WHITE;*/
|
||||||
|
-fx-background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected, .jfx-list-cell:selected,
|
.list-cell:selected, .jfx-list-cell:selected,
|
||||||
@@ -637,6 +643,17 @@
|
|||||||
-jfx-cell-vertical-margin: 5.0;
|
-jfx-cell-vertical-margin: 5.0;
|
||||||
-jfx-vertical-gap: 10.0;
|
-jfx-vertical-gap: 10.0;
|
||||||
-jfx-expanded: false;
|
-jfx-expanded: false;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jfx-list-view-float {
|
||||||
|
-fx-padding: 5;
|
||||||
|
-fx-background-insets: 0.0;
|
||||||
|
-jfx-cell-horizontal-margin: 0.0;
|
||||||
|
-jfx-cell-vertical-margin: 5.0;
|
||||||
|
-jfx-vertical-gap: 10.0;
|
||||||
|
-jfx-expanded: false;
|
||||||
|
-fx-background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-list {
|
.options-list {
|
||||||
|
|||||||
@@ -3,70 +3,85 @@
|
|||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import com.jfoenix.effects.JFXDepthManager?>
|
||||||
<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" style="-fx-padding: 16;">
|
type="StackPane" style="-fx-padding: 16;">
|
||||||
<BorderPane>
|
<BorderPane>
|
||||||
<top>
|
|
||||||
<VBox alignment="CENTER" style="-fx-padding: 40px;" spacing="20">
|
|
||||||
<Label fx:id="lblGameVersion" alignment="CENTER"/>
|
|
||||||
<JFXTextField fx:id="txtName" labelFloat="true" promptText="%modpack.enter_name" maxWidth="300"/>
|
|
||||||
</VBox>
|
|
||||||
</top>
|
|
||||||
<center>
|
<center>
|
||||||
<VBox fx:id="list" styleClass="jfx-list-view" maxHeight="150" maxWidth="300">
|
<VBox fx:id="list" spacing="8">
|
||||||
<JFXButton fx:id="btnFabric" prefWidth="${list.width}">
|
<HBox spacing="8" alignment="CENTER_LEFT" style="-fx-background-color: white; -fx-padding: 20 8 20 16">
|
||||||
<graphic>
|
<Label text="%archive.name" />
|
||||||
<BorderPane mouseTransparent="true">
|
<JFXTextField fx:id="txtName" maxWidth="300"/>
|
||||||
|
</HBox>
|
||||||
|
<BorderPane fx:id="btnGame" style="-fx-background-color: white; -fx-padding: 8">
|
||||||
<left>
|
<left>
|
||||||
<Label fx:id="lblFabric"/>
|
<ImageView>
|
||||||
|
<Image url="/assets/img/grass.png" requestedHeight="32" requestedWidth="32" smooth="true" />
|
||||||
|
</ImageView>
|
||||||
</left>
|
</left>
|
||||||
|
<center>
|
||||||
|
<Label fx:id="lblGame" BorderPane.alignment="CENTER_LEFT" />
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
<BorderPane fx:id="btnFabric" style="-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND">
|
||||||
|
<left>
|
||||||
|
<ImageView>
|
||||||
|
<Image url="/assets/img/fabric.png" requestedHeight="32" requestedWidth="32" smooth="true" />
|
||||||
|
</ImageView>
|
||||||
|
</left>
|
||||||
|
<center>
|
||||||
|
<Label fx:id="lblFabric" BorderPane.alignment="CENTER_LEFT" />
|
||||||
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<fx:include source="/assets/svg/arrow-right.fxml"/>
|
<fx:include BorderPane.alignment="CENTER_RIGHT" source="/assets/svg/arrow-right.fxml"/>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</graphic>
|
<BorderPane fx:id="btnForge" style="-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND">
|
||||||
</JFXButton>
|
|
||||||
<JFXButton fx:id="btnForge" prefWidth="${list.width}">
|
|
||||||
<graphic>
|
|
||||||
<BorderPane mouseTransparent="true">
|
|
||||||
<left>
|
<left>
|
||||||
<Label fx:id="lblForge"/>
|
<ImageView>
|
||||||
|
<Image url="/assets/img/forge.png" requestedHeight="32" requestedWidth="32" smooth="true" />
|
||||||
|
</ImageView>
|
||||||
</left>
|
</left>
|
||||||
|
<center>
|
||||||
|
<Label fx:id="lblForge" BorderPane.alignment="CENTER_LEFT" />
|
||||||
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<fx:include source="/assets/svg/arrow-right.fxml"/>
|
<fx:include BorderPane.alignment="CENTER_RIGHT" source="/assets/svg/arrow-right.fxml"/>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</graphic>
|
<BorderPane fx:id="btnLiteLoader" style="-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND">
|
||||||
</JFXButton>
|
|
||||||
<JFXButton fx:id="btnLiteLoader" prefWidth="${list.width}">
|
|
||||||
<graphic>
|
|
||||||
<BorderPane mouseTransparent="true">
|
|
||||||
<left>
|
<left>
|
||||||
<Label fx:id="lblLiteLoader"/>
|
<ImageView>
|
||||||
|
<Image url="/assets/img/chicken.png" requestedHeight="32" requestedWidth="32" smooth="true" />
|
||||||
|
</ImageView>
|
||||||
</left>
|
</left>
|
||||||
|
<center>
|
||||||
|
<Label fx:id="lblLiteLoader" BorderPane.alignment="CENTER_LEFT" />
|
||||||
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<fx:include source="/assets/svg/arrow-right.fxml"/>
|
<fx:include BorderPane.alignment="CENTER_RIGHT" source="/assets/svg/arrow-right.fxml"/>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</graphic>
|
<BorderPane fx:id="btnOptiFine" style="-fx-background-color: white; -fx-padding: 8; -fx-cursor: HAND">
|
||||||
</JFXButton>
|
|
||||||
<JFXButton fx:id="btnOptiFine" prefWidth="${list.width}">
|
|
||||||
<graphic>
|
|
||||||
<BorderPane mouseTransparent="true">
|
|
||||||
<left>
|
<left>
|
||||||
<Label fx:id="lblOptiFine"/>
|
<ImageView>
|
||||||
|
<Image url="/assets/img/command.png" requestedHeight="32" requestedWidth="32" smooth="true" />
|
||||||
|
</ImageView>
|
||||||
</left>
|
</left>
|
||||||
|
<center>
|
||||||
|
<Label fx:id="lblOptiFine" BorderPane.alignment="CENTER_LEFT" />
|
||||||
|
</center>
|
||||||
<right>
|
<right>
|
||||||
<fx:include source="/assets/svg/arrow-right.fxml"/>
|
<fx:include BorderPane.alignment="CENTER_RIGHT" source="/assets/svg/arrow-right.fxml"/>
|
||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</graphic>
|
|
||||||
</JFXButton>
|
|
||||||
</VBox>
|
</VBox>
|
||||||
</center>
|
</center>
|
||||||
<bottom>
|
<bottom>
|
||||||
<HBox alignment="CENTER">
|
<HBox alignment="CENTER_RIGHT">
|
||||||
<JFXButton fx:id="btnInstall" onMouseClicked="#onInstall" prefWidth="100" prefHeight="40"
|
<JFXButton fx:id="btnInstall" onMouseClicked="#onInstall" prefWidth="100" prefHeight="40"
|
||||||
buttonType="RAISED" text="%button.install" styleClass="jfx-button-raised"/>
|
buttonType="RAISED" text="%button.install" styleClass="jfx-button-raised"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<JFXCheckBox fx:id="chkSnapshot" text="%version.game.snapshot" />
|
<JFXCheckBox fx:id="chkSnapshot" text="%version.game.snapshot" />
|
||||||
<JFXCheckBox fx:id="chkOld" text="%version.game.old" />
|
<JFXCheckBox fx:id="chkOld" text="%version.game.old" />
|
||||||
</HBox>
|
</HBox>
|
||||||
<JFXListView fx:id="list" styleClass="jfx-list-view" VBox.vgrow="ALWAYS">
|
<JFXListView fx:id="list" styleClass="jfx-list-view-float" VBox.vgrow="ALWAYS">
|
||||||
</JFXListView>
|
</JFXListView>
|
||||||
</VBox>
|
</VBox>
|
||||||
<StackPane fx:id="failedPane" styleClass="notice-pane">
|
<StackPane fx:id="failedPane" styleClass="notice-pane">
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 9.8 KiB |
BIN
HMCL/src/main/resources/assets/img/fabric.png
Normal file
BIN
HMCL/src/main/resources/assets/img/fabric.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -100,7 +100,7 @@ download.failed.empty=No candidates. Click here to return.
|
|||||||
download.failed.refresh=Unable to download version list. Click here to retry.
|
download.failed.refresh=Unable to download version list. Click here to retry.
|
||||||
download.provider.mcbbs=MCBBS (https://www.mcbbs.net/)
|
download.provider.mcbbs=MCBBS (https://www.mcbbs.net/)
|
||||||
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)
|
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)
|
||||||
download.provider.mojang=Mojang (Forge and OptiFine installation are downloaded from BMCLAPI)
|
download.provider.mojang=Mojang (OptiFine download service is provided by BMCLAPI)
|
||||||
|
|
||||||
extension.bat=Windows Bat file
|
extension.bat=Windows Bat file
|
||||||
extension.mod=Mod file
|
extension.mod=Mod file
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ download.failed.empty=沒有能安裝的版本,按一下此處返回。
|
|||||||
download.failed.refresh=載入版本列表失敗,按一下此處重試。
|
download.failed.refresh=載入版本列表失敗,按一下此處重試。
|
||||||
download.provider.mcbbs=我的世界中文論壇 (MCBBS, https://www.mcbbs.net/)
|
download.provider.mcbbs=我的世界中文論壇 (MCBBS, https://www.mcbbs.net/)
|
||||||
download.provider.bmclapi=BMCLAPI (bangbang93,https://bmclapi2.bangbang93.com/)
|
download.provider.bmclapi=BMCLAPI (bangbang93,https://bmclapi2.bangbang93.com/)
|
||||||
download.provider.mojang=官方伺服器 (Forge 和 OptiFine 自動安裝的下載來源是 BMCLAPI)
|
download.provider.mojang=官方伺服器 (OptiFine 自動安裝的下載來源是 BMCLAPI)
|
||||||
|
|
||||||
extension.bat=Windows 指令碼
|
extension.bat=Windows 指令碼
|
||||||
extension.mod=模組檔案
|
extension.mod=模組檔案
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ download.failed.empty=没有可供安装的版本,点击此处返回。
|
|||||||
download.failed.refresh=加载版本列表失败,点击此处重试。
|
download.failed.refresh=加载版本列表失败,点击此处重试。
|
||||||
download.provider.mcbbs=我的世界中文论坛 (MCBBS, https://www.mcbbs.net/)
|
download.provider.mcbbs=我的世界中文论坛 (MCBBS, https://www.mcbbs.net/)
|
||||||
download.provider.bmclapi=BMCLAPI(bangbang93,https://bmclapi2.bangbang93.com/)
|
download.provider.bmclapi=BMCLAPI(bangbang93,https://bmclapi2.bangbang93.com/)
|
||||||
download.provider.mojang=官方(Forge 和 OptiFine 自动安装使用 BMCLAPI 下载源)
|
download.provider.mojang=官方(OptiFine 自动安装使用 BMCLAPI 下载源)
|
||||||
|
|
||||||
extension.bat=Windows 脚本
|
extension.bat=Windows 脚本
|
||||||
extension.mod=模组文件
|
extension.mod=模组文件
|
||||||
|
|||||||
Reference in New Issue
Block a user