@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.jackhuang.hmcl.ui.construct;
|
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXCheckBox;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.control.CheckBox;
|
|
||||||
import javafx.scene.control.TreeTableCell;
|
|
||||||
import javafx.util.Callback;
|
|
||||||
import javafx.util.StringConverter;
|
|
||||||
|
|
||||||
public class JFXCheckBoxTreeTableCell<S, T> extends TreeTableCell<S, T> {
|
|
||||||
|
|
||||||
private final CheckBox checkBox;
|
|
||||||
private boolean showLabel;
|
|
||||||
private ObservableValue<Boolean> booleanProperty;
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell() {
|
|
||||||
this(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell(
|
|
||||||
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty) {
|
|
||||||
this(getSelectedProperty, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JFXCheckBoxTreeTableCell(
|
|
||||||
final Callback<Integer, ObservableValue<Boolean>> getSelectedProperty,
|
|
||||||
final StringConverter<T> converter) {
|
|
||||||
this.getStyleClass().add("check-box-tree-table-cell");
|
|
||||||
this.checkBox = new JFXCheckBox();
|
|
||||||
setGraphic(null);
|
|
||||||
setSelectedStateCallback(getSelectedProperty);
|
|
||||||
setConverter(converter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<StringConverter<T>>(this, "converter") {
|
|
||||||
protected void invalidated() {
|
|
||||||
updateShowLabel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public final ObjectProperty<StringConverter<T>> converterProperty() {
|
|
||||||
return converter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setConverter(StringConverter<T> value) {
|
|
||||||
converterProperty().set(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final StringConverter<T> getConverter() {
|
|
||||||
return converterProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectProperty<Callback<Integer, ObservableValue<Boolean>>>
|
|
||||||
selectedStateCallback =
|
|
||||||
new SimpleObjectProperty<Callback<Integer, ObservableValue<Boolean>>>(
|
|
||||||
this, "selectedStateCallback");
|
|
||||||
|
|
||||||
public final ObjectProperty<Callback<Integer, ObservableValue<Boolean>>> selectedStateCallbackProperty() {
|
|
||||||
return selectedStateCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setSelectedStateCallback(Callback<Integer, ObservableValue<Boolean>> value) {
|
|
||||||
selectedStateCallbackProperty().set(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Callback<Integer, ObservableValue<Boolean>> getSelectedStateCallback() {
|
|
||||||
return selectedStateCallbackProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public void updateItem(T item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
setText(null);
|
|
||||||
setGraphic(null);
|
|
||||||
} else {
|
|
||||||
StringConverter<T> c = getConverter();
|
|
||||||
|
|
||||||
if (showLabel) {
|
|
||||||
setText(c.toString(item));
|
|
||||||
}
|
|
||||||
setGraphic(checkBox);
|
|
||||||
|
|
||||||
if (booleanProperty instanceof BooleanProperty) {
|
|
||||||
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) booleanProperty);
|
|
||||||
}
|
|
||||||
ObservableValue<?> obsValue = getSelectedProperty();
|
|
||||||
if (obsValue instanceof BooleanProperty) {
|
|
||||||
booleanProperty = (ObservableValue<Boolean>) obsValue;
|
|
||||||
checkBox.selectedProperty().bindBidirectional((BooleanProperty) booleanProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkBox.disableProperty().bind(Bindings.not(
|
|
||||||
getTreeTableView().editableProperty().and(
|
|
||||||
getTableColumn().editableProperty()).and(
|
|
||||||
editableProperty())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateShowLabel() {
|
|
||||||
this.showLabel = converter != null;
|
|
||||||
this.checkBox.setAlignment(showLabel ? Pos.CENTER_LEFT : Pos.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableValue<?> getSelectedProperty() {
|
|
||||||
return getSelectedStateCallback() != null ?
|
|
||||||
getSelectedStateCallback().call(getIndex()) :
|
|
||||||
getTableColumn().getCellObservableValue(getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -179,7 +179,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void checkUpdates() {
|
public void checkUpdates() {
|
||||||
Controllers.taskDialog(Task
|
Runnable action = () -> Controllers.taskDialog(Task
|
||||||
.composeAsync(() -> {
|
.composeAsync(() -> {
|
||||||
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
||||||
if (gameVersion.isPresent()) {
|
if (gameVersion.isPresent()) {
|
||||||
@@ -193,11 +193,20 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
} else if (result.isEmpty()) {
|
} else if (result.isEmpty()) {
|
||||||
Controllers.dialog(i18n("mods.check_updates.empty"));
|
Controllers.dialog(i18n("mods.check_updates.empty"));
|
||||||
} else {
|
} else {
|
||||||
Controllers.navigate(new ModUpdatesPage(modManager, result, profile.getRepository().isModpack(versionId)));
|
Controllers.navigate(new ModUpdatesPage(modManager, result));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.withStagesHint(Collections.singletonList("mods.check_updates")),
|
.withStagesHint(Collections.singletonList("mods.check_updates")),
|
||||||
i18n("update.checking"), TaskCancellationAction.NORMAL);
|
i18n("update.checking"), TaskCancellationAction.NORMAL);
|
||||||
|
|
||||||
|
if (profile.getRepository().isModpack(versionId)) {
|
||||||
|
Controllers.confirm(
|
||||||
|
i18n("mods.update_modpack_mod.warning"), null,
|
||||||
|
MessageDialogPane.MessageType.WARNING,
|
||||||
|
action, null);
|
||||||
|
} else {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void download() {
|
public void download() {
|
||||||
|
|||||||
@@ -17,20 +17,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import com.jfoenix.controls.*;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
import com.jfoenix.controls.JFXListView;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.paint.Paint;
|
|
||||||
import org.apache.commons.lang3.mutable.MutableObject;
|
import org.apache.commons.lang3.mutable.MutableObject;
|
||||||
import org.jackhuang.hmcl.mod.LocalModFile;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.ModManager;
|
import org.jackhuang.hmcl.mod.ModManager;
|
||||||
@@ -41,8 +40,10 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
|
|||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.construct.MDListCell;
|
||||||
import org.jackhuang.hmcl.ui.construct.*;
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
import org.jackhuang.hmcl.util.Pair;
|
import org.jackhuang.hmcl.util.Pair;
|
||||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||||
@@ -67,38 +68,36 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
private final ObservableList<ModUpdateObject> objects;
|
private final ObservableList<ModUpdateObject> objects;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> updates, Boolean isModpack) {
|
public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> updates) {
|
||||||
this.modManager = modManager;
|
this.modManager = modManager;
|
||||||
|
|
||||||
getStyleClass().add("gray-background");
|
getStyleClass().add("gray-background");
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, Boolean> enabledColumn = new JFXTreeTableColumn<>();
|
TableColumn<ModUpdateObject, Boolean> enabledColumn = new TableColumn<>();
|
||||||
enabledColumn.setCellFactory(column -> new JFXCheckBoxTreeTableCell<>());
|
enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn));
|
||||||
setupCellValueFactory(enabledColumn, ModUpdateObject::enabledProperty);
|
setupCellValueFactory(enabledColumn, ModUpdateObject::enabledProperty);
|
||||||
enabledColumn.setEditable(true);
|
enabledColumn.setEditable(true);
|
||||||
enabledColumn.setMaxWidth(40);
|
enabledColumn.setMaxWidth(40);
|
||||||
enabledColumn.setMinWidth(40);
|
enabledColumn.setMinWidth(40);
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> fileNameColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.file"));
|
TableColumn<ModUpdateObject, String> fileNameColumn = new TableColumn<>(i18n("mods.check_updates.file"));
|
||||||
|
fileNameColumn.setPrefWidth(200);
|
||||||
setupCellValueFactory(fileNameColumn, ModUpdateObject::fileNameProperty);
|
setupCellValueFactory(fileNameColumn, ModUpdateObject::fileNameProperty);
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> currentVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.current_version"));
|
TableColumn<ModUpdateObject, String> currentVersionColumn = new TableColumn<>(i18n("mods.check_updates.current_version"));
|
||||||
|
currentVersionColumn.setPrefWidth(200);
|
||||||
setupCellValueFactory(currentVersionColumn, ModUpdateObject::currentVersionProperty);
|
setupCellValueFactory(currentVersionColumn, ModUpdateObject::currentVersionProperty);
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> targetVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.target_version"));
|
TableColumn<ModUpdateObject, String> targetVersionColumn = new TableColumn<>(i18n("mods.check_updates.target_version"));
|
||||||
|
targetVersionColumn.setPrefWidth(200);
|
||||||
setupCellValueFactory(targetVersionColumn, ModUpdateObject::targetVersionProperty);
|
setupCellValueFactory(targetVersionColumn, ModUpdateObject::targetVersionProperty);
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> sourceColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.source"));
|
TableColumn<ModUpdateObject, String> sourceColumn = new TableColumn<>(i18n("mods.check_updates.source"));
|
||||||
setupCellValueFactory(sourceColumn, ModUpdateObject::sourceProperty);
|
setupCellValueFactory(sourceColumn, ModUpdateObject::sourceProperty);
|
||||||
|
|
||||||
objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
|
objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
|
||||||
|
|
||||||
RecursiveTreeItem<ModUpdateObject> root = new RecursiveTreeItem<>(
|
TableView<ModUpdateObject> table = new TableView<>(objects);
|
||||||
objects,
|
|
||||||
RecursiveTreeObject::getChildren);
|
|
||||||
|
|
||||||
JFXTreeTableView<ModUpdateObject> table = new JFXTreeTableView<>(root);
|
|
||||||
table.setShowRoot(false);
|
|
||||||
table.setEditable(true);
|
table.setEditable(true);
|
||||||
table.getColumns().setAll(enabledColumn, fileNameColumn, currentVersionColumn, targetVersionColumn, sourceColumn);
|
table.getColumns().setAll(enabledColumn, fileNameColumn, currentVersionColumn, targetVersionColumn, sourceColumn);
|
||||||
|
|
||||||
@@ -111,9 +110,6 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
JFXButton nextButton = new JFXButton(i18n("mods.check_updates.update"));
|
JFXButton nextButton = new JFXButton(i18n("mods.check_updates.update"));
|
||||||
nextButton.getStyleClass().add("jfx-button-raised");
|
nextButton.getStyleClass().add("jfx-button-raised");
|
||||||
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||||
nextButton.setOnAction(e -> {
|
|
||||||
if (isModpack) updateModpackModWarningDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
|
JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
|
||||||
cancelButton.getStyleClass().add("jfx-button-raised");
|
cancelButton.getStyleClass().add("jfx-button-raised");
|
||||||
@@ -125,38 +121,8 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
setBottom(actions);
|
setBottom(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void setupCellValueFactory(JFXTreeTableColumn<ModUpdateObject, T> column, Function<ModUpdateObject, ObservableValue<T>> mapper) {
|
private <T> void setupCellValueFactory(TableColumn<ModUpdateObject, T> column, Function<ModUpdateObject, ObservableValue<T>> mapper) {
|
||||||
column.setCellValueFactory(param -> {
|
column.setCellValueFactory(param -> mapper.apply(param.getValue()));
|
||||||
if (column.validateValue(param))
|
|
||||||
return mapper.apply(param.getValue().getValue());
|
|
||||||
else
|
|
||||||
return column.getComputedValue(param);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateModpackModWarningDialog() {
|
|
||||||
JFXDialogLayout warningPane = new JFXDialogLayout();
|
|
||||||
warningPane.setHeading(new Label(i18n("message.warning"), SVG.alert(new ObjectBinding<Paint>() {
|
|
||||||
@Override
|
|
||||||
protected Paint computeValue() {
|
|
||||||
return Color.ORANGERED;
|
|
||||||
}
|
|
||||||
}, -1, -1)));
|
|
||||||
warningPane.setBody(new Label(i18n("mods.update_modpack_mod.warning")));
|
|
||||||
JFXButton yesButton = new JFXButton(i18n("button.yes"));
|
|
||||||
yesButton.getStyleClass().add("dialog-accept");
|
|
||||||
yesButton.setOnAction(event -> {
|
|
||||||
warningPane.fireEvent(new DialogCloseEvent());
|
|
||||||
updateMods();
|
|
||||||
});
|
|
||||||
JFXButton noButton = new JFXButton(i18n("button.cancel"));
|
|
||||||
noButton.getStyleClass().add("dialog-cancel");
|
|
||||||
noButton.setOnAction(event -> {
|
|
||||||
// Do nothing.
|
|
||||||
warningPane.fireEvent(new DialogCloseEvent());
|
|
||||||
});
|
|
||||||
warningPane.setActions(yesButton, noButton);
|
|
||||||
Controllers.dialog(warningPane);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMods() {
|
private void updateMods() {
|
||||||
@@ -189,7 +155,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ModUpdateCell extends MDListCell<LocalModFile.ModUpdate> {
|
public static final class ModUpdateCell extends MDListCell<LocalModFile.ModUpdate> {
|
||||||
TwoLineListItem content = new TwoLineListItem();
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
|
|
||||||
public ModUpdateCell(JFXListView<LocalModFile.ModUpdate> listView, MutableObject<Object> lastCell) {
|
public ModUpdateCell(JFXListView<LocalModFile.ModUpdate> listView, MutableObject<Object> lastCell) {
|
||||||
@@ -214,7 +180,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ModUpdateObject extends RecursiveTreeObject<ModUpdateObject> {
|
private static final class ModUpdateObject {
|
||||||
final LocalModFile.ModUpdate data;
|
final LocalModFile.ModUpdate data;
|
||||||
final BooleanProperty enabled = new SimpleBooleanProperty();
|
final BooleanProperty enabled = new SimpleBooleanProperty();
|
||||||
final StringProperty fileName = new SimpleStringProperty();
|
final StringProperty fileName = new SimpleStringProperty();
|
||||||
|
|||||||
@@ -705,7 +705,7 @@ mods.name=名稱
|
|||||||
mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。
|
mods.not_modded=你需要先在自動安裝頁面安裝 Fabric、Forge 或 LiteLoader 才能進行模組管理。
|
||||||
mods.restore=回退
|
mods.restore=回退
|
||||||
mods.url=官方頁面
|
mods.url=官方頁面
|
||||||
mods.update_modpack_mod.warning=更新整合包中的Mod可能導致整合包損壞,使整合包無法正常啟動。該操作不可逆,確定要更新嗎?
|
mods.update_modpack_mod.warning=更新模組包中的 Mod 可能導致綜合包損壞,使模組包無法正常啟動。該操作不可逆,確定要更新嗎?
|
||||||
|
|
||||||
multiplayer=多人聯機
|
multiplayer=多人聯機
|
||||||
multiplayer.agreement.prompt=多人聯機功能由 速聚 提供。使用前,你需要先同意多人聯機服務提供方 速聚 的用戶協議與免責聲明。\n你需要了解,HMCL 僅為 速聚 提供多人聯機服務入口,使用中遇到的任何問題由 速聚 負責處理。您在使用多人聯機服務過程中所遇到的任何問題與糾紛(包括其付費業務)均與 HMCL 無關,應與 速聚 協商解決。
|
multiplayer.agreement.prompt=多人聯機功能由 速聚 提供。使用前,你需要先同意多人聯機服務提供方 速聚 的用戶協議與免責聲明。\n你需要了解,HMCL 僅為 速聚 提供多人聯機服務入口,使用中遇到的任何問題由 速聚 負責處理。您在使用多人聯機服務過程中所遇到的任何問題與糾紛(包括其付費業務)均與 HMCL 無關,應與 速聚 協商解決。
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ mods.name=名称
|
|||||||
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理!
|
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理!
|
||||||
mods.restore=回退
|
mods.restore=回退
|
||||||
mods.url=官方页面
|
mods.url=官方页面
|
||||||
mods.update_modpack_mod.warning=更新整合包中的Mod可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
|
mods.update_modpack_mod.warning=更新整合包中的 Mod 可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
|
||||||
|
|
||||||
multiplayer=多人联机
|
multiplayer=多人联机
|
||||||
multiplayer.agreement.prompt=多人联机功能由 速聚 提供。使用前,你需要先同意多人联机服务提供方 速聚 的用户协议与免责声明。\n你需要了解,HMCL 仅为 速聚 提供多人联机服务入口,使用中遇到的任何问题由 速聚 负责处理。\n您在使用多人联机服务过程中所遇到的任何问题与纠纷(包括其付费业务)均与 HMCL 无关,请与 速聚 协商解决。
|
multiplayer.agreement.prompt=多人联机功能由 速聚 提供。使用前,你需要先同意多人联机服务提供方 速聚 的用户协议与免责声明。\n你需要了解,HMCL 仅为 速聚 提供多人联机服务入口,使用中遇到的任何问题由 速聚 负责处理。\n您在使用多人联机服务过程中所遇到的任何问题与纠纷(包括其付费业务)均与 HMCL 无关,请与 速聚 协商解决。
|
||||||
|
|||||||
Reference in New Issue
Block a user