Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ NVIDIA
minecraft-exported-crash-info*
hmcl-exported-logs-*
/.java/
/.local/
/.cache/

# gradle build
/build/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@
public class TwoLineListItem extends VBox {
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();
tagLabel.getStyleClass().add("tag");
tagLabel.setText(tag);
HBox.setMargin(tagLabel, new Insets(0, 8, 0, 0));
return tagLabel;
Expand Down Expand Up @@ -111,7 +110,15 @@ public void setSubtitle(String subtitle) {
}

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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,10 +712,7 @@ private void updateProfiles(List<TerracottaProfile> profiles) {
TwoLineListItem item = new TwoLineListItem();
item.setTitle(profile.getName());
item.setSubtitle(profile.getVendor());
item.getTags().setAll(TwoLineListItem.createTagLabel(
i18n("terracotta.player_kind." + profile.getType().name().toLowerCase(Locale.ROOT)))
);

item.addTag(i18n("terracotta.player_kind." + profile.getType().name().toLowerCase(Locale.ROOT)));
pane.getChildren().add(item);
}

Expand Down
107 changes: 76 additions & 31 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.ui.versions;

import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
Expand All @@ -27,6 +26,7 @@
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.ModLoaderType;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
Expand All @@ -44,19 +44,22 @@
import java.nio.file.Path;
import java.util.*;
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.i18n.I18n.i18n;

public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable, PageAware {
private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false);

private final ReentrantLock lock = new ReentrantLock();

private ModManager modManager;
private LibraryAnalyzer libraryAnalyzer;
private Profile profile;
private String versionId;
private String instanceId;
private String gameVersion;

final EnumSet<ModLoaderType> supportedLoaders = EnumSet.noneOf(ModLoaderType.class);

public ModListPage() {
FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> {
Expand All @@ -83,34 +86,76 @@ public void refresh() {
@Override
public void loadVersion(Profile profile, String id) {
this.profile = profile;
this.versionId = id;
this.instanceId = id;

HMCLGameRepository repository = profile.getRepository();
Version resolved = repository.getResolvedPreservingPatchesVersion(id);
libraryAnalyzer = LibraryAnalyzer.analyze(resolved, repository.getGameVersion(resolved).orElse(null));
modded.set(libraryAnalyzer.hasModLoader());
this.gameVersion = repository.getGameVersion(resolved).orElse(null);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion);
modded.set(analyzer.hasModLoader());
loadMods(profile.getRepository().getModManager(id));
}

private CompletableFuture<?> loadMods(ModManager modManager) {
private void loadMods(ModManager modManager) {
setLoading(true);

this.modManager = modManager;
return CompletableFuture.supplyAsync(() -> {
CompletableFuture.supplyAsync(() -> {
lock.lock();
try {
synchronized (ModListPage.this) {
runInFX(() -> loadingProperty().set(true));
modManager.refreshMods();
return new ArrayList<>(modManager.getMods());
}
modManager.refreshMods();
return modManager.getMods();
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
lock.unlock();
}
}, Schedulers.io()).whenCompleteAsync((list, exception) -> {
updateSupportedLoaders(modManager);

if (exception == null) {
getItems().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).toList());
} else {
LOG.warning("Failed to load mods", exception);
getItems().clear();
}
}, Schedulers.defaultScheduler()).whenCompleteAsync((list, exception) -> {
loadingProperty().set(false);
if (exception == null)
itemsProperty().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).sorted().collect(Collectors.toList()));
else
getProperties().remove(ModListPage.class);
}, Platform::runLater);
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() {
Expand Down Expand Up @@ -148,7 +193,7 @@ public void add() {
}).start();
}

public void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
try {
modManager.removeMods(selectedItems.stream()
.filter(Objects::nonNull)
Expand All @@ -160,28 +205,28 @@ public void removeSelected(ObservableList<ModListPageSkin.ModInfoObject> selecte
}
}

public void enableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
void enableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
selectedItems.stream()
.filter(Objects::nonNull)
.map(ModListPageSkin.ModInfoObject::getModInfo)
.forEach(info -> info.setActive(true));
}

public void disableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
void disableSelected(ObservableList<ModListPageSkin.ModInfoObject> selectedItems) {
selectedItems.stream()
.filter(Objects::nonNull)
.map(ModListPageSkin.ModInfoObject::getModInfo)
.forEach(info -> info.setActive(false));
}

public void openModFolder() {
FXUtils.openFolder(profile.getRepository().getRunDirectory(versionId).resolve("mods"));
FXUtils.openFolder(profile.getRepository().getRunDirectory(instanceId).resolve("mods"));
}

public void checkUpdates() {
Runnable action = () -> Controllers.taskDialog(Task
.composeAsync(() -> {
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
Optional<String> gameVersion = profile.getRepository().getGameVersion(instanceId);
if (gameVersion.isPresent()) {
return new ModCheckUpdatesTask(gameVersion.get(), modManager.getMods());
}
Expand All @@ -199,7 +244,7 @@ public void checkUpdates() {
.withStagesHint(Collections.singletonList("mods.check_updates")),
i18n("update.checking"), TaskCancellationAction.NORMAL);

if (profile.getRepository().isModpack(versionId)) {
if (profile.getRepository().isModpack(instanceId)) {
Controllers.confirm(
i18n("mods.update_modpack_mod.warning"), null,
MessageDialogPane.MessageType.WARNING,
Expand All @@ -210,7 +255,7 @@ public void checkUpdates() {
}

public void download() {
Controllers.getDownloadPage().showModDownloads().selectVersion(versionId);
Controllers.getDownloadPage().showModDownloads().selectVersion(instanceId);
Controllers.navigate(Controllers.getDownloadPage());
}

Expand Down Expand Up @@ -239,7 +284,7 @@ public Profile getProfile() {
return this.profile;
}

public String getVersionId() {
return this.versionId;
public String getInstanceId() {
return this.instanceId;
}
}
Loading