优化模组管理页面模组信息的展示方式 (#4621)
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,6 +16,8 @@ NVIDIA
|
|||||||
minecraft-exported-crash-info*
|
minecraft-exported-crash-info*
|
||||||
hmcl-exported-logs-*
|
hmcl-exported-logs-*
|
||||||
/.java/
|
/.java/
|
||||||
|
/.local/
|
||||||
|
/.cache/
|
||||||
|
|
||||||
# gradle build
|
# gradle build
|
||||||
/build/
|
/build/
|
||||||
|
|||||||
@@ -33,9 +33,8 @@ import org.jackhuang.hmcl.util.AggregatedObservableList;
|
|||||||
public class TwoLineListItem extends VBox {
|
public class TwoLineListItem extends VBox {
|
||||||
private static final String DEFAULT_STYLE_CLASS = "two-line-list-item";
|
private static final String DEFAULT_STYLE_CLASS = "two-line-list-item";
|
||||||
|
|
||||||
public static Label createTagLabel(String tag) {
|
private static Label createTagLabel(String tag) {
|
||||||
Label tagLabel = new Label();
|
Label tagLabel = new Label();
|
||||||
tagLabel.getStyleClass().add("tag");
|
|
||||||
tagLabel.setText(tag);
|
tagLabel.setText(tag);
|
||||||
HBox.setMargin(tagLabel, new Insets(0, 8, 0, 0));
|
HBox.setMargin(tagLabel, new Insets(0, 8, 0, 0));
|
||||||
return tagLabel;
|
return tagLabel;
|
||||||
@@ -111,7 +110,15 @@ public class TwoLineListItem extends VBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addTag(String tag) {
|
public void addTag(String tag) {
|
||||||
getTags().add(createTagLabel(tag));
|
Label tagLabel = createTagLabel(tag);
|
||||||
|
tagLabel.getStyleClass().add("tag");
|
||||||
|
getTags().add(tagLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTagWarning(String tag) {
|
||||||
|
Label tagLabel = createTagLabel(tag);
|
||||||
|
tagLabel.getStyleClass().add("tag-warning");
|
||||||
|
getTags().add(tagLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableList<Label> getTags() {
|
public ObservableList<Label> getTags() {
|
||||||
|
|||||||
@@ -712,10 +712,7 @@ public class TerracottaControllerPage extends StackPane {
|
|||||||
TwoLineListItem item = new TwoLineListItem();
|
TwoLineListItem item = new TwoLineListItem();
|
||||||
item.setTitle(profile.getName());
|
item.setTitle(profile.getName());
|
||||||
item.setSubtitle(profile.getVendor());
|
item.setSubtitle(profile.getVendor());
|
||||||
item.getTags().setAll(TwoLineListItem.createTagLabel(
|
item.addTag(i18n("terracotta.player_kind." + profile.getType().name().toLowerCase(Locale.ROOT)));
|
||||||
i18n("terracotta.player_kind." + profile.getType().name().toLowerCase(Locale.ROOT)))
|
|
||||||
);
|
|
||||||
|
|
||||||
pane.getChildren().add(item);
|
pane.getChildren().add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
@@ -27,6 +26,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
|||||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.mod.LocalModFile;
|
import org.jackhuang.hmcl.mod.LocalModFile;
|
||||||
|
import org.jackhuang.hmcl.mod.ModLoaderType;
|
||||||
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;
|
||||||
@@ -44,19 +44,22 @@ import java.io.UncheckedIOException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
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, PageAware {
|
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 final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
private ModManager modManager;
|
private ModManager modManager;
|
||||||
private LibraryAnalyzer libraryAnalyzer;
|
|
||||||
private Profile profile;
|
private Profile profile;
|
||||||
private String versionId;
|
private String instanceId;
|
||||||
|
private String gameVersion;
|
||||||
|
|
||||||
|
final EnumSet<ModLoaderType> supportedLoaders = EnumSet.noneOf(ModLoaderType.class);
|
||||||
|
|
||||||
public ModListPage() {
|
public ModListPage() {
|
||||||
FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> {
|
FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> {
|
||||||
@@ -83,34 +86,76 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
@Override
|
@Override
|
||||||
public void loadVersion(Profile profile, String id) {
|
public void loadVersion(Profile profile, String id) {
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.versionId = id;
|
this.instanceId = id;
|
||||||
|
|
||||||
HMCLGameRepository repository = profile.getRepository();
|
HMCLGameRepository repository = profile.getRepository();
|
||||||
Version resolved = repository.getResolvedPreservingPatchesVersion(id);
|
Version resolved = repository.getResolvedPreservingPatchesVersion(id);
|
||||||
libraryAnalyzer = LibraryAnalyzer.analyze(resolved, repository.getGameVersion(resolved).orElse(null));
|
this.gameVersion = repository.getGameVersion(resolved).orElse(null);
|
||||||
modded.set(libraryAnalyzer.hasModLoader());
|
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion);
|
||||||
|
modded.set(analyzer.hasModLoader());
|
||||||
loadMods(profile.getRepository().getModManager(id));
|
loadMods(profile.getRepository().getModManager(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<?> loadMods(ModManager modManager) {
|
private void loadMods(ModManager modManager) {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
this.modManager = modManager;
|
this.modManager = modManager;
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
CompletableFuture.supplyAsync(() -> {
|
||||||
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
synchronized (ModListPage.this) {
|
modManager.refreshMods();
|
||||||
runInFX(() -> loadingProperty().set(true));
|
return modManager.getMods();
|
||||||
modManager.refreshMods();
|
|
||||||
return new ArrayList<>(modManager.getMods());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}, Schedulers.defaultScheduler()).whenCompleteAsync((list, exception) -> {
|
}, Schedulers.io()).whenCompleteAsync((list, exception) -> {
|
||||||
loadingProperty().set(false);
|
updateSupportedLoaders(modManager);
|
||||||
if (exception == null)
|
|
||||||
itemsProperty().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).sorted().collect(Collectors.toList()));
|
if (exception == null) {
|
||||||
else
|
getItems().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).toList());
|
||||||
getProperties().remove(ModListPage.class);
|
} else {
|
||||||
}, Platform::runLater);
|
LOG.warning("Failed to load mods", exception);
|
||||||
|
getItems().clear();
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}, Schedulers.javafx());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSupportedLoaders(ModManager modManager) {
|
||||||
|
supportedLoaders.clear();
|
||||||
|
|
||||||
|
LibraryAnalyzer analyzer = modManager.getLibraryAnalyzer();
|
||||||
|
if (analyzer == null) {
|
||||||
|
Collections.addAll(supportedLoaders, ModLoaderType.values());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) {
|
||||||
|
if (type.isModLoader() && analyzer.has(type)) {
|
||||||
|
ModLoaderType modLoaderType = type.getModLoaderType();
|
||||||
|
if (modLoaderType != null) {
|
||||||
|
supportedLoaders.add(modLoaderType);
|
||||||
|
|
||||||
|
if (modLoaderType == ModLoaderType.CLEANROOM)
|
||||||
|
supportedLoaders.add(ModLoaderType.FORGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analyzer.has(LibraryAnalyzer.LibraryType.NEO_FORGE) && "1.20.1".equals(gameVersion)) {
|
||||||
|
supportedLoaders.add(ModLoaderType.FORGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analyzer.has(LibraryAnalyzer.LibraryType.QUILT)) {
|
||||||
|
supportedLoaders.add(ModLoaderType.FABRIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analyzer.has(LibraryAnalyzer.LibraryType.FABRIC) && modManager.hasMod("kilt", ModLoaderType.FABRIC)) {
|
||||||
|
supportedLoaders.add(ModLoaderType.FORGE);
|
||||||
|
supportedLoaders.add(ModLoaderType.NEO_FORGED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add() {
|
public void add() {
|
||||||
@@ -148,7 +193,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
try {
|
try {
|
||||||
modManager.removeMods(selectedItems.stream()
|
modManager.removeMods(selectedItems.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
@@ -160,14 +205,14 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
void enableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
.forEach(info -> info.setActive(true));
|
.forEach(info -> info.setActive(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
void disableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
|
||||||
selectedItems.stream()
|
selectedItems.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
.map(ModListPageSkin.ModInfoObject::getModInfo)
|
||||||
@@ -175,13 +220,13 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openModFolder() {
|
public void openModFolder() {
|
||||||
FXUtils.openFolder(profile.getRepository().getRunDirectory(versionId).resolve("mods"));
|
FXUtils.openFolder(profile.getRepository().getRunDirectory(instanceId).resolve("mods"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkUpdates() {
|
public void checkUpdates() {
|
||||||
Runnable action = () -> Controllers.taskDialog(Task
|
Runnable action = () -> Controllers.taskDialog(Task
|
||||||
.composeAsync(() -> {
|
.composeAsync(() -> {
|
||||||
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
Optional<String> gameVersion = profile.getRepository().getGameVersion(instanceId);
|
||||||
if (gameVersion.isPresent()) {
|
if (gameVersion.isPresent()) {
|
||||||
return new ModCheckUpdatesTask(gameVersion.get(), modManager.getMods());
|
return new ModCheckUpdatesTask(gameVersion.get(), modManager.getMods());
|
||||||
}
|
}
|
||||||
@@ -199,7 +244,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
.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)) {
|
if (profile.getRepository().isModpack(instanceId)) {
|
||||||
Controllers.confirm(
|
Controllers.confirm(
|
||||||
i18n("mods.update_modpack_mod.warning"), null,
|
i18n("mods.update_modpack_mod.warning"), null,
|
||||||
MessageDialogPane.MessageType.WARNING,
|
MessageDialogPane.MessageType.WARNING,
|
||||||
@@ -210,7 +255,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void download() {
|
public void download() {
|
||||||
Controllers.getDownloadPage().showModDownloads().selectVersion(versionId);
|
Controllers.getDownloadPage().showModDownloads().selectVersion(instanceId);
|
||||||
Controllers.navigate(Controllers.getDownloadPage());
|
Controllers.navigate(Controllers.getDownloadPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +284,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
return this.profile;
|
return this.profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersionId() {
|
public String getInstanceId() {
|
||||||
return this.versionId;
|
return this.instanceId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
|
|||||||
import javafx.animation.PauseTransition;
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.css.PseudoClass;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.control.SelectionMode;
|
|
||||||
import javafx.scene.control.SkinBase;
|
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
@@ -74,14 +73,12 @@ import java.nio.file.Path;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
|
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2;
|
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2;
|
||||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
import static org.jackhuang.hmcl.util.StringUtils.isNotBlank;
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
@@ -98,6 +95,9 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
// FXThread
|
// FXThread
|
||||||
private boolean isSearching = false;
|
private boolean isSearching = false;
|
||||||
|
|
||||||
|
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||||
|
private final ChangeListener<Boolean> holder;
|
||||||
|
|
||||||
ModListPageSkin(ModListPage skinnable) {
|
ModListPageSkin(ModListPage skinnable) {
|
||||||
super(skinnable);
|
super(skinnable);
|
||||||
|
|
||||||
@@ -109,6 +109,12 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
root.getStyleClass().add("no-padding");
|
root.getStyleClass().add("no-padding");
|
||||||
listView = new JFXListView<>();
|
listView = new JFXListView<>();
|
||||||
|
|
||||||
|
this.holder = FXUtils.onWeakChange(skinnable.loadingProperty(), loading -> {
|
||||||
|
if (!loading) {
|
||||||
|
listView.scrollTo(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
toolbarPane = new TransitionPane();
|
toolbarPane = new TransitionPane();
|
||||||
|
|
||||||
@@ -276,7 +282,7 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
|| predicate.test(modInfo.getGameVersion())
|
|| predicate.test(modInfo.getGameVersion())
|
||||||
|| predicate.test(modInfo.getId())
|
|| predicate.test(modInfo.getId())
|
||||||
|| predicate.test(Objects.toString(modInfo.getModLoaderType()))
|
|| predicate.test(Objects.toString(modInfo.getModLoaderType()))
|
||||||
|| predicate.test((item.getMod() != null ? item.getMod().getDisplayName() : null))) {
|
|| predicate.test((item.getModTranslations() != null ? item.getModTranslations().getDisplayName() : null))) {
|
||||||
listView.getItems().add(item);
|
listView.getItems().add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,9 +371,7 @@ final 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 LocalModFile localModFile;
|
private final LocalModFile localModFile;
|
||||||
private final String title;
|
private final @Nullable ModTranslations.Mod modTranslations;
|
||||||
private final String message;
|
|
||||||
private final ModTranslations.Mod mod;
|
|
||||||
|
|
||||||
private SoftReference<Image> iconCache;
|
private SoftReference<Image> iconCache;
|
||||||
|
|
||||||
@@ -375,36 +379,15 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
this.localModFile = localModFile;
|
this.localModFile = localModFile;
|
||||||
this.active = localModFile.activeProperty();
|
this.active = localModFile.activeProperty();
|
||||||
|
|
||||||
this.title = localModFile.getName();
|
this.modTranslations = ModTranslations.MOD.getMod(localModFile.getId(), localModFile.getName());
|
||||||
|
|
||||||
List<String> parts = new ArrayList<>();
|
|
||||||
if (isNotBlank(localModFile.getId())) {
|
|
||||||
parts.add(localModFile.getId());
|
|
||||||
}
|
|
||||||
if (isNotBlank(localModFile.getVersion())) {
|
|
||||||
parts.add(localModFile.getVersion());
|
|
||||||
}
|
|
||||||
if (isNotBlank(localModFile.getGameVersion())) {
|
|
||||||
parts.add(i18n("game.version") + ": " + localModFile.getGameVersion());
|
|
||||||
}
|
|
||||||
this.message = String.join(", ", parts);
|
|
||||||
this.mod = ModTranslations.MOD.getMod(localModFile.getId(), localModFile.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getSubtitle() {
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalModFile getModInfo() {
|
LocalModFile getModInfo() {
|
||||||
return localModFile;
|
return localModFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModTranslations.Mod getMod() {
|
public @Nullable ModTranslations.Mod getModTranslations() {
|
||||||
return mod;
|
return modTranslations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -431,23 +414,23 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
TwoLineListItem title = new TwoLineListItem();
|
TwoLineListItem title = new TwoLineListItem();
|
||||||
title.setTitle(modInfo.getModInfo().getName());
|
if (modInfo.getModTranslations() != null && I18n.isUseChinese())
|
||||||
if (modInfo.getMod() != null) {
|
title.setTitle(modInfo.getModTranslations().getDisplayName());
|
||||||
title.addTag(modInfo.getMod().getDisplayName());
|
else
|
||||||
}
|
title.setTitle(modInfo.getModInfo().getName());
|
||||||
|
|
||||||
List<String> subtitleParts = new ArrayList<>();
|
StringJoiner subtitle = new StringJoiner(" | ");
|
||||||
subtitleParts.add(FileUtils.getName(modInfo.getModInfo().getFile()));
|
subtitle.add(FileUtils.getName(modInfo.getModInfo().getFile()));
|
||||||
if (StringUtils.isNotBlank(modInfo.getModInfo().getGameVersion())) {
|
if (StringUtils.isNotBlank(modInfo.getModInfo().getGameVersion())) {
|
||||||
subtitleParts.add(modInfo.getModInfo().getGameVersion());
|
subtitle.add(modInfo.getModInfo().getGameVersion());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(modInfo.getModInfo().getVersion())) {
|
if (StringUtils.isNotBlank(modInfo.getModInfo().getVersion())) {
|
||||||
subtitleParts.add(modInfo.getModInfo().getVersion());
|
subtitle.add(modInfo.getModInfo().getVersion());
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(modInfo.getModInfo().getAuthors())) {
|
if (StringUtils.isNotBlank(modInfo.getModInfo().getAuthors())) {
|
||||||
subtitleParts.add(i18n("archive.author") + ": " + modInfo.getModInfo().getAuthors());
|
subtitle.add(i18n("archive.author") + ": " + modInfo.getModInfo().getAuthors());
|
||||||
}
|
}
|
||||||
title.setSubtitle(String.join(", ", subtitleParts));
|
title.setSubtitle(subtitle.toString());
|
||||||
|
|
||||||
titleContainer.getChildren().setAll(FXUtils.limitingSize(imageView, 40, 40), title);
|
titleContainer.getChildren().setAll(FXUtils.limitingSize(imageView, 40, 40), title);
|
||||||
setHeading(titleContainer);
|
setHeading(titleContainer);
|
||||||
@@ -517,7 +500,7 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
Controllers.navigate(new DownloadPage(
|
Controllers.navigate(new DownloadPage(
|
||||||
repository instanceof CurseForgeRemoteModRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false),
|
repository instanceof CurseForgeRemoteModRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false),
|
||||||
remoteMod,
|
remoteMod,
|
||||||
new Profile.ProfileVersion(ModListPageSkin.this.getSkinnable().getProfile(), ModListPageSkin.this.getSkinnable().getVersionId()),
|
new Profile.ProfileVersion(ModListPageSkin.this.getSkinnable().getProfile(), ModListPageSkin.this.getSkinnable().getInstanceId()),
|
||||||
(profile, version, file) -> org.jackhuang.hmcl.ui.download.DownloadPage.download(profile, version, file, "mods")
|
(profile, version, file) -> org.jackhuang.hmcl.ui.download.DownloadPage.download(profile, version, file, "mods")
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
@@ -540,7 +523,7 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
getActions().add(officialPageButton);
|
getActions().add(officialPageButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modInfo.getMod() == null || StringUtils.isBlank(modInfo.getMod().getMcmod())) {
|
if (modInfo.getModTranslations() == null || StringUtils.isBlank(modInfo.getModTranslations().getMcmod())) {
|
||||||
JFXHyperlink searchButton = new JFXHyperlink(i18n("mods.mcmod.search"));
|
JFXHyperlink searchButton = new JFXHyperlink(i18n("mods.mcmod.search"));
|
||||||
searchButton.setOnAction(e -> {
|
searchButton.setOnAction(e -> {
|
||||||
fireEvent(new DialogCloseEvent());
|
fireEvent(new DialogCloseEvent());
|
||||||
@@ -555,7 +538,7 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
JFXHyperlink mcmodButton = new JFXHyperlink(i18n("mods.mcmod.page"));
|
JFXHyperlink mcmodButton = new JFXHyperlink(i18n("mods.mcmod.page"));
|
||||||
mcmodButton.setOnAction(e -> {
|
mcmodButton.setOnAction(e -> {
|
||||||
fireEvent(new DialogCloseEvent());
|
fireEvent(new DialogCloseEvent());
|
||||||
FXUtils.openLink(ModTranslations.MOD.getMcmodUrl(modInfo.getMod()));
|
FXUtils.openLink(ModTranslations.MOD.getMcmodUrl(modInfo.getModTranslations()));
|
||||||
});
|
});
|
||||||
getActions().add(mcmodButton);
|
getActions().add(mcmodButton);
|
||||||
}
|
}
|
||||||
@@ -574,6 +557,8 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
private static final Lazy<JFXPopup> popup = new Lazy<>(() -> new JFXPopup(menu.get()));
|
private static final Lazy<JFXPopup> popup = new Lazy<>(() -> new JFXPopup(menu.get()));
|
||||||
|
|
||||||
final class ModInfoListCell extends MDListCell<ModInfoObject> {
|
final class ModInfoListCell extends MDListCell<ModInfoObject> {
|
||||||
|
private static final PseudoClass WARNING = PseudoClass.getPseudoClass("warning");
|
||||||
|
|
||||||
JFXCheckBox checkBox = new JFXCheckBox();
|
JFXCheckBox checkBox = new JFXCheckBox();
|
||||||
ImageView imageView = new ImageView();
|
ImageView imageView = new ImageView();
|
||||||
TwoLineListItem content = new TwoLineListItem();
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
@@ -582,9 +567,13 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
JFXButton revealButton = new JFXButton();
|
JFXButton revealButton = new JFXButton();
|
||||||
BooleanProperty booleanProperty;
|
BooleanProperty booleanProperty;
|
||||||
|
|
||||||
|
Tooltip warningTooltip;
|
||||||
|
|
||||||
ModInfoListCell(JFXListView<ModInfoObject> listView, Holder<Object> lastCell) {
|
ModInfoListCell(JFXListView<ModInfoObject> listView, Holder<Object> lastCell) {
|
||||||
super(listView, lastCell);
|
super(listView, lastCell);
|
||||||
|
|
||||||
|
this.getStyleClass().add("mod-info-list-cell");
|
||||||
|
|
||||||
HBox container = new HBox(8);
|
HBox container = new HBox(8);
|
||||||
container.setPickOnBounds(false);
|
container.setPickOnBounds(false);
|
||||||
container.setAlignment(Pos.CENTER_LEFT);
|
container.setAlignment(Pos.CENTER_LEFT);
|
||||||
@@ -616,68 +605,104 @@ final class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateControl(ModInfoObject dataItem, boolean empty) {
|
protected void updateControl(ModInfoObject dataItem, boolean empty) {
|
||||||
|
pseudoClassStateChanged(WARNING, false);
|
||||||
|
if (warningTooltip != null) {
|
||||||
|
Tooltip.uninstall(this, warningTooltip);
|
||||||
|
warningTooltip = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (empty) return;
|
if (empty) return;
|
||||||
|
|
||||||
|
List<String> warning = new ArrayList<>();
|
||||||
|
|
||||||
|
content.getTags().clear();
|
||||||
|
|
||||||
|
LocalModFile modInfo = dataItem.getModInfo();
|
||||||
|
ModTranslations.Mod modTranslations = dataItem.getModTranslations();
|
||||||
|
|
||||||
SoftReference<Image> iconCache = dataItem.iconCache;
|
SoftReference<Image> iconCache = dataItem.iconCache;
|
||||||
Image icon;
|
Image icon;
|
||||||
if (iconCache != null && (icon = iconCache.get()) != null) {
|
if (iconCache != null && (icon = iconCache.get()) != null) {
|
||||||
imageView.setImage(icon);
|
imageView.setImage(icon);
|
||||||
} else {
|
} else {
|
||||||
loadModIcon(dataItem.getModInfo(), 24)
|
loadModIcon(modInfo, 24)
|
||||||
.whenComplete(Schedulers.javafx(), (image, exception) -> {
|
.whenComplete(Schedulers.javafx(), (image, exception) -> {
|
||||||
dataItem.iconCache = new SoftReference<>(image);
|
dataItem.iconCache = new SoftReference<>(image);
|
||||||
imageView.setImage(image);
|
imageView.setImage(image);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
content.setTitle(dataItem.getTitle());
|
if (modTranslations != null && I18n.isUseChinese() && !modInfo.getName().equals(modTranslations.getName()))
|
||||||
content.getTags().clear();
|
content.setTitle(modInfo.getName() + " (" + modTranslations.getName() + ")");
|
||||||
switch (dataItem.getModInfo().getModLoaderType()) {
|
else
|
||||||
case FORGE:
|
content.setTitle(modInfo.getName());
|
||||||
content.addTag(i18n("install.installer.forge"));
|
|
||||||
break;
|
StringJoiner joiner = new StringJoiner(" | ");
|
||||||
case CLEANROOM:
|
|
||||||
content.addTag(i18n("install.installer.cleanroom"));
|
if (StringUtils.isNotBlank(modInfo.getId()))
|
||||||
break;
|
joiner.add(modInfo.getId());
|
||||||
case NEO_FORGED:
|
|
||||||
content.addTag(i18n("install.installer.neoforge"));
|
joiner.add(FileUtils.getName(modInfo.getFile()));
|
||||||
break;
|
|
||||||
case FABRIC:
|
content.setSubtitle(joiner.toString());
|
||||||
content.addTag(i18n("install.installer.fabric"));
|
|
||||||
break;
|
ModLoaderType modLoaderType = modInfo.getModLoaderType();
|
||||||
case LITE_LOADER:
|
if (!ModListPageSkin.this.getSkinnable().supportedLoaders.contains(modLoaderType)) {
|
||||||
content.addTag(i18n("install.installer.liteloader"));
|
warning.add(i18n("mods.warning.loader_mismatch"));
|
||||||
break;
|
switch (dataItem.getModInfo().getModLoaderType()) {
|
||||||
case QUILT:
|
case FORGE:
|
||||||
content.addTag(i18n("install.installer.quilt"));
|
content.addTagWarning(i18n("install.installer.forge"));
|
||||||
break;
|
break;
|
||||||
}
|
case CLEANROOM:
|
||||||
if (dataItem.getMod() != null && I18n.isUseChinese()) {
|
content.addTagWarning(i18n("install.installer.cleanroom"));
|
||||||
if (isNotBlank(dataItem.getSubtitle())) {
|
break;
|
||||||
content.setSubtitle(dataItem.getSubtitle() + ", " + dataItem.getMod().getDisplayName());
|
case NEO_FORGED:
|
||||||
} else {
|
content.addTagWarning(i18n("install.installer.neoforge"));
|
||||||
content.setSubtitle(dataItem.getMod().getDisplayName());
|
break;
|
||||||
|
case FABRIC:
|
||||||
|
content.addTagWarning(i18n("install.installer.fabric"));
|
||||||
|
break;
|
||||||
|
case LITE_LOADER:
|
||||||
|
content.addTagWarning(i18n("install.installer.liteloader"));
|
||||||
|
break;
|
||||||
|
case QUILT:
|
||||||
|
content.addTagWarning(i18n("install.installer.quilt"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
content.setSubtitle(dataItem.getSubtitle());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String modVersion = modInfo.getVersion();
|
||||||
|
if (StringUtils.isNotBlank(modVersion) && !"${version}".equals(modVersion)) {
|
||||||
|
content.addTag(modVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (booleanProperty != null) {
|
if (booleanProperty != null) {
|
||||||
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
|
checkBox.selectedProperty().unbindBidirectional(booleanProperty);
|
||||||
}
|
}
|
||||||
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
|
checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active);
|
||||||
restoreButton.setVisible(!dataItem.getModInfo().getMod().getOldFiles().isEmpty());
|
restoreButton.setVisible(!modInfo.getMod().getOldFiles().isEmpty());
|
||||||
restoreButton.setOnAction(e -> {
|
restoreButton.setOnAction(e -> {
|
||||||
menu.get().getContent().setAll(dataItem.getModInfo().getMod().getOldFiles().stream()
|
menu.get().getContent().setAll(modInfo.getMod().getOldFiles().stream()
|
||||||
.map(localModFile -> new IconedMenuItem(null, localModFile.getVersion(),
|
.map(localModFile -> new IconedMenuItem(null, localModFile.getVersion(),
|
||||||
() -> getSkinnable().rollback(dataItem.getModInfo(), localModFile),
|
() -> getSkinnable().rollback(modInfo, localModFile),
|
||||||
popup.get()))
|
popup.get()))
|
||||||
.collect(Collectors.toList())
|
.toList()
|
||||||
);
|
);
|
||||||
|
|
||||||
popup.get().show(restoreButton, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, restoreButton.getHeight());
|
popup.get().show(restoreButton, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, 0, restoreButton.getHeight());
|
||||||
});
|
});
|
||||||
revealButton.setOnAction(e -> FXUtils.showFileInExplorer(dataItem.getModInfo().getFile()));
|
revealButton.setOnAction(e -> FXUtils.showFileInExplorer(modInfo.getFile()));
|
||||||
infoButton.setOnAction(e -> Controllers.dialog(new ModInfoDialog(dataItem)));
|
infoButton.setOnAction(e -> Controllers.dialog(new ModInfoDialog(dataItem)));
|
||||||
|
|
||||||
|
if (!warning.isEmpty()) {
|
||||||
|
pseudoClassStateChanged(WARNING, true);
|
||||||
|
|
||||||
|
//noinspection ConstantValue
|
||||||
|
this.warningTooltip = warning.size() == 1
|
||||||
|
? new Tooltip(warning.get(0))
|
||||||
|
: new Tooltip(String.join("\n", warning));
|
||||||
|
FXUtils.installFastTooltip(this, warningTooltip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ public enum ModTranslations {
|
|||||||
modIdMap = new HashMap<>(mods.size());
|
modIdMap = new HashMap<>(mods.size());
|
||||||
for (Mod mod : mods) {
|
for (Mod mod : mods) {
|
||||||
for (String id : mod.getModIds()) {
|
for (String id : mod.getModIds()) {
|
||||||
if (StringUtils.isNotBlank(id) && !"examplemod".equals(id)) {
|
if (StringUtils.isNotBlank(id)) {
|
||||||
modIdMap.put(id, mod);
|
modIdMap.putIfAbsent(id, mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ public enum ModTranslations {
|
|||||||
for (Mod mod : mods) {
|
for (Mod mod : mods) {
|
||||||
String subname = cleanSubname(mod.getSubname());
|
String subname = cleanSubname(mod.getSubname());
|
||||||
if (StringUtils.isNotBlank(subname)) {
|
if (StringUtils.isNotBlank(subname)) {
|
||||||
subnameMap.put(subname, mod);
|
subnameMap.putIfAbsent(subname, mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ public enum ModTranslations {
|
|||||||
curseForgeMap = new HashMap<>(mods.size());
|
curseForgeMap = new HashMap<>(mods.size());
|
||||||
for (Mod mod : mods) {
|
for (Mod mod : mods) {
|
||||||
if (StringUtils.isNotBlank(mod.getCurseforge())) {
|
if (StringUtils.isNotBlank(mod.getCurseforge())) {
|
||||||
curseForgeMap.put(mod.getCurseforge(), mod);
|
curseForgeMap.putIfAbsent(mod.getCurseforge(), mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,6 +293,14 @@
|
|||||||
-fx-font-size: 12px;
|
-fx-font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.two-line-list-item > .first-line > .tag-warning {
|
||||||
|
-fx-text-fill: #d34336;;
|
||||||
|
-fx-background-color: #f1aeb5;
|
||||||
|
-fx-padding: 2;
|
||||||
|
-fx-font-weight: normal;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.two-line-item-second-large {
|
.two-line-item-second-large {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -918,6 +926,10 @@
|
|||||||
-fx-background-color: derive(-fx-base-color, 60%);
|
-fx-background-color: derive(-fx-base-color, 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mod-info-list-cell:warning {
|
||||||
|
-fx-background-color: #F8D7DA;
|
||||||
|
}
|
||||||
|
|
||||||
.options-sublist {
|
.options-sublist {
|
||||||
-fx-background-color: white;
|
-fx-background-color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1090,6 +1090,7 @@ mods.not_modded=You must install a modloader (Forge, NeoForge, Fabric, Quilt, or
|
|||||||
mods.restore=Restore
|
mods.restore=Restore
|
||||||
mods.url=Official Page
|
mods.url=Official Page
|
||||||
mods.update_modpack_mod.warning=Updating mods in a modpack can lead to irreparable results, possibly corrupting the modpack so that it cannot launch. Are you sure you want to update?
|
mods.update_modpack_mod.warning=Updating mods in a modpack can lead to irreparable results, possibly corrupting the modpack so that it cannot launch. Are you sure you want to update?
|
||||||
|
mods.warning.loader_mismatch=Mod loader mismatch
|
||||||
mods.install=Install
|
mods.install=Install
|
||||||
mods.save_as=Save As
|
mods.save_as=Save As
|
||||||
|
|
||||||
|
|||||||
@@ -887,6 +887,7 @@ mods.not_modded=你需要先在「自動安裝」頁面安裝 Forge、NeoForge
|
|||||||
mods.restore=回退
|
mods.restore=回退
|
||||||
mods.url=官方頁面
|
mods.url=官方頁面
|
||||||
mods.update_modpack_mod.warning=更新模組包中的模組可能導致模組包損壞,使模組包無法正常啟動。該操作不可逆,確定要更新嗎?
|
mods.update_modpack_mod.warning=更新模組包中的模組可能導致模組包損壞,使模組包無法正常啟動。該操作不可逆,確定要更新嗎?
|
||||||
|
mods.warning.loader_mismatch=模組加載器不匹配
|
||||||
mods.install=安裝到目前實例
|
mods.install=安裝到目前實例
|
||||||
mods.save_as=下載到本機目錄
|
mods.save_as=下載到本機目錄
|
||||||
|
|
||||||
|
|||||||
@@ -897,6 +897,7 @@ mods.not_modded=你需要先在“自动安装”页面安装 Forge、NeoForge
|
|||||||
mods.restore=回退
|
mods.restore=回退
|
||||||
mods.url=官方页面
|
mods.url=官方页面
|
||||||
mods.update_modpack_mod.warning=更新整合包中的模组可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
|
mods.update_modpack_mod.warning=更新整合包中的模组可能导致整合包损坏,使整合包无法正常启动。该操作不可逆,确定要更新吗?
|
||||||
|
mods.warning.loader_mismatch=模组加载器不匹配
|
||||||
mods.install=安装到当前实例
|
mods.install=安装到当前实例
|
||||||
mods.save_as=下载到本地文件夹
|
mods.save_as=下载到本地文件夹
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.StringUtils;
|
|||||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
@@ -62,7 +63,7 @@ public final class ModManager {
|
|||||||
private final GameRepository repository;
|
private final GameRepository repository;
|
||||||
private final String id;
|
private final String id;
|
||||||
private final TreeSet<LocalModFile> localModFiles = new TreeSet<>();
|
private final TreeSet<LocalModFile> localModFiles = new TreeSet<>();
|
||||||
private final HashMap<LocalMod, LocalMod> localMods = new HashMap<>();
|
private final HashMap<Pair<String, ModLoaderType>, LocalMod> localMods = new HashMap<>();
|
||||||
private LibraryAnalyzer analyzer;
|
private LibraryAnalyzer analyzer;
|
||||||
|
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
@@ -84,8 +85,17 @@ public final class ModManager {
|
|||||||
return repository.getModsDirectory(id);
|
return repository.getModsDirectory(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) {
|
public LibraryAnalyzer getLibraryAnalyzer() {
|
||||||
return localMods.computeIfAbsent(new LocalMod(id, modLoaderType), x -> x);
|
return analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalMod getLocalMod(String modId, ModLoaderType modLoaderType) {
|
||||||
|
return localMods.computeIfAbsent(pair(modId, modLoaderType),
|
||||||
|
x -> new LocalMod(x.getKey(), x.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMod(String modId, ModLoaderType modLoaderType) {
|
||||||
|
return localMods.containsKey(pair(modId, modLoaderType));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addModInfo(Path file) {
|
private void addModInfo(Path file) {
|
||||||
@@ -184,10 +194,10 @@ public final class ModManager {
|
|||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<LocalModFile> getMods() throws IOException {
|
public @Unmodifiable List<LocalModFile> getMods() throws IOException {
|
||||||
if (!loaded)
|
if (!loaded)
|
||||||
refreshMods();
|
refreshMods();
|
||||||
return localModFiles;
|
return List.copyOf(localModFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMod(Path file) throws IOException {
|
public void addMod(Path file) throws IOException {
|
||||||
|
|||||||
Reference in New Issue
Block a user