feat(mod): WIP: mod update.
This commit is contained in:
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
@@ -25,13 +25,13 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
|
public class ModCheckUpdatesTask extends Task<List<LocalModFile.ModUpdate>> {
|
||||||
|
|
||||||
private final String gameVersion;
|
private final String gameVersion;
|
||||||
private final Collection<LocalMod> mods;
|
private final Collection<LocalModFile> mods;
|
||||||
private final Collection<Task<LocalMod.ModUpdate>> dependents;
|
private final Collection<Task<LocalModFile.ModUpdate>> dependents;
|
||||||
|
|
||||||
public ModUpdateTask(String gameVersion, Collection<LocalMod> mods) {
|
public ModCheckUpdatesTask(String gameVersion, Collection<LocalModFile> mods) {
|
||||||
this.gameVersion = gameVersion;
|
this.gameVersion = gameVersion;
|
||||||
this.mods = mods;
|
this.mods = mods;
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preExecute() throws Exception {
|
public void preExecute() {
|
||||||
notifyPropertiesChanged();
|
notifyPropertiesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
||||||
@@ -87,7 +87,7 @@ public class ModDownloadListPage extends DownloadListPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) {
|
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.ModManager;
|
import org.jackhuang.hmcl.mod.ModManager;
|
||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.Controllers;
|
|||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.ListPageBase;
|
import org.jackhuang.hmcl.ui.ListPageBase;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.PageAware;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ import java.util.stream.Collectors;
|
|||||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable {
|
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable, PageAware {
|
||||||
private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false);
|
private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false);
|
||||||
|
|
||||||
private ModManager modManager;
|
private ModManager modManager;
|
||||||
@@ -149,7 +150,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
modManager.removeMods(selectedItems.stream()
|
modManager.removeMods(selectedItems.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
.toArray(LocalMod[]::new));
|
.toArray(LocalModFile[]::new));
|
||||||
loadMods(modManager);
|
loadMods(modManager);
|
||||||
} catch (IOException ignore) {
|
} catch (IOException ignore) {
|
||||||
// Fail to remove mods if the game is running or the mod is absent.
|
// Fail to remove mods if the game is running or the mod is absent.
|
||||||
@@ -179,7 +180,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
Task.composeAsync(() -> {
|
Task.composeAsync(() -> {
|
||||||
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
||||||
if (gameVersion.isPresent()) {
|
if (gameVersion.isPresent()) {
|
||||||
return new ModUpdateTask(gameVersion.get(), modManager.getMods());
|
return new ModCheckUpdatesTask(gameVersion.get(), modManager.getMods());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@@ -187,7 +188,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
Controllers.dialog("Failed to check updates", "failed", MessageDialogPane.MessageType.ERROR);
|
Controllers.dialog("Failed to check updates", "failed", MessageDialogPane.MessageType.ERROR);
|
||||||
} else {
|
} else {
|
||||||
Controllers.dialog(new ModUpdatesPane(result));
|
Controllers.navigate(new ModUpdatesPage(modManager, result));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.withStagesHint(Collections.singletonList("mods.check_updates"))
|
.withStagesHint(Collections.singletonList("mods.check_updates"))
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import javafx.scene.image.ImageView;
|
|||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.ModManager;
|
import org.jackhuang.hmcl.mod.ModManager;
|
||||||
import org.jackhuang.hmcl.setting.Theme;
|
import org.jackhuang.hmcl.setting.Theme;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
@@ -126,34 +126,34 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
|
|
||||||
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> implements Comparable<ModInfoObject> {
|
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> implements Comparable<ModInfoObject> {
|
||||||
private final BooleanProperty active;
|
private final BooleanProperty active;
|
||||||
private final LocalMod localMod;
|
private final LocalModFile localModFile;
|
||||||
private final String message;
|
private final String message;
|
||||||
private final ModTranslations.Mod mod;
|
private final ModTranslations.Mod mod;
|
||||||
|
|
||||||
ModInfoObject(LocalMod localMod) {
|
ModInfoObject(LocalModFile localModFile) {
|
||||||
this.localMod = localMod;
|
this.localModFile = localModFile;
|
||||||
this.active = localMod.activeProperty();
|
this.active = localModFile.activeProperty();
|
||||||
StringBuilder message = new StringBuilder(localMod.getName());
|
StringBuilder message = new StringBuilder(localModFile.getName());
|
||||||
if (isNotBlank(localMod.getVersion()))
|
if (isNotBlank(localModFile.getVersion()))
|
||||||
message.append(", ").append(i18n("archive.version")).append(": ").append(localMod.getVersion());
|
message.append(", ").append(i18n("archive.version")).append(": ").append(localModFile.getVersion());
|
||||||
if (isNotBlank(localMod.getGameVersion()))
|
if (isNotBlank(localModFile.getGameVersion()))
|
||||||
message.append(", ").append(i18n("archive.game_version")).append(": ").append(localMod.getGameVersion());
|
message.append(", ").append(i18n("archive.game_version")).append(": ").append(localModFile.getGameVersion());
|
||||||
if (isNotBlank(localMod.getAuthors()))
|
if (isNotBlank(localModFile.getAuthors()))
|
||||||
message.append(", ").append(i18n("archive.author")).append(": ").append(localMod.getAuthors());
|
message.append(", ").append(i18n("archive.author")).append(": ").append(localModFile.getAuthors());
|
||||||
this.message = message.toString();
|
this.message = message.toString();
|
||||||
this.mod = ModTranslations.getModById(localMod.getId());
|
this.mod = ModTranslations.getModById(localModFile.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTitle() {
|
String getTitle() {
|
||||||
return localMod.getFileName();
|
return localModFile.getFileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSubtitle() {
|
String getSubtitle() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalMod getModInfo() {
|
LocalModFile getModInfo() {
|
||||||
return localMod;
|
return localModFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModTranslations.Mod getMod() {
|
public ModTranslations.Mod getMod() {
|
||||||
@@ -162,7 +162,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NotNull ModListPageSkin.ModInfoObject o) {
|
public int compareTo(@NotNull ModListPageSkin.ModInfoObject o) {
|
||||||
return localMod.getFileName().toLowerCase().compareTo(o.localMod.getFileName().toLowerCase());
|
return localModFile.getFileName().toLowerCase().compareTo(o.localModFile.getFileName().toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,294 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 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.versions;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.*;
|
||||||
|
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
|
import org.jackhuang.hmcl.mod.ModManager;
|
||||||
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
|
import org.jackhuang.hmcl.mod.curse.CurseAddon;
|
||||||
|
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MDListCell;
|
||||||
|
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.util.Pair;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
|
public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
||||||
|
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("mods.check_updates"), -1));
|
||||||
|
|
||||||
|
private final ModManager modManager;
|
||||||
|
private final ObservableList<ModUpdateObject> objects;
|
||||||
|
|
||||||
|
public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> updates) {
|
||||||
|
this.modManager = modManager;
|
||||||
|
|
||||||
|
JFXTreeTableColumn<ModUpdateObject, Boolean> enabledColumn = new JFXTreeTableColumn<>();
|
||||||
|
enabledColumn.setCellFactory(column -> new JFXCheckBoxTreeTableCell<>());
|
||||||
|
setupCellValueFactory(enabledColumn, ModUpdateObject::enabledProperty);
|
||||||
|
enabledColumn.setEditable(true);
|
||||||
|
enabledColumn.setMaxWidth(40);
|
||||||
|
enabledColumn.setMinWidth(40);
|
||||||
|
|
||||||
|
JFXTreeTableColumn<ModUpdateObject, String> fileNameColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.file"));
|
||||||
|
setupCellValueFactory(fileNameColumn, ModUpdateObject::fileNameProperty);
|
||||||
|
|
||||||
|
JFXTreeTableColumn<ModUpdateObject, String> currentVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.current_version"));
|
||||||
|
setupCellValueFactory(currentVersionColumn, ModUpdateObject::currentVersionProperty);
|
||||||
|
|
||||||
|
JFXTreeTableColumn<ModUpdateObject, String> targetVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.target_version"));
|
||||||
|
setupCellValueFactory(targetVersionColumn, ModUpdateObject::targetVersionProperty);
|
||||||
|
|
||||||
|
JFXTreeTableColumn<ModUpdateObject, String> sourceColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.source"));
|
||||||
|
setupCellValueFactory(sourceColumn, ModUpdateObject::sourceProperty);
|
||||||
|
|
||||||
|
objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
RecursiveTreeItem<ModUpdateObject> root = new RecursiveTreeItem<>(
|
||||||
|
objects,
|
||||||
|
RecursiveTreeObject::getChildren);
|
||||||
|
|
||||||
|
JFXTreeTableView<ModUpdateObject> table = new JFXTreeTableView<>(root);
|
||||||
|
table.setShowRoot(false);
|
||||||
|
table.setEditable(true);
|
||||||
|
table.getColumns().setAll(enabledColumn, fileNameColumn, currentVersionColumn, targetVersionColumn, sourceColumn);
|
||||||
|
|
||||||
|
setCenter(table);
|
||||||
|
|
||||||
|
HBox actions = new HBox(8);
|
||||||
|
actions.setPadding(new Insets(8));
|
||||||
|
actions.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
|
JFXButton nextButton = new JFXButton(i18n("mods.check_updates.update"));
|
||||||
|
nextButton.getStyleClass().add("jfx-button-raised");
|
||||||
|
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||||
|
nextButton.setOnAction(e -> updateMods());
|
||||||
|
|
||||||
|
JFXButton cancelButton = new JFXButton(i18n("button.cancel"));
|
||||||
|
cancelButton.getStyleClass().add("jfx-button-raised");
|
||||||
|
cancelButton.setButtonType(JFXButton.ButtonType.RAISED);
|
||||||
|
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
|
||||||
|
onEscPressed(this, cancelButton::fire);
|
||||||
|
|
||||||
|
actions.getChildren().setAll(nextButton, cancelButton);
|
||||||
|
setBottom(actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void setupCellValueFactory(JFXTreeTableColumn<ModUpdateObject, T> column, Function<ModUpdateObject, ObservableValue<T>> mapper) {
|
||||||
|
column.setCellValueFactory(param -> {
|
||||||
|
if (column.validateValue(param))
|
||||||
|
return mapper.apply(param.getValue().getValue());
|
||||||
|
else
|
||||||
|
return column.getComputedValue(param);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMods() {
|
||||||
|
Controllers.taskDialog(
|
||||||
|
new ModUpdateTask(
|
||||||
|
modManager,
|
||||||
|
objects.stream()
|
||||||
|
.filter(o -> o.enabled.get())
|
||||||
|
.map(object -> pair(object.data.getLocalMod(), object.data.getCandidates().get(0)))
|
||||||
|
.collect(Collectors.toList())),
|
||||||
|
i18n("mods.check_updates.update"),
|
||||||
|
t -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectWrapper<State> stateProperty() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ModUpdateCell extends MDListCell<LocalModFile.ModUpdate> {
|
||||||
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
|
|
||||||
|
public ModUpdateCell(JFXListView<LocalModFile.ModUpdate> listView) {
|
||||||
|
super(listView);
|
||||||
|
|
||||||
|
getContainer().getChildren().setAll(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateControl(LocalModFile.ModUpdate item, boolean empty) {
|
||||||
|
if (empty) return;
|
||||||
|
ModTranslations.Mod mod = ModTranslations.getModById(item.getLocalMod().getId());
|
||||||
|
content.setTitle(mod != null ? mod.getDisplayName() : item.getCurrentVersion().getName());
|
||||||
|
content.setSubtitle(item.getLocalMod().getFileName());
|
||||||
|
content.getTags().setAll();
|
||||||
|
|
||||||
|
if (item.getCurrentVersion().getSelf() instanceof CurseAddon.LatestFile) {
|
||||||
|
content.getTags().add("Curseforge");
|
||||||
|
} else if (item.getCurrentVersion().getSelf() instanceof ModrinthRemoteModRepository.ModVersion) {
|
||||||
|
content.getTags().add("Modrinth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModUpdateObject extends RecursiveTreeObject<ModUpdateObject> {
|
||||||
|
final LocalModFile.ModUpdate data;
|
||||||
|
final BooleanProperty enabled = new SimpleBooleanProperty();
|
||||||
|
final StringProperty fileName = new SimpleStringProperty();
|
||||||
|
final StringProperty currentVersion = new SimpleStringProperty();
|
||||||
|
final StringProperty targetVersion = new SimpleStringProperty();
|
||||||
|
final StringProperty source = new SimpleStringProperty();
|
||||||
|
|
||||||
|
public ModUpdateObject(LocalModFile.ModUpdate data) {
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
enabled.set(true);
|
||||||
|
fileName.set(data.getLocalMod().getFileName());
|
||||||
|
currentVersion.set(data.getCurrentVersion().getVersion());
|
||||||
|
targetVersion.set(data.getCandidates().get(0).getVersion());
|
||||||
|
switch (data.getCurrentVersion().getSelf().getType()) {
|
||||||
|
case CURSEFORGE:
|
||||||
|
source.set(i18n("mods.curseforge"));
|
||||||
|
break;
|
||||||
|
case MODRINTH:
|
||||||
|
source.set(i18n("mods.modrinth"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty enabledProperty() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled.set(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty fileNameProperty() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileName(String fileName) {
|
||||||
|
this.fileName.set(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentVersion() {
|
||||||
|
return currentVersion.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty currentVersionProperty() {
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentVersion(String currentVersion) {
|
||||||
|
this.currentVersion.set(currentVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetVersion() {
|
||||||
|
return targetVersion.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty targetVersionProperty() {
|
||||||
|
return targetVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetVersion(String targetVersion) {
|
||||||
|
this.targetVersion.set(targetVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty sourceProperty() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source.set(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ModUpdateTask extends Task<Void> {
|
||||||
|
private final Collection<Task<?>> dependents;
|
||||||
|
|
||||||
|
ModUpdateTask(ModManager modManager, List<Pair<LocalModFile, RemoteMod.Version>> mods) {
|
||||||
|
setStage("mods.check_updates.update");
|
||||||
|
getProperties().put("total", mods.size());
|
||||||
|
|
||||||
|
dependents = mods.stream()
|
||||||
|
.map(mod -> {
|
||||||
|
return Task
|
||||||
|
.supplyAsync(() -> {
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.setName(mod.getKey().getName())
|
||||||
|
.setSignificance(TaskSignificance.MAJOR)
|
||||||
|
.withCounter("mods.check_updates.update");
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Task<?>> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doPreExecute() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preExecute() {
|
||||||
|
notifyPropertiesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRelyingOnDependents() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2021 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.versions;
|
|
||||||
|
|
||||||
import com.jfoenix.controls.*;
|
|
||||||
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseAddon;
|
|
||||||
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.MDListCell;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
|
||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|
||||||
|
|
||||||
public class ModUpdatesPane extends BorderPane implements DecoratorPage {
|
|
||||||
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1));
|
|
||||||
|
|
||||||
public ModUpdatesPane(List<LocalMod.ModUpdate> updates) {
|
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> fileNameColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.file"));
|
|
||||||
fileNameColumn.setCellValueFactory(data -> {
|
|
||||||
if (fileNameColumn.validateValue(data)) {
|
|
||||||
return data.getValue().getValue().fileName;
|
|
||||||
} else {
|
|
||||||
return fileNameColumn.getComputedValue(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> currentVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.current_version"));
|
|
||||||
currentVersionColumn.setCellValueFactory(data -> {
|
|
||||||
if (currentVersionColumn.validateValue(data)) {
|
|
||||||
return data.getValue().getValue().currentVersion;
|
|
||||||
} else {
|
|
||||||
return currentVersionColumn.getComputedValue(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JFXTreeTableColumn<ModUpdateObject, String> targetVersionColumn = new JFXTreeTableColumn<>(i18n("mods.check_updates.target_version"));
|
|
||||||
targetVersionColumn.setCellValueFactory(data -> {
|
|
||||||
if (targetVersionColumn.validateValue(data)) {
|
|
||||||
return data.getValue().getValue().targetVersion;
|
|
||||||
} else {
|
|
||||||
return targetVersionColumn.getComputedValue(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ObservableList<ModUpdateObject> objects = FXCollections.observableList(updates.stream().map(ModUpdateObject::new).collect(Collectors.toList()));
|
|
||||||
|
|
||||||
RecursiveTreeItem<ModUpdateObject> root = new RecursiveTreeItem<>(
|
|
||||||
objects,
|
|
||||||
RecursiveTreeObject::getChildren);
|
|
||||||
|
|
||||||
JFXTreeTableView<ModUpdateObject> table = new JFXTreeTableView<>(root);
|
|
||||||
table.setShowRoot(false);
|
|
||||||
table.setEditable(true);
|
|
||||||
table.getColumns().setAll(fileNameColumn, currentVersionColumn, targetVersionColumn);
|
|
||||||
|
|
||||||
setCenter(table);
|
|
||||||
|
|
||||||
HBox actions = new HBox(8);
|
|
||||||
actions.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
|
|
||||||
JFXButton nextButton = new JFXButton();
|
|
||||||
nextButton.getStyleClass().add("jfx-button-raised");
|
|
||||||
nextButton.setButtonType(JFXButton.ButtonType.RAISED);
|
|
||||||
nextButton.setOnAction(e -> updateMods());
|
|
||||||
|
|
||||||
JFXButton cancelButton = new JFXButton();
|
|
||||||
cancelButton.getStyleClass().add("jfx-button-raised");
|
|
||||||
cancelButton.setButtonType(JFXButton.ButtonType.RAISED);
|
|
||||||
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
|
|
||||||
onEscPressed(this, cancelButton::fire);
|
|
||||||
|
|
||||||
actions.getChildren().setAll(nextButton, cancelButton);
|
|
||||||
setBottom(actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMods() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReadOnlyObjectWrapper<State> stateProperty() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ModUpdateCell extends MDListCell<LocalMod.ModUpdate> {
|
|
||||||
TwoLineListItem content = new TwoLineListItem();
|
|
||||||
|
|
||||||
public ModUpdateCell(JFXListView<LocalMod.ModUpdate> listView) {
|
|
||||||
super(listView);
|
|
||||||
|
|
||||||
getContainer().getChildren().setAll(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateControl(LocalMod.ModUpdate item, boolean empty) {
|
|
||||||
if (empty) return;
|
|
||||||
ModTranslations.Mod mod = ModTranslations.getModById(item.getLocalMod().getId());
|
|
||||||
content.setTitle(mod != null ? mod.getDisplayName() : item.getCurrentVersion().getName());
|
|
||||||
content.setSubtitle(item.getLocalMod().getFileName());
|
|
||||||
content.getTags().setAll();
|
|
||||||
|
|
||||||
if (item.getCurrentVersion().getSelf() instanceof CurseAddon.LatestFile) {
|
|
||||||
content.getTags().add("Curseforge");
|
|
||||||
} else if (item.getCurrentVersion().getSelf() instanceof ModrinthRemoteModRepository.ModVersion) {
|
|
||||||
content.getTags().add("Modrinth");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ModUpdateObject extends RecursiveTreeObject<ModUpdateObject> {
|
|
||||||
final LocalMod.ModUpdate data;
|
|
||||||
final StringProperty fileName = new SimpleStringProperty();
|
|
||||||
final StringProperty currentVersion = new SimpleStringProperty();
|
|
||||||
final StringProperty targetVersion = new SimpleStringProperty();
|
|
||||||
|
|
||||||
public ModUpdateObject(LocalMod.ModUpdate data) {
|
|
||||||
this.data = data;
|
|
||||||
|
|
||||||
fileName.set(data.getLocalMod().getFileName());
|
|
||||||
currentVersion.set(data.getCurrentVersion().getName());
|
|
||||||
targetVersion.set(data.getCandidates().get(0).getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -430,6 +430,7 @@ launch.advice.newer_java=Java 8 is recommended to make the game faster. For many
|
|||||||
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game may crash. Shall we continue launching?
|
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game may crash. Shall we continue launching?
|
||||||
launch.advice.require_newer_java_version=Minecraft %1$s requires Java %2$s or later, are you willing to download one now?
|
launch.advice.require_newer_java_version=Minecraft %1$s requires Java %2$s or later, are you willing to download one now?
|
||||||
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game may crash. The maximum memory capacity for 32 bit systems is is 1024MB. Shall we continue launching?
|
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game may crash. The maximum memory capacity for 32 bit systems is is 1024MB. Shall we continue launching?
|
||||||
|
launch.advice.vanilla_linux_java_8=For Linux x86-64, Minecraft 1.12.2 and below can only run on Java 8.\nJava 9 and above versions cannot load 32-bit native libraries like liblwjgl.so.
|
||||||
launch.failed=Unable to launch
|
launch.failed=Unable to launch
|
||||||
launch.failed.cannot_create_jvm=Java virtual machine could not be created. Java arguments may cause issues. Please restart without JVM arguments.
|
launch.failed.cannot_create_jvm=Java virtual machine could not be created. Java arguments may cause issues. Please restart without JVM arguments.
|
||||||
launch.failed.creating_process=Failed to create process. Check your Java path.
|
launch.failed.creating_process=Failed to create process. Check your Java path.
|
||||||
@@ -581,6 +582,11 @@ mods.add.failed=Failed to install mods %s.
|
|||||||
mods.add.success=Successfully installed mods %s.
|
mods.add.success=Successfully installed mods %s.
|
||||||
mods.category=Category
|
mods.category=Category
|
||||||
mods.check_updates=Check updates
|
mods.check_updates=Check updates
|
||||||
|
mods.check_updates.current_version=Current
|
||||||
|
mods.check_updates.file=File
|
||||||
|
mods.check_updates.source=Source
|
||||||
|
mods.check_updates.target_version=Target
|
||||||
|
mods.check_updates.update=Update
|
||||||
mods.choose_mod=Choose your mods
|
mods.choose_mod=Choose your mods
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=Dependencies
|
mods.dependencies=Dependencies
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ launch.advice.newer_java=偵測到您未使用 Java 8 及更新版本,Java 8
|
|||||||
launch.advice.not_enough_space=您設定的記憶體大小過大,由於超過了系統記憶體大小 %dMB,所以可能影響遊戲體驗或無法啟動遊戲。是否繼續啟動?
|
launch.advice.not_enough_space=您設定的記憶體大小過大,由於超過了系統記憶體大小 %dMB,所以可能影響遊戲體驗或無法啟動遊戲。是否繼續啟動?
|
||||||
launch.advice.require_newer_java_version=Minecraft %1$s 僅能運行在 Java %2$s 或更高版本上,是否下載?
|
launch.advice.require_newer_java_version=Minecraft %1$s 僅能運行在 Java %2$s 或更高版本上,是否下載?
|
||||||
launch.advice.too_large_memory_for_32bit=您設定的記憶體大小過大,由於可能超過了 32 位元 Java 的記憶體分配限制,所以可能無法啟動遊戲,請將記憶體調至低於 1024MB 的值。是否繼續啟動?
|
launch.advice.too_large_memory_for_32bit=您設定的記憶體大小過大,由於可能超過了 32 位元 Java 的記憶體分配限制,所以可能無法啟動遊戲,請將記憶體調至低於 1024MB 的值。是否繼續啟動?
|
||||||
|
launch.advice.vanilla_linux_java_8=對於 Linux x86-64 平台,Minecraft 1.12.2 及以下版本僅支持 Java 8。\nJava 9+ 版本會無法載入 32 位的 liblwjgl.so。
|
||||||
launch.failed=啟動失敗
|
launch.failed=啟動失敗
|
||||||
launch.failed.cannot_create_jvm=偵測到無法建立 Java 虛擬機,可能是 Java 參數有問題。可以在設定中開啟無參數模式啟動。
|
launch.failed.cannot_create_jvm=偵測到無法建立 Java 虛擬機,可能是 Java 參數有問題。可以在設定中開啟無參數模式啟動。
|
||||||
launch.failed.creating_process=啟動失敗,在建立新處理程式時發生錯誤。可能是 Java 路徑錯誤。
|
launch.failed.creating_process=啟動失敗,在建立新處理程式時發生錯誤。可能是 Java 路徑錯誤。
|
||||||
@@ -581,6 +582,11 @@ mods.add.failed=新增模組 %s 失敗。
|
|||||||
mods.add.success=成功新增模組 %s。
|
mods.add.success=成功新增模組 %s。
|
||||||
mods.category=類別
|
mods.category=類別
|
||||||
mods.check_updates=檢查模組更新
|
mods.check_updates=檢查模組更新
|
||||||
|
mods.check_updates.current_version=當前版本
|
||||||
|
mods.check_updates.file=文件
|
||||||
|
mods.check_updates.source=來源
|
||||||
|
mods.check_updates.target_version=目標版本
|
||||||
|
mods.check_updates.update=更新
|
||||||
mods.choose_mod=選擇模組
|
mods.choose_mod=選擇模組
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=前置 Mod
|
mods.dependencies=前置 Mod
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ launch.advice.newer_java=检测到您未使用 Java 8 及更新版本,Java 8
|
|||||||
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小 %dMB,所以可能影响游戏体验或无法启动游戏。是否继续启动?
|
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小 %dMB,所以可能影响游戏体验或无法启动游戏。是否继续启动?
|
||||||
launch.advice.require_newer_java_version=Minecraft %1$s 仅能运行在 Java %2$s 或更高版本上,是否下载?
|
launch.advice.require_newer_java_version=Minecraft %1$s 仅能运行在 Java %2$s 或更高版本上,是否下载?
|
||||||
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏,请将内存调至 1024MB 或更小。是否继续启动?
|
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了 32 位 Java 的内存分配限制,所以可能无法启动游戏,请将内存调至 1024MB 或更小。是否继续启动?
|
||||||
|
launch.advice.vanilla_linux_java_8=对于 Linux x86-64 平台,Minecraft 1.12.2 及以下版本仅支持 Java 8。\nJava 9+ 版本会无法加载 32 位的 liblwjgl.so。
|
||||||
launch.failed=启动失败
|
launch.failed=启动失败
|
||||||
launch.failed.cannot_create_jvm=截获到无法创建 Java 虚拟机,可能是 Java 参数有问题,可以在设置中开启无参数模式启动。
|
launch.failed.cannot_create_jvm=截获到无法创建 Java 虚拟机,可能是 Java 参数有问题,可以在设置中开启无参数模式启动。
|
||||||
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
|
launch.failed.creating_process=启动失败,在创建新进程时发生错误,可能是 Java 路径错误。
|
||||||
@@ -583,7 +584,9 @@ mods.category=类别
|
|||||||
mods.check_updates=检查模组更新
|
mods.check_updates=检查模组更新
|
||||||
mods.check_updates.current_version=当前版本
|
mods.check_updates.current_version=当前版本
|
||||||
mods.check_updates.file=文件
|
mods.check_updates.file=文件
|
||||||
|
mods.check_updates.source=来源
|
||||||
mods.check_updates.target_version=目标版本
|
mods.check_updates.target_version=目标版本
|
||||||
|
mods.check_updates.update=更新
|
||||||
mods.choose_mod=选择模组
|
mods.choose_mod=选择模组
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=前置 Mod
|
mods.dependencies=前置 Mod
|
||||||
|
|||||||
@@ -202,10 +202,10 @@ public class Datapack {
|
|||||||
private Path file;
|
private Path file;
|
||||||
private final BooleanProperty active;
|
private final BooleanProperty active;
|
||||||
private final String id;
|
private final String id;
|
||||||
private final LocalMod.Description description;
|
private final LocalModFile.Description description;
|
||||||
private final Datapack datapack;
|
private final Datapack datapack;
|
||||||
|
|
||||||
public Pack(Path file, String id, LocalMod.Description description, Datapack datapack) {
|
public Pack(Path file, String id, LocalModFile.Description description, Datapack datapack) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@@ -235,7 +235,7 @@ public class Datapack {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod.Description getDescription() {
|
public LocalModFile.Description getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,14 +64,14 @@ public final class FabricModMetadata {
|
|||||||
this.contact = contact;
|
this.contact = contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod fromFile(File modFile) throws IOException, JsonParseException {
|
public static LocalModFile fromFile(File modFile) throws IOException, JsonParseException {
|
||||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
||||||
Path mcmod = fs.getPath("fabric.mod.json");
|
Path mcmod = fs.getPath("fabric.mod.json");
|
||||||
if (Files.notExists(mcmod))
|
if (Files.notExists(mcmod))
|
||||||
throw new IOException("File " + modFile + " is not a Fabric mod.");
|
throw new IOException("File " + modFile + " is not a Fabric mod.");
|
||||||
FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class);
|
FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class);
|
||||||
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", "));
|
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", "));
|
||||||
return new LocalMod(modFile, ModLoaderType.FABRIC, metadata.id, metadata.name, new LocalMod.Description(metadata.description),
|
return new LocalModFile(modFile, ModLoaderType.FABRIC, metadata.id, metadata.name, new LocalModFile.Description(metadata.description),
|
||||||
authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon);
|
authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public final class ForgeNewModMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod fromFile(File modFile) throws IOException, JsonParseException {
|
public static LocalModFile fromFile(File modFile) throws IOException, JsonParseException {
|
||||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
||||||
Path modstoml = fs.getPath("META-INF/mods.toml");
|
Path modstoml = fs.getPath("META-INF/mods.toml");
|
||||||
if (Files.notExists(modstoml))
|
if (Files.notExists(modstoml))
|
||||||
@@ -135,7 +135,7 @@ public final class ForgeNewModMetadata {
|
|||||||
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile.getPath());
|
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile.getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new LocalMod(modFile, ModLoaderType.FORGE, mod.getModId(), mod.getDisplayName(), new LocalMod.Description(mod.getDescription()),
|
return new LocalModFile(modFile, ModLoaderType.FORGE, mod.getModId(), mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()),
|
||||||
mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
|
mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
|
||||||
mod.getDisplayURL(),
|
mod.getDisplayURL(),
|
||||||
metadata.getLogoFile());
|
metadata.getLogoFile());
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public final class ForgeOldModMetadata {
|
|||||||
return authors;
|
return authors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod fromFile(File modFile) throws IOException, JsonParseException {
|
public static LocalModFile fromFile(File modFile) throws IOException, JsonParseException {
|
||||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
||||||
Path mcmod = fs.getPath("mcmod.info");
|
Path mcmod = fs.getPath("mcmod.info");
|
||||||
if (Files.notExists(mcmod))
|
if (Files.notExists(mcmod))
|
||||||
@@ -138,7 +138,7 @@ public final class ForgeOldModMetadata {
|
|||||||
authors = String.join(", ", metadata.getAuthorList());
|
authors = String.join(", ", metadata.getAuthorList());
|
||||||
if (StringUtils.isBlank(authors))
|
if (StringUtils.isBlank(authors))
|
||||||
authors = metadata.getCredits();
|
authors = metadata.getCredits();
|
||||||
return new LocalMod(modFile, ModLoaderType.FORGE, metadata.getModId(), metadata.getName(), new LocalMod.Description(metadata.getDescription()),
|
return new LocalModFile(modFile, ModLoaderType.FORGE, metadata.getModId(), metadata.getName(), new LocalModFile.Description(metadata.getDescription()),
|
||||||
authors, metadata.getVersion(), metadata.getGameVersion(),
|
authors, metadata.getVersion(), metadata.getGameVersion(),
|
||||||
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
|
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
|
||||||
metadata.getLogoFile());
|
metadata.getLogoFile());
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public final class LiteModMetadata {
|
|||||||
return updateURI;
|
return updateURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod fromFile(File modFile) throws IOException, JsonParseException {
|
public static LocalModFile fromFile(File modFile) throws IOException, JsonParseException {
|
||||||
try (ZipFile zipFile = new ZipFile(modFile)) {
|
try (ZipFile zipFile = new ZipFile(modFile)) {
|
||||||
ZipEntry entry = zipFile.getEntry("litemod.json");
|
ZipEntry entry = zipFile.getEntry("litemod.json");
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
@@ -116,7 +116,7 @@ public final class LiteModMetadata {
|
|||||||
LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class);
|
LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class);
|
||||||
if (metadata == null)
|
if (metadata == null)
|
||||||
throw new IOException("Mod " + modFile + " `litemod.json` is malformed.");
|
throw new IOException("Mod " + modFile + " `litemod.json` is malformed.");
|
||||||
return new LocalMod(modFile, ModLoaderType.FORGE, null, metadata.getName(), new LocalMod.Description(metadata.getDescription()), metadata.getAuthor(),
|
return new LocalModFile(modFile, ModLoaderType.FORGE, null, metadata.getName(), new LocalModFile.Description(metadata.getDescription()), metadata.getAuthor(),
|
||||||
metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), "");
|
metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import java.util.stream.Collectors;
|
|||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class LocalMod implements Comparable<LocalMod> {
|
public final class LocalModFile implements Comparable<LocalModFile> {
|
||||||
|
|
||||||
private Path file;
|
private Path file;
|
||||||
private final ModLoaderType modLoaderType;
|
private final ModLoaderType modLoaderType;
|
||||||
@@ -49,11 +49,11 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
private final String logoPath;
|
private final String logoPath;
|
||||||
private final BooleanProperty activeProperty;
|
private final BooleanProperty activeProperty;
|
||||||
|
|
||||||
public LocalMod(File file, ModLoaderType modLoaderType, String id, String name, Description description) {
|
public LocalModFile(File file, ModLoaderType modLoaderType, String id, String name, Description description) {
|
||||||
this(file, modLoaderType, id, name, description, "", "", "", "", "");
|
this(file, modLoaderType, id, name, description, "", "", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod(File file, ModLoaderType modLoaderType, String id, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) {
|
public LocalModFile(File file, ModLoaderType modLoaderType, String id, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) {
|
||||||
this.file = file.toPath();
|
this.file = file.toPath();
|
||||||
this.modLoaderType = modLoaderType;
|
this.modLoaderType = modLoaderType;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -68,13 +68,13 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
activeProperty = new SimpleBooleanProperty(this, "active", !ModManager.isDisabled(file)) {
|
activeProperty = new SimpleBooleanProperty(this, "active", !ModManager.isDisabled(file)) {
|
||||||
@Override
|
@Override
|
||||||
protected void invalidated() {
|
protected void invalidated() {
|
||||||
Path path = LocalMod.this.file.toAbsolutePath();
|
Path path = LocalModFile.this.file.toAbsolutePath();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (get())
|
if (get())
|
||||||
LocalMod.this.file = ModManager.enableMod(path);
|
LocalModFile.this.file = ModManager.enableMod(path);
|
||||||
else
|
else
|
||||||
LocalMod.this.file = ModManager.disableMod(path);
|
LocalModFile.this.file = ModManager.disableMod(path);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e);
|
Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e);
|
||||||
}
|
}
|
||||||
@@ -153,13 +153,13 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(LocalMod o) {
|
public int compareTo(LocalModFile o) {
|
||||||
return getFileName().compareTo(o.getFileName());
|
return getFileName().compareTo(o.getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof LocalMod && Objects.equals(getFileName(), ((LocalMod) obj).getFileName());
|
return obj instanceof LocalModFile && Objects.equals(getFileName(), ((LocalModFile) obj).getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -168,18 +168,18 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ModUpdate {
|
public static class ModUpdate {
|
||||||
private final LocalMod localMod;
|
private final LocalModFile localModFile;
|
||||||
private final RemoteMod.Version currentVersion;
|
private final RemoteMod.Version currentVersion;
|
||||||
private final List<RemoteMod.Version> candidates;
|
private final List<RemoteMod.Version> candidates;
|
||||||
|
|
||||||
public ModUpdate(LocalMod localMod, RemoteMod.Version currentVersion, List<RemoteMod.Version> candidates) {
|
public ModUpdate(LocalModFile localModFile, RemoteMod.Version currentVersion, List<RemoteMod.Version> candidates) {
|
||||||
this.localMod = localMod;
|
this.localModFile = localModFile;
|
||||||
this.currentVersion = currentVersion;
|
this.currentVersion = currentVersion;
|
||||||
this.candidates = candidates;
|
this.candidates = candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod getLocalMod() {
|
public LocalModFile getLocalMod() {
|
||||||
return localMod;
|
return localModFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteMod.Version getCurrentVersion() {
|
public RemoteMod.Version getCurrentVersion() {
|
||||||
@@ -32,7 +32,7 @@ import java.util.TreeSet;
|
|||||||
public final class ModManager {
|
public final class ModManager {
|
||||||
private final GameRepository repository;
|
private final GameRepository repository;
|
||||||
private final String id;
|
private final String id;
|
||||||
private final TreeSet<LocalMod> localMods = new TreeSet<>();
|
private final TreeSet<LocalModFile> localModFiles = new TreeSet<>();
|
||||||
|
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
|
|
||||||
@@ -55,12 +55,12 @@ public final class ModManager {
|
|||||||
|
|
||||||
private void addModInfo(File file) {
|
private void addModInfo(File file) {
|
||||||
try {
|
try {
|
||||||
localMods.add(getModInfo(file));
|
localModFiles.add(getModInfo(file));
|
||||||
} catch (IllegalArgumentException ignore) {
|
} catch (IllegalArgumentException ignore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod getModInfo(File modFile) {
|
public static LocalModFile getModInfo(File modFile) {
|
||||||
File file = isDisabled(modFile) ? new File(modFile.getAbsoluteFile().getParentFile(), FileUtils.getNameWithoutExtension(modFile)) : modFile;
|
File file = isDisabled(modFile) ? new File(modFile.getAbsoluteFile().getParentFile(), FileUtils.getNameWithoutExtension(modFile)) : modFile;
|
||||||
String description, extension = FileUtils.getExtension(file);
|
String description, extension = FileUtils.getExtension(file);
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
@@ -98,11 +98,11 @@ public final class ModManager {
|
|||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("File " + modFile + " is not a mod file.");
|
throw new IllegalArgumentException("File " + modFile + " is not a mod file.");
|
||||||
}
|
}
|
||||||
return new LocalMod(modFile, ModLoaderType.UNKNOWN, null, FileUtils.getNameWithoutExtension(modFile), new LocalMod.Description(description));
|
return new LocalModFile(modFile, ModLoaderType.UNKNOWN, null, FileUtils.getNameWithoutExtension(modFile), new LocalModFile.Description(description));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshMods() throws IOException {
|
public void refreshMods() throws IOException {
|
||||||
localMods.clear();
|
localModFiles.clear();
|
||||||
if (Files.isDirectory(getModsDirectory())) {
|
if (Files.isDirectory(getModsDirectory())) {
|
||||||
try (DirectoryStream<Path> modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) {
|
try (DirectoryStream<Path> modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) {
|
||||||
for (Path subitem : modsDirectoryStream) {
|
for (Path subitem : modsDirectoryStream) {
|
||||||
@@ -122,10 +122,10 @@ public final class ModManager {
|
|||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<LocalMod> getMods() throws IOException {
|
public Collection<LocalModFile> getMods() throws IOException {
|
||||||
if (!loaded)
|
if (!loaded)
|
||||||
refreshMods();
|
refreshMods();
|
||||||
return localMods;
|
return localModFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMod(File file) throws IOException {
|
public void addMod(File file) throws IOException {
|
||||||
@@ -145,9 +145,9 @@ public final class ModManager {
|
|||||||
addModInfo(newFile);
|
addModInfo(newFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeMods(LocalMod... localMods) throws IOException {
|
public void removeMods(LocalModFile... localModFiles) throws IOException {
|
||||||
for (LocalMod localMod : localMods) {
|
for (LocalModFile localModFile : localModFiles) {
|
||||||
Files.deleteIfExists(localMod.getFile());
|
Files.deleteIfExists(localModFile.getFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +165,10 @@ public final class ModManager {
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isOld(File file) {
|
||||||
|
return file.getPath().endsWith(OLD_EXTENSION);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isDisabled(File file) {
|
public static boolean isDisabled(File file) {
|
||||||
return file.getPath().endsWith(DISABLED_EXTENSION);
|
return file.getPath().endsWith(DISABLED_EXTENSION);
|
||||||
}
|
}
|
||||||
@@ -228,4 +232,5 @@ public final class ModManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final String DISABLED_EXTENSION = ".disabled";
|
public static final String DISABLED_EXTENSION = ".disabled";
|
||||||
|
public static final String OLD_EXTENSION = ".old";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,13 +66,13 @@ public class PackMcMeta implements Validation {
|
|||||||
private final int packFormat;
|
private final int packFormat;
|
||||||
|
|
||||||
@SerializedName("description")
|
@SerializedName("description")
|
||||||
private final LocalMod.Description description;
|
private final LocalModFile.Description description;
|
||||||
|
|
||||||
public PackInfo() {
|
public PackInfo() {
|
||||||
this(0, new LocalMod.Description(Collections.emptyList()));
|
this(0, new LocalModFile.Description(Collections.emptyList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public PackInfo(int packFormat, LocalMod.Description description) {
|
public PackInfo(int packFormat, LocalModFile.Description description) {
|
||||||
this.packFormat = packFormat;
|
this.packFormat = packFormat;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ public class PackMcMeta implements Validation {
|
|||||||
return packFormat;
|
return packFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod.Description getDescription() {
|
public LocalModFile.Description getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,13 +112,13 @@ public class PackMcMeta implements Validation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod.Description.Part deserialize(JsonElement json, JsonDeserializationContext context) throws JsonParseException {
|
public LocalModFile.Description.Part deserialize(JsonElement json, JsonDeserializationContext context) throws JsonParseException {
|
||||||
if (json.isJsonPrimitive()) {
|
if (json.isJsonPrimitive()) {
|
||||||
return new LocalMod.Description.Part(parseText(json));
|
return new LocalModFile.Description.Part(parseText(json));
|
||||||
} else if (json.isJsonObject()) {
|
} else if (json.isJsonObject()) {
|
||||||
JsonObject obj = json.getAsJsonObject();
|
JsonObject obj = json.getAsJsonObject();
|
||||||
String text = parseText(obj.get("text"));
|
String text = parseText(obj.get("text"));
|
||||||
return new LocalMod.Description.Part(text);
|
return new LocalModFile.Description.Part(text);
|
||||||
} else {
|
} else {
|
||||||
throw new JsonParseException("pack.mcmeta Raw JSON text should be string or an object");
|
throw new JsonParseException("pack.mcmeta Raw JSON text should be string or an object");
|
||||||
}
|
}
|
||||||
@@ -126,31 +126,31 @@ public class PackMcMeta implements Validation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
List<LocalMod.Description.Part> parts = new ArrayList<>();
|
List<LocalModFile.Description.Part> parts = new ArrayList<>();
|
||||||
JsonObject packInfo = json.getAsJsonObject();
|
JsonObject packInfo = json.getAsJsonObject();
|
||||||
int packFormat = packInfo.get("pack_format").getAsInt();
|
int packFormat = packInfo.get("pack_format").getAsInt();
|
||||||
JsonElement description = packInfo.get("description");
|
JsonElement description = packInfo.get("description");
|
||||||
if (description.isJsonPrimitive()) {
|
if (description.isJsonPrimitive()) {
|
||||||
parts.add(new LocalMod.Description.Part(parseText(description)));
|
parts.add(new LocalModFile.Description.Part(parseText(description)));
|
||||||
} else if (description.isJsonArray()) {
|
} else if (description.isJsonArray()) {
|
||||||
for (JsonElement element : description.getAsJsonArray()) {
|
for (JsonElement element : description.getAsJsonArray()) {
|
||||||
JsonObject descriptionPart = element.getAsJsonObject();
|
JsonObject descriptionPart = element.getAsJsonObject();
|
||||||
parts.add(new LocalMod.Description.Part(descriptionPart.get("text").getAsString(), descriptionPart.get("color").getAsString()));
|
parts.add(new LocalModFile.Description.Part(descriptionPart.get("text").getAsString(), descriptionPart.get("color").getAsString()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new JsonParseException("pack.mcmeta::pack::description should be String or array of text objects with text and color fields");
|
throw new JsonParseException("pack.mcmeta::pack::description should be String or array of text objects with text and color fields");
|
||||||
}
|
}
|
||||||
return new PackInfo(packFormat, new LocalMod.Description(parts));
|
return new PackInfo(packFormat, new LocalModFile.Description(parts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalMod fromFile(File modFile) throws IOException, JsonParseException {
|
public static LocalModFile fromFile(File modFile) throws IOException, JsonParseException {
|
||||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) {
|
||||||
Path mcmod = fs.getPath("pack.mcmeta");
|
Path mcmod = fs.getPath("pack.mcmeta");
|
||||||
if (Files.notExists(mcmod))
|
if (Files.notExists(mcmod))
|
||||||
throw new IOException("File " + modFile + " is not a resource pack.");
|
throw new IOException("File " + modFile + " is not a resource pack.");
|
||||||
PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class);
|
PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class);
|
||||||
return new LocalMod(modFile, ModLoaderType.PACK, null, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", "");
|
return new LocalModFile(modFile, ModLoaderType.PACK, null, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,14 +82,23 @@ public class RemoteMod {
|
|||||||
Alpha
|
Alpha
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
CURSEFORGE,
|
||||||
|
MODRINTH
|
||||||
|
}
|
||||||
|
|
||||||
public interface IMod {
|
public interface IMod {
|
||||||
List<RemoteMod> loadDependencies() throws IOException;
|
List<RemoteMod> loadDependencies() throws IOException;
|
||||||
|
|
||||||
Stream<Version> loadVersions() throws IOException;
|
Stream<Version> loadVersions() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IVersion {
|
||||||
|
Type getType();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Version {
|
public static class Version {
|
||||||
private final Object self;
|
private final IVersion self;
|
||||||
private final String modid;
|
private final String modid;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String version;
|
private final String version;
|
||||||
@@ -101,7 +110,7 @@ public class RemoteMod {
|
|||||||
private final List<String> gameVersions;
|
private final List<String> gameVersions;
|
||||||
private final List<ModLoaderType> loaders;
|
private final List<ModLoaderType> loaders;
|
||||||
|
|
||||||
public Version(Object self, String modid, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List<String> dependencies, List<String> gameVersions, List<ModLoaderType> loaders) {
|
public Version(IVersion self, String modid, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List<String> dependencies, List<String> gameVersions, List<ModLoaderType> loaders) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.modid = modid;
|
this.modid = modid;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -115,7 +124,7 @@ public class RemoteMod {
|
|||||||
this.loaders = loaders;
|
this.loaders = loaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getSelf() {
|
public IVersion getSelf() {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public interface RemoteModRepository {
|
|||||||
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
|
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException;
|
Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException;
|
||||||
|
|
||||||
RemoteMod getModById(String id) throws IOException;
|
RemoteMod getModById(String id) throws IOException;
|
||||||
|
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
public static class LatestFile {
|
public static class LatestFile implements RemoteMod.IVersion {
|
||||||
private final int id;
|
private final int id;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final String fileName;
|
private final String fileName;
|
||||||
@@ -481,6 +481,11 @@ public class CurseAddon implements RemoteMod.IMod {
|
|||||||
return fileDataInstant;
|
return fileDataInstant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteMod.Type getType() {
|
||||||
|
return RemoteMod.Type.CURSEFORGE;
|
||||||
|
}
|
||||||
|
|
||||||
public RemoteMod.Version toVersion() {
|
public RemoteMod.Version toVersion() {
|
||||||
RemoteMod.VersionType versionType;
|
RemoteMod.VersionType versionType;
|
||||||
switch (getReleaseType()) {
|
switch (getReleaseType()) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.mod.curse;
|
package org.jackhuang.hmcl.mod.curse;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.util.MurmurHash2;
|
import org.jackhuang.hmcl.util.MurmurHash2;
|
||||||
@@ -71,7 +71,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException {
|
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
try (InputStream stream = Files.newInputStream(file)) {
|
try (InputStream stream = Files.newInputStream(file)) {
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.mod.modrinth;
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.jackhuang.hmcl.mod.LocalMod;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
import org.jackhuang.hmcl.mod.ModLoaderType;
|
import org.jackhuang.hmcl.mod.ModLoaderType;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
@@ -74,7 +74,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException {
|
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException {
|
||||||
String sha1 = Hex.encodeHex(DigestUtils.digest("SHA-1", file));
|
String sha1 = Hex.encodeHex(DigestUtils.digest("SHA-1", file));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -198,7 +198,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ModVersion {
|
public static class ModVersion implements RemoteMod.IVersion {
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
@SerializedName("mod_id")
|
@SerializedName("mod_id")
|
||||||
@@ -299,6 +299,11 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
return loaders;
|
return loaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteMod.Type getType() {
|
||||||
|
return RemoteMod.Type.MODRINTH;
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<RemoteMod.Version> toVersion() {
|
public Optional<RemoteMod.Version> toVersion() {
|
||||||
RemoteMod.VersionType type;
|
RemoteMod.VersionType type;
|
||||||
if ("release".equals(versionType)) {
|
if ("release".equals(versionType)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user