diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index df605c10c01..3ea5b48c221 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -26,10 +26,10 @@ import org.jackhuang.hmcl.event.Event; import org.jackhuang.hmcl.event.EventManager; import org.jackhuang.hmcl.java.JavaRuntime; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionIconType; import org.jackhuang.hmcl.setting.VersionSetting; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index 5a333eed3fa..3dc615413f2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -20,10 +20,10 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.MinecraftInstanceTask; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackInstallTask; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManifest.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManifest.java index ba091d5ac31..aaecceb8d85 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManifest.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackManifest.java @@ -17,8 +17,8 @@ */ package org.jackhuang.hmcl.game; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; public final class HMCLModpackManifest implements ModpackManifest { public static final HMCLModpackManifest INSTANCE = new HMCLModpackManifest(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java index b96bd92c1ce..16826e29eec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java @@ -20,10 +20,10 @@ import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackProvider; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 4800d1ad645..8b7dd85c926 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -31,9 +31,9 @@ import org.jackhuang.hmcl.java.JavaManager; import org.jackhuang.hmcl.java.JavaRuntime; import org.jackhuang.hmcl.launch.*; -import org.jackhuang.hmcl.mod.ModpackCompletionException; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackCompletionException; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.*; import org.jackhuang.hmcl.ui.*; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteModRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java similarity index 82% rename from HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteModRepository.java rename to HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java index ff90b67a994..9b56911059d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteModRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java @@ -18,8 +18,8 @@ package org.jackhuang.hmcl.game; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.ui.versions.ModTranslations; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; @@ -31,12 +31,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public abstract class LocalizedRemoteModRepository implements RemoteModRepository { +public abstract class LocalizedRemoteAddonRepository implements RemoteAddonRepository { private static final int CONTAIN_CHINESE_WEIGHT = 10; private static final int INITIAL_CAPACITY = 16; - protected abstract RemoteModRepository getBackedRemoteModRepository(); + protected abstract RemoteAddonRepository getBackedRemoteModRepository(); protected abstract SortType getBackedRemoteModRepositorySortOrder(); @@ -63,30 +63,30 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion return getBackedRemoteModRepository().search(downloadProvider, gameVersion, category, pageOffset, pageSize, searchFilter, sort, sortOrder); } - RemoteMod[] searchResultArray = new RemoteMod[pageSize]; + RemoteAddon[] searchResultArray = new RemoteAddon[pageSize]; int totalPages, chineseIndex = 0, englishIndex = pageSize - 1; { SearchResult searchResult = null; - List remoteMods = List.of(); + List remoteAddons = List.of(); for (String englishSearchFilter : englishSearchFiltersSet) { searchResult = getBackedRemoteModRepository().search(downloadProvider, gameVersion, category, pageOffset, pageSize, englishSearchFilter, getBackedRemoteModRepositorySortOrder(), sortOrder); - remoteMods = searchResult.getUnsortedResults().toList(); - if (!remoteMods.isEmpty()) { + remoteAddons = searchResult.getUnsortedResults().toList(); + if (!remoteAddons.isEmpty()) { break; } } - for (RemoteMod remoteMod : remoteMods) { + for (RemoteAddon remoteAddon : remoteAddons) { if (chineseIndex > englishIndex) { LOG.warning("Too many search results! Are the backed remote mod repository broken? Or are the API broken?"); continue; } - ModTranslations.Mod chineseTranslation = ModTranslations.getTranslationsByRepositoryType(getType()).getModByCurseForgeId(remoteMod.getSlug()); + ModTranslations.Mod chineseTranslation = ModTranslations.getTranslationsByRepositoryType(getType()).getModByCurseForgeId(remoteAddon.getSlug()); if (chineseTranslation != null && !StringUtils.isBlank(chineseTranslation.getName()) && StringUtils.containsChinese(chineseTranslation.getName())) { - searchResultArray[chineseIndex++] = remoteMod; + searchResultArray[chineseIndex++] = remoteAddon; } else { - searchResultArray[englishIndex--] = remoteMod; + searchResultArray[englishIndex--] = remoteAddon; } } totalPages = searchResult.getTotalPages(); @@ -118,22 +118,22 @@ public Stream getCategories() throws IOException { } @Override - public Optional getRemoteVersionByLocalFile(Path file) throws IOException { + public Optional getRemoteVersionByLocalFile(Path file) throws IOException { return getBackedRemoteModRepository().getRemoteVersionByLocalFile(file); } @Override - public RemoteMod getModById(DownloadProvider downloadProvider, String id) throws IOException { + public RemoteAddon getModById(DownloadProvider downloadProvider, String id) throws IOException { return getBackedRemoteModRepository().getModById(downloadProvider, id); } @Override - public RemoteMod.File getModFile(String modId, String fileId) throws IOException { + public RemoteAddon.File getModFile(String modId, String fileId) throws IOException { return getBackedRemoteModRepository().getModFile(modId, fileId); } @Override - public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { + public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { return getBackedRemoteModRepository().getRemoteVersionsById(downloadProvider, id); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 109e9514209..9950f4c1e7c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -20,17 +20,17 @@ import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.mod.*; -import org.jackhuang.hmcl.mod.curse.CurseModpackProvider; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackProvider; -import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackProvider; -import org.jackhuang.hmcl.mod.multimc.MultiMCComponents; -import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; -import org.jackhuang.hmcl.mod.multimc.MultiMCModpackProvider; -import org.jackhuang.hmcl.mod.server.ServerModpackManifest; -import org.jackhuang.hmcl.mod.server.ServerModpackProvider; -import org.jackhuang.hmcl.mod.server.ServerModpackRemoteInstallTask; +import org.jackhuang.hmcl.addon.modpack.*; +import org.jackhuang.hmcl.addon.modpack.curse.CurseModpackProvider; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackManifest; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackProvider; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthModpackProvider; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCComponents; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCInstanceConfiguration; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCModpackProvider; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackManifest; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackProvider; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackRemoteInstallTask; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.VersionSetting; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java index b904a6d771f..ed8a24cd9d5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.setting; import javafx.scene.image.Image; -import org.jackhuang.hmcl.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.ui.FXUtils; public enum VersionIconType { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java index 08c390a95e5..69b718a0314 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java @@ -50,21 +50,21 @@ import org.jackhuang.hmcl.download.quilt.QuiltInstallTask; import org.jackhuang.hmcl.game.HMCLModpackInstallTask; import org.jackhuang.hmcl.java.JavaInstallTask; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.ModpackInstallTask; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; -import org.jackhuang.hmcl.mod.curse.CurseCompletionTask; -import org.jackhuang.hmcl.mod.curse.CurseInstallTask; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackCompletionTask; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; -import org.jackhuang.hmcl.mod.modrinth.ModrinthCompletionTask; -import org.jackhuang.hmcl.mod.modrinth.ModrinthInstallTask; -import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackExportTask; -import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; -import org.jackhuang.hmcl.mod.multimc.MultiMCModpackInstallTask; -import org.jackhuang.hmcl.mod.server.ServerModpackCompletionTask; -import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; -import org.jackhuang.hmcl.mod.server.ServerModpackLocalInstallTask; +import org.jackhuang.hmcl.addon.modpack.MinecraftInstanceTask; +import org.jackhuang.hmcl.addon.modpack.ModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.curse.CurseCompletionTask; +import org.jackhuang.hmcl.addon.modpack.curse.CurseInstallTask; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackCompletionTask; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthCompletionTask; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthInstallTask; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackCompletionTask; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackLocalInstallTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskListener; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index ee2cac9b3e0..151cc51d611 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -23,8 +23,8 @@ import javafx.scene.Node; import org.jackhuang.hmcl.download.*; import org.jackhuang.hmcl.download.game.GameRemoteVersion; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; @@ -71,9 +71,9 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_MOD = (downloadProvider, profile, version, mod, file) -> download(downloadProvider, profile, version, file, "mods"); public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_RESOURCE_PACK = - (downloadProvider, profile, version, mod, file) -> download(downloadProvider, profile, version, file, "resourcepacks"); + (downloadProvider, profile, version, pack, file) -> download(downloadProvider, profile, version, file, "resourcepacks"); public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_SHADER = - (downloadProvider, profile, version, mod, file) -> download(downloadProvider, profile, version, file, "shaderpacks"); + (downloadProvider, profile, version, shader, file) -> download(downloadProvider, profile, version, file, "shaderpacks"); private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1)); private final TabHeader tab; @@ -96,8 +96,8 @@ public DownloadPage(String uploadVersion) { newGameTab.setNodeSupplier(loadVersionFor(() -> new VersionsPage(versionPageNavigator, i18n("install.installer.choose", i18n("install.installer.game")), "", DownloadProviders.getDownloadProvider(), "game", versionPageNavigator::onGameSelected))); modpackTab.setNodeSupplier(loadVersionFor(() -> { - DownloadListPage page = HMCLLocalizedDownloadListPage.ofModPack((downloadProvider, profile, __, mod, file) -> { - Versions.downloadModpackImpl(downloadProvider, profile, uploadVersion, mod, file); + DownloadListPage page = HMCLLocalizedDownloadListPage.ofModPack((downloadProvider, profile, __, modpack, file) -> { + Versions.downloadModpackImpl(downloadProvider, profile, uploadVersion, modpack, file); }, false); JFXButton installLocalModpackButton = FXUtils.newRaisedButton(i18n("install.modpack")); @@ -109,7 +109,7 @@ public DownloadPage(String uploadVersion) { modTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofMod(FOR_MOD, true))); resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack(FOR_RESOURCE_PACK, true))); shaderTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofShaderPack(FOR_SHADER, true))); - worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteModRepository.WORLDS))); + worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteAddonRepository.WORLDS))); tab = new TabHeader(transitionPane, newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab); Profiles.registerVersionsListener(this::loadVersions); @@ -141,7 +141,7 @@ private static Supplier loadVersionFor(Supplier nodeSuppl }; } - public static void download(DownloadProvider downloadProvider, Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) { + public static void download(DownloadProvider downloadProvider, Profile profile, @Nullable String version, RemoteAddon.Version file, String subdirectoryName) { if (version == null) version = profile.getSelectedVersion(); Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version) : profile.getRepository().getBaseDirectory(); @@ -163,8 +163,8 @@ public static void download(DownloadProvider downloadProvider, Profile profile, Path dest = runDirectory.resolve(subdirectoryName).resolve(result); Controllers.taskDialog(Task.composeAsync(() -> { - var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(file.getFile().getUrl()), dest); - task.setName(file.getName()); + var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(file.file().url()), dest); + task.setName(file.name()); return task; }).whenComplete(Schedulers.javafx(), exception -> { if (exception != null) { @@ -178,7 +178,7 @@ public static void download(DownloadProvider downloadProvider, Profile profile, } }), i18n("message.downloading"), TaskCancellationAction.NORMAL); handler.resolve(); - }, file.getFile().getFilename(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValidForJar), new Validator(i18n("profile.already_exists"), (it) -> !finalExistingFiles.contains(it))); + }, file.file().filename(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValidForJar), new Validator(i18n("profile.already_exists"), (it) -> !finalExistingFiles.contains(it))); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index 064f580d526..8352fa4016e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -24,7 +24,7 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ManuallyCreatedModpackException; import org.jackhuang.hmcl.game.ModpackHelper; -import org.jackhuang.hmcl.mod.Modpack; +import org.jackhuang.hmcl.addon.modpack.Modpack; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Schedulers; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 1826421b108..e5278e3eb3f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -20,11 +20,11 @@ import javafx.scene.Node; import org.jackhuang.hmcl.game.ManuallyCreatedModpackException; import org.jackhuang.hmcl.game.ModpackHelper; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackCompletionException; -import org.jackhuang.hmcl.mod.UnsupportedModpackException; -import org.jackhuang.hmcl.mod.server.ServerModpackManifest; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackCompletionException; +import org.jackhuang.hmcl.addon.modpack.UnsupportedModpackException; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java index 91e4b3d0a10..86c7038fe2b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java @@ -28,7 +28,7 @@ import javafx.scene.shape.SVGPath; import javafx.stage.FileChooser; import org.jackhuang.hmcl.game.ModpackHelper; -import org.jackhuang.hmcl.mod.server.ServerModpackManifest; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Schedulers; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java index 2ace1017fe7..d53fbea2dbd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java @@ -19,8 +19,8 @@ import javafx.application.Platform; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.server.ServerModpackManifest; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.WebPage; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 8473cb27a71..e957af51707 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -19,13 +19,13 @@ import javafx.scene.Node; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.ModpackExportInfo; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; -import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackExportTask; -import org.jackhuang.hmcl.mod.multimc.MultiMCInstanceConfiguration; -import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; -import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCInstanceConfiguration; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackExportTask; import org.jackhuang.hmcl.setting.Config; import org.jackhuang.hmcl.setting.FontManager; import org.jackhuang.hmcl.setting.Profile; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index ed7664a9da8..a0ee329bfdb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -27,7 +27,7 @@ import javafx.scene.control.TreeItem; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import org.jackhuang.hmcl.mod.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index 65aff682c06..c4488309609 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -36,8 +36,8 @@ import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.mod.ModpackExportInfo; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackManifest; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.ui.Controllers; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java index 9f0f7bb410e..3d1316ac304 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackTypeSelectionPage.java @@ -24,11 +24,11 @@ import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; -import org.jackhuang.hmcl.mod.ModpackExportInfo; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackExportTask; -import org.jackhuang.hmcl.mod.multimc.MultiMCModpackExportTask; -import org.jackhuang.hmcl.mod.server.ServerModpackExportTask; -import org.jackhuang.hmcl.mod.modrinth.ModrinthModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.multimc.MultiMCModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.server.ServerModpackExportTask; +import org.jackhuang.hmcl.addon.modpack.modrinth.ModrinthModpackExportTask; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.wizard.WizardController; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java index 54dcdc5b628..47597d34a63 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java @@ -18,8 +18,8 @@ package org.jackhuang.hmcl.ui.versions; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; @@ -39,10 +39,10 @@ public AddonCheckUpdatesTask(DownloadProvider downloadProvider, String gameVersi dependents = addons.stream().map(addon -> Task.supplyAsync(Schedulers.io(), () -> { LocalAddonFile.AddonUpdate candidate = null; - for (RemoteMod.Type type : RemoteMod.Type.values()) { + for (RemoteAddon.Source source : RemoteAddon.Source.values()) { LocalAddonFile.AddonUpdate update = null; try { - update = addon.checkUpdates(downloadProvider, gameVersion, type); + update = addon.checkUpdates(downloadProvider, gameVersion, source); } catch (IOException e) { LOG.warning(String.format("Cannot check update for addon %s.", addon.getFileName()), e); } @@ -50,7 +50,7 @@ public AddonCheckUpdatesTask(DownloadProvider downloadProvider, String gameVersi continue; } - if (candidate == null || candidate.targetVersion().getDatePublished().isBefore(update.targetVersion().getDatePublished())) { + if (candidate == null || candidate.targetVersion().datePublished().isBefore(update.targetVersion().datePublished())) { candidate = update; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java index f9ea8b0718a..b9c251715f6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java @@ -29,9 +29,9 @@ import javafx.scene.control.TableView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalAddonManager; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; @@ -61,7 +61,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class AddonUpdatesPage extends BorderPane implements DecoratorPage { - private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("mods.check_updates"))); + private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("addon.check_update"))); private final LocalAddonManager localAddonManager; private final ObservableList objects; @@ -82,19 +82,19 @@ public AddonUpdatesPage(LocalAddonManager localAddonManager, List fileNameColumn = new TableColumn<>(i18n("mods.check_updates.file")); + TableColumn fileNameColumn = new TableColumn<>(i18n("addon.check_update.file")); fileNameColumn.setPrefWidth(200); setupCellValueFactory(fileNameColumn, AddonUpdateObject::fileNameProperty); - TableColumn currentVersionColumn = new TableColumn<>(i18n("mods.check_updates.current_version")); + TableColumn currentVersionColumn = new TableColumn<>(i18n("addon.check_update.current_version")); currentVersionColumn.setPrefWidth(200); setupCellValueFactory(currentVersionColumn, AddonUpdateObject::currentVersionProperty); - TableColumn targetVersionColumn = new TableColumn<>(i18n("mods.check_updates.target_version")); + TableColumn targetVersionColumn = new TableColumn<>(i18n("addon.check_update.target_version")); targetVersionColumn.setPrefWidth(200); setupCellValueFactory(targetVersionColumn, AddonUpdateObject::targetVersionProperty); - TableColumn sourceColumn = new TableColumn<>(i18n("mods.check_updates.source")); + TableColumn sourceColumn = new TableColumn<>(i18n("addon.check_update.source")); setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceProperty); objects = FXCollections.observableList(updates.stream().map(AddonUpdateObject::new).collect(Collectors.toList())); @@ -114,7 +114,7 @@ public AddonUpdatesPage(LocalAddonManager localAddonManager, List exportList()); - JFXButton nextButton = FXUtils.newRaisedButton(i18n("mods.check_updates.confirm")); + JFXButton nextButton = FXUtils.newRaisedButton(i18n("addon.check_update.confirm")); nextButton.setOnAction(e -> updateFiles()); JFXButton cancelButton = FXUtils.newRaisedButton(i18n("button.cancel")); @@ -142,7 +142,7 @@ private void updateFiles() { task.whenComplete(Schedulers.javafx(), exception -> { fireEvent(new PageCloseEvent()); if (!task.getFailedAddons().isEmpty()) { - Controllers.dialog(i18n("mods.check_updates.failed_download") + "\n" + + Controllers.dialog(i18n("addon.check_update.failed_download") + "\n" + task.getFailedAddons().stream().map(LocalAddonFile::getFileName).collect(Collectors.joining("\n")), i18n("install.failed"), MessageDialogPane.MessageType.ERROR); @@ -152,7 +152,7 @@ private void updateFiles() { Controllers.dialog(i18n("install.success")); } }), - i18n("mods.check_updates"), + i18n("addon.check_update"), TaskCancellationAction.NORMAL); } @@ -204,14 +204,14 @@ public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { enabled.set(!data.localAddonFile().isDisabled()); fileName.set(data.localAddonFile().getFileName()); - currentVersion.set(data.currentVersion().getVersion()); - targetVersion.set(data.targetVersion().getVersion()); - switch (data.currentVersion().getSelf().getType()) { + currentVersion.set(data.currentVersion().version()); + targetVersion.set(data.targetVersion().version()); + switch (data.currentVersion().self().getType()) { case CURSEFORGE: - source.set(i18n("mods.curseforge")); + source.set(i18n("addon.curseforge")); break; case MODRINTH: - source.set(i18n("mods.modrinth")); + source.set(i18n("addon.modrinth")); } } @@ -285,29 +285,29 @@ public static class AddonUpdateTask extends Task { private final List failedAddons = new ArrayList<>(); AddonUpdateTask(Path addonDirectory, List addons) { - setStage("mods.check_updates.confirm"); + setStage("addon.check_update.confirm"); getProperties().put("total", addons.size()); this.dependents = new ArrayList<>(); for (LocalAddonFile.AddonUpdate addon : addons) { LocalAddonFile local = addon.localAddonFile(); - RemoteMod.Version remote = addon.targetVersion(); + RemoteAddon.Version remote = addon.targetVersion(); boolean isDisabled = local.isDisabled(); String originalFileName = local.getFile().getFileName().toString(); dependents.add(Task .runAsync(Schedulers.javafx(), () -> local.setOld(true)) .thenComposeAsync(() -> { - String fileName = addon.useRemoteFileName() ? remote.getFile().getFilename() : originalFileName; + String fileName = addon.useRemoteFileName() ? remote.file().filename() : originalFileName; if (isDisabled) fileName = StringUtils.addSuffix(fileName, LocalAddonManager.DISABLED_EXTENSION); var task = new FileDownloadTask( - remote.getFile().getUrl(), + remote.file().url(), addonDirectory.resolve(fileName) ); - task.setName(remote.getName()); + task.setName(remote.name()); return task; }) .whenComplete(Schedulers.javafx(), exception -> { @@ -325,7 +325,7 @@ public static class AddonUpdateTask extends Task { } } }) - .withCounter("mods.check_updates.confirm")); + .withCounter("addon.check_update.confirm")); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java index 8f619a6ee66..76d130b17a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java @@ -22,7 +22,7 @@ import javafx.scene.control.Skin; import javafx.stage.FileChooser; import org.jackhuang.hmcl.game.World; -import org.jackhuang.hmcl.mod.DataPack; +import org.jackhuang.hmcl.addon.datapack.DataPack; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java index c29c8b34e9e..40fc80daf59 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java @@ -43,7 +43,7 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.util.Duration; -import org.jackhuang.hmcl.mod.DataPack; +import org.jackhuang.hmcl.addon.datapack.DataPack; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index d73f1164ee0..2c32045d9e7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -39,9 +39,9 @@ import javafx.scene.layout.*; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; @@ -74,7 +74,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP private final ObjectProperty version = new SimpleObjectProperty<>(); private final IntegerProperty pageOffset = new SimpleIntegerProperty(0); private final IntegerProperty pageCount = new SimpleIntegerProperty(-1); - private final ListProperty items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); + private final ListProperty items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); private final ObservableList versions = FXCollections.observableArrayList(); private final StringProperty selectedVersion = new SimpleStringProperty(); private final DownloadPage.DownloadCallback callback; @@ -85,16 +85,16 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP protected final StringProperty downloadSource = new SimpleStringProperty(); private final WeakListenerHolder listenerHolder = new WeakListenerHolder(); private int searchID = 0; - protected RemoteModRepository repository; + protected RemoteAddonRepository repository; private final DownloadProvider downloadProvider; private Runnable retrySearch; - public DownloadListPage(RemoteModRepository repository) { + public DownloadListPage(RemoteAddonRepository repository) { this(repository, null, false); } - public DownloadListPage(RemoteModRepository repository, DownloadPage.DownloadCallback callback, boolean versionSelection) { + public DownloadListPage(RemoteAddonRepository repository, DownloadPage.DownloadCallback callback, boolean versionSelection) { this.repository = repository; this.callback = callback; this.versionSelection = versionSelection; @@ -118,7 +118,7 @@ public void loadVersion(Profile profile, String version) { if (!searchInitialized) { searchInitialized = true; - search("", null, 0, "", RemoteModRepository.SortType.POPULARITY); + search("", null, 0, "", RemoteAddonRepository.SortType.POPULARITY); } if (versionSelection) { @@ -157,7 +157,7 @@ public void selectVersion(String versionID) { FXUtils.runInFX(() -> selectedVersion.set(versionID)); } - private void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) { + private void search(String userGameVersion, RemoteAddonRepository.Category category, int pageOffset, String searchFilter, RemoteAddonRepository.SortType sort) { retrySearch = null; setLoading(true); setFailed(false); @@ -173,7 +173,7 @@ private void search(String userGameVersion, RemoteModRepository.Category categor : ""; } }).thenApplyAsync( - gameVersion -> repository.search(downloadProvider, gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC) + gameVersion -> repository.search(downloadProvider, gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteAddonRepository.SortOrder.DESC) ).whenComplete(Schedulers.javafx(), (result, exception) -> { if (searchID != currentSearchID) { return; @@ -193,7 +193,7 @@ private void search(String userGameVersion, RemoteModRepository.Category categor } protected String getLocalizedCategory(String category, Object self) { - return repository instanceof ModrinthRemoteModRepository + return repository instanceof ModrinthRemoteAddonRepository ? i18n("modrinth.category." + category) : i18n("curse.category." + category); } @@ -210,10 +210,10 @@ private String getLocalizedCategoryIndent(ModDownloadListPageSkin.CategoryIndent } protected String getLocalizedOfficialPage() { - if (repository instanceof ModrinthRemoteModRepository) { - return i18n("mods.modrinth"); + if (repository instanceof ModrinthRemoteAddonRepository) { + return i18n("addon.modrinth"); } else { - return i18n("mods.curseforge"); + return i18n("addon.curseforge"); } } @@ -236,7 +236,7 @@ protected Skin createDefaultSkin() { } private static class ModDownloadListPageSkin extends SkinBase { - private final JFXListView listView = new JFXListView<>(); + private final JFXListView listView = new JFXListView<>(); private final RemoteImageLoader iconLoader; protected ModDownloadListPageSkin(DownloadListPage control) { @@ -341,7 +341,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { categoryStackPane.getChildren().setAll(categoryComboBox); categoryComboBox.prefWidthProperty().bind(categoryStackPane.widthProperty()); categoryComboBox.getStyleClass().add("fit-width"); - categoryComboBox.setPromptText(i18n("mods.category")); + categoryComboBox.setPromptText(i18n("addon.category")); categoryComboBox.getSelectionModel().select(0); categoryComboBox.setConverter(stringConverter(getSkinnable()::getLocalizedCategoryIndent)); FXUtils.onChangeAndOperate(getSkinnable().downloadSource, downloadSource -> { @@ -356,7 +356,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { List result = new ArrayList<>(); result.add(CategoryIndented.ALL); - for (RemoteModRepository.Category category : Lang.toIterable(categories)) { + for (RemoteAddonRepository.Category category : Lang.toIterable(categories)) { resolveCategory(category, 0, result); } categoryComboBox.getItems().setAll(result); @@ -365,14 +365,14 @@ protected ModDownloadListPageSkin(DownloadListPage control) { }); StackPane sortStackPane = new StackPane(); - JFXComboBox sortComboBox = new JFXComboBox<>(); + JFXComboBox sortComboBox = new JFXComboBox<>(); sortStackPane.getChildren().setAll(sortComboBox); sortComboBox.prefWidthProperty().bind(sortStackPane.widthProperty()); sortComboBox.getStyleClass().add("fit-width"); sortComboBox.setConverter(stringConverter(sortType -> i18n("curse.sort." + sortType.name().toLowerCase(Locale.ROOT)))); - sortComboBox.getItems().setAll(RemoteModRepository.SortType.values()); + sortComboBox.getItems().setAll(RemoteAddonRepository.SortType.values()); sortComboBox.getSelectionModel().select(0); - searchPane.addRow(rowIndex++, new Label(i18n("mods.category")), categoryStackPane, new Label(i18n("search.sort")), sortStackPane); + searchPane.addRow(rowIndex++, new Label(i18n("addon.category")), categoryStackPane, new Label(i18n("search.sort")), sortStackPane); IntegerProperty filterID = new SimpleIntegerProperty(this, "Filter ID", 0); IntegerProperty currentFilterID = new SimpleIntegerProperty(this, "Current Filter ID", -1); @@ -558,7 +558,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { wrapper.getStyleClass().add("card-no-padding"); FXUtils.onClicked(wrapper, () -> { - RemoteMod item = getItem(); + RemoteAddon item = getItem(); if (item != null) Controllers.navigate(new DownloadPage(getSkinnable(), item, getSkinnable().getProfileVersion(), getSkinnable().callback)); }); @@ -570,7 +570,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { } @Override - protected void updateItem(RemoteMod item, boolean empty) { + protected void updateItem(RemoteAddon item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { setGraphic(null); @@ -597,13 +597,13 @@ protected void updateItem(RemoteMod item, boolean empty) { getChildren().setAll(pane); } - private record CategoryIndented(int indent, RemoteModRepository.Category category) { + private record CategoryIndented(int indent, RemoteAddonRepository.Category category) { private static final CategoryIndented ALL = new CategoryIndented(0, null); } - private static void resolveCategory(RemoteModRepository.Category category, int indent, List result) { + private static void resolveCategory(RemoteAddonRepository.Category category, int indent, List result) { result.add(new CategoryIndented(indent, category)); - for (RemoteModRepository.Category subcategory : category.subcategories()) { + for (RemoteAddonRepository.Category subcategory : category.subcategories()) { resolveCategory(subcategory, indent + 1, result); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index d65bba001bd..f4be0be8b16 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -34,9 +34,9 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Schedulers; @@ -66,18 +66,18 @@ public class DownloadPage extends Control implements DecoratorPage { private final BooleanProperty loaded = new SimpleBooleanProperty(false); private final BooleanProperty loading = new SimpleBooleanProperty(false); private final BooleanProperty failed = new SimpleBooleanProperty(false); - private final RemoteModRepository repository; + private final RemoteAddonRepository repository; private final ModTranslations translations; - private final RemoteMod addon; + private final RemoteAddon addon; private final ModTranslations.Mod mod; private final Profile.ProfileVersion version; private final DownloadCallback callback; private final DownloadListPage page; - private final RemoteModRepository.Type type; + private final RemoteAddonRepository.Type type; - private SimpleMultimap> versions; + private SimpleMultimap> versions; - public DownloadPage(DownloadListPage page, RemoteMod addon, Profile.ProfileVersion version, @Nullable DownloadCallback callback) { + public DownloadPage(DownloadListPage page, RemoteAddon addon, Profile.ProfileVersion version, @Nullable DownloadCallback callback) { this.page = page; this.repository = page.repository; this.addon = addon; @@ -86,17 +86,17 @@ public DownloadPage(DownloadListPage page, RemoteMod addon, Profile.ProfileVersi this.mod = translations.getModByCurseForgeId(addon.getSlug()); this.version = version; this.callback = callback; - loadModVersions(); + loadAddonVersions(); this.state.set(State.fromTitle(addon.getTitle())); } - private void loadModVersions() { + private void loadAddonVersions() { setLoading(true); setFailed(false); Task.supplyAsync(() -> { - Stream versions = addon.getData().loadVersions(repository, page.getDownloadProvider()); + Stream versions = addon.getData().loadVersions(repository, page.getDownloadProvider()); return sortVersions(versions); }).whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { @@ -111,23 +111,23 @@ private void loadModVersions() { }).start(); } - private SimpleMultimap> sortVersions(Stream versions) { - SimpleMultimap> classifiedVersions + private SimpleMultimap> sortVersions(Stream versions) { + SimpleMultimap> classifiedVersions = new SimpleMultimap<>(HashMap::new, ArrayList::new); versions.forEach(version -> { - for (String gameVersion : version.getGameVersions()) { + for (String gameVersion : version.gameVersions()) { classifiedVersions.put(gameVersion, version); } }); for (String gameVersion : classifiedVersions.keys()) { - List versionList = classifiedVersions.get(gameVersion); - versionList.sort(Comparator.comparing(RemoteMod.Version::getDatePublished).reversed()); + List versionList = classifiedVersions.get(gameVersion); + versionList.sort(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()); } return classifiedVersions; } - public RemoteMod getAddon() { + public RemoteAddon getAddon() { return addon; } @@ -159,21 +159,21 @@ public void setFailed(boolean failed) { this.failed.set(failed); } - public void download(RemoteMod mod, RemoteMod.Version file) { + public void download(RemoteAddon addon, RemoteAddon.Version file) { if (this.callback == null) { saveAs(file); } else { - this.callback.download(page.getDownloadProvider(), version.getProfile(), version.getVersion(), mod, file); + this.callback.download(page.getDownloadProvider(), version.getProfile(), version.getVersion(), addon, file); } } - public void saveAs(RemoteMod.Version file) { - String extension = StringUtils.substringAfterLast(file.getFile().getFilename(), '.'); + public void saveAs(RemoteAddon.Version file) { + String extension = StringUtils.substringAfterLast(file.file().filename(), '.'); FileChooser fileChooser = new FileChooser(); fileChooser.setTitle(i18n("button.save_as")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("file"), "*." + extension)); - fileChooser.setInitialFileName(file.getFile().getFilename()); + fileChooser.setInitialFileName(file.file().filename()); Path dest = FileUtils.toPath(fileChooser.showSaveDialog(Controllers.getStage())); if (dest == null) { return; @@ -181,8 +181,8 @@ public void saveAs(RemoteMod.Version file) { Controllers.taskDialog( Task.composeAsync(() -> { - var task = new FileDownloadTask(file.getFile().getUrl(), dest, file.getFile().getIntegrityCheck()); - task.setName(file.getName()); + var task = new FileDownloadTask(file.file().url(), dest, file.file().getIntegrityCheck()); + task.setName(file.name()); return task; }), i18n("message.downloading"), @@ -196,12 +196,12 @@ public ReadOnlyObjectProperty stateProperty() { @Override protected Skin createDefaultSkin() { - return new ModDownloadPageSkin(this); + return new DownloadPageSkin(this); } - private static class ModDownloadPageSkin extends SkinBase { + private static class DownloadPageSkin extends SkinBase { - protected ModDownloadPageSkin(DownloadPage control) { + protected DownloadPageSkin(DownloadPage control) { super(control); VBox pane = new VBox(8); @@ -258,7 +258,7 @@ protected ModDownloadPageSkin(DownloadPage control) { return null; } }, getSkinnable().failedProperty())); - spinnerPane.setOnFailedAction(e -> getSkinnable().loadModVersions()); + spinnerPane.setOnFailedAction(e -> getSkinnable().loadAddonVersions()); ComponentList list = new ComponentList(); ScrollPane scrollPane = new ScrollPane(list); @@ -278,18 +278,18 @@ protected ModDownloadPageSkin(DownloadPage control) { String gameVersion = repository.getGameVersion(game).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { - List modVersions = control.versions.get(gameVersion); + List modVersions = control.versions.get(gameVersion); if (modVersions != null && !modVersions.isEmpty()) { Set targetLoaders = LibraryAnalyzer.analyze(game, gameVersion).getModLoaders(); resolve: - for (RemoteMod.Version modVersion : modVersions) { - if (getSkinnable().type == RemoteModRepository.Type.MOD) { - for (ModLoaderType loader : modVersion.getLoaders()) { + for (RemoteAddon.Version modVersion : modVersions) { + if (getSkinnable().type == RemoteAddonRepository.Type.MOD) { + for (ModLoaderType loader : modVersion.loaders()) { if (targetLoaders.contains(loader)) { list.getContent().addAll( ComponentList.createComponentListTitle(i18n("mods.download.recommend", gameVersion)), - new ModItem(control.addon, modVersion, control) + new AddonItem(control.addon, modVersion, control) ); break resolve; } @@ -297,7 +297,7 @@ protected ModDownloadPageSkin(DownloadPage control) { } else { list.getContent().addAll( ComponentList.createComponentListTitle(i18n("mods.download.recommend", gameVersion)), - new ModItem(control.addon, modVersion, control) + new AddonItem(control.addon, modVersion, control) ); break; } @@ -309,12 +309,12 @@ protected ModDownloadPageSkin(DownloadPage control) { final class Versions { static ComponentSublist createSublist(DownloadPage control, String title, List target) { var sublist = new ComponentSublist(() -> { - ArrayList items = new ArrayList<>(); + ArrayList items = new ArrayList<>(); for (String gv : target) { - List versions = control.versions.get(gv); + List versions = control.versions.get(gv); if (versions != null) { - for (RemoteMod.Version v : versions) { - items.add(new ModItem(control.addon, v, control)); + for (RemoteAddon.Version v : versions) { + items.add(new AddonItem(control.addon, v, control)); } } } @@ -363,18 +363,18 @@ static ComponentSublist createSublist(DownloadPage control, String title, List I18N_KEY = new EnumMap<>(Lang.mapOf( - Pair.pair(RemoteMod.DependencyType.EMBEDDED, "mods.dependency.embedded"), - Pair.pair(RemoteMod.DependencyType.OPTIONAL, "mods.dependency.optional"), - Pair.pair(RemoteMod.DependencyType.REQUIRED, "mods.dependency.required"), - Pair.pair(RemoteMod.DependencyType.TOOL, "mods.dependency.tool"), - Pair.pair(RemoteMod.DependencyType.INCLUDE, "mods.dependency.include"), - Pair.pair(RemoteMod.DependencyType.INCOMPATIBLE, "mods.dependency.incompatible"), - Pair.pair(RemoteMod.DependencyType.BROKEN, "mods.dependency.broken") + private static final class DependencyAddonItem extends LineButton { + public static final EnumMap I18N_KEY = new EnumMap<>(Lang.mapOf( + Pair.pair(RemoteAddon.DependencyType.EMBEDDED, "addon.dependency.embedded"), + Pair.pair(RemoteAddon.DependencyType.OPTIONAL, "addon.dependency.optional"), + Pair.pair(RemoteAddon.DependencyType.REQUIRED, "addon.dependency.required"), + Pair.pair(RemoteAddon.DependencyType.TOOL, "addon.dependency.tool"), + Pair.pair(RemoteAddon.DependencyType.INCLUDE, "addon.dependency.include"), + Pair.pair(RemoteAddon.DependencyType.INCOMPATIBLE, "addon.dependency.incompatible"), + Pair.pair(RemoteAddon.DependencyType.BROKEN, "addon.dependency.broken") )); - DependencyModItem(DownloadListPage page, RemoteMod addon, Profile.ProfileVersion version) { + DependencyAddonItem(DownloadListPage page, RemoteAddon addon, Profile.ProfileVersion version) { HBox pane = new HBox(8); pane.setPadding(new Insets(0, 8, 0, 8)); pane.setAlignment(Pos.CENTER_LEFT); @@ -385,7 +385,7 @@ private static final class DependencyModItem extends LineButton { pane.getChildren().setAll(imageView, content); FXUtils.setLimitHeight(this, 60); - RemoteModRepository.Type type = addon.getRepositoryType(); + RemoteAddonRepository.Type type = addon.getRepositoryType(); DownloadCallback callback = switch (type) { case MOD -> org.jackhuang.hmcl.ui.download.DownloadPage.FOR_MOD; case RESOURCE_PACK -> org.jackhuang.hmcl.ui.download.DownloadPage.FOR_RESOURCE_PACK; @@ -398,7 +398,7 @@ private static final class DependencyModItem extends LineButton { }); setNode(IDX_LEADING, pane); - if (addon != RemoteMod.BROKEN) { + if (addon != RemoteAddon.BROKEN) { ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(type).getModByCurseForgeId(addon.getSlug()); content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.getTitle()); content.setSubtitle(addon.getDescription()); @@ -410,16 +410,16 @@ private static final class DependencyModItem extends LineButton { imageView.imageProperty().bind(FXUtils.newRemoteImage(addon.getIconUrl(), 80, 80, true, true)); } } else { - content.setTitle(i18n("mods.broken_dependency.title")); - content.setSubtitle(i18n("mods.broken_dependency.desc")); + content.setTitle(i18n("addon.broken_dependency.title")); + content.setSubtitle(i18n("addon.broken_dependency.desc")); imageView.setImage(FXUtils.newBuiltinImage("/assets/img/icon@4x.png")); } } } - private static final class ModItem extends StackPane { + private static final class AddonItem extends StackPane { - ModItem(RemoteMod mod, RemoteMod.Version dataItem, DownloadPage selfPage) { + AddonItem(RemoteAddon mod, RemoteAddon.Version dataItem, DownloadPage selfPage) { VBox pane = new VBox(8); pane.setPadding(new Insets(8, 0, 8, 0)); @@ -433,25 +433,25 @@ private static final class ModItem extends StackPane { StackPane graphicPane = new StackPane(); TwoLineListItem content = new TwoLineListItem(); HBox.setHgrow(content, Priority.ALWAYS); - content.setTitle(dataItem.getName()); - content.setSubtitle(I18n.formatDateTime(dataItem.getDatePublished())); + content.setTitle(dataItem.name()); + content.setSubtitle(I18n.formatDateTime(dataItem.datePublished())); - switch (dataItem.getVersionType()) { + switch (dataItem.versionType()) { case Alpha: - content.addTag(i18n("mods.channel.alpha")); + content.addTag(i18n("addon.channel.alpha")); graphicPane.getChildren().setAll(SVG.ALPHA_CIRCLE.createIcon(24)); break; case Beta: - content.addTag(i18n("mods.channel.beta")); + content.addTag(i18n("addon.channel.beta")); graphicPane.getChildren().setAll(SVG.BETA_CIRCLE.createIcon(24)); break; case Release: - content.addTag(i18n("mods.channel.release")); + content.addTag(i18n("addon.channel.release")); graphicPane.getChildren().setAll(SVG.RELEASE_CIRCLE.createIcon(24)); break; } - for (ModLoaderType modLoaderType : dataItem.getLoaders()) { + for (ModLoaderType modLoaderType : dataItem.loaders()) { switch (modLoaderType) { case FORGE: content.addTag(i18n("install.installer.forge")); @@ -481,7 +481,7 @@ private static final class ModItem extends StackPane { } RipplerContainer container = new RipplerContainer(pane); - FXUtils.onClicked(container, () -> Controllers.dialog(new ModVersion(mod, dataItem, selfPage))); + FXUtils.onClicked(container, () -> Controllers.dialog(new AddonVersion(mod, dataItem, selfPage))); getChildren().setAll(container); // Workaround for https://github.com/HMCL-dev/HMCL/issues/2129 @@ -489,9 +489,9 @@ private static final class ModItem extends StackPane { } } - private static final class ModVersion extends JFXDialogLayout { - public ModVersion(RemoteMod mod, RemoteMod.Version version, DownloadPage selfPage) { - RemoteModRepository.Type type = selfPage.type; + private static final class AddonVersion extends JFXDialogLayout { + public AddonVersion(RemoteAddon mod, RemoteAddon.Version version, DownloadPage selfPage) { + RemoteAddonRepository.Type type = selfPage.type; String title = switch (type) { case WORLD -> "world.download.title"; @@ -500,13 +500,13 @@ public ModVersion(RemoteMod mod, RemoteMod.Version version, DownloadPage selfPag case SHADER_PACK -> "shaderpack.download.title"; default -> "mods.download.title"; }; - this.setHeading(new HBox(new Label(i18n(title, version.getName())))); + this.setHeading(new HBox(new Label(i18n(title, version.name())))); VBox box = new VBox(8); box.setPadding(new Insets(8)); - ModItem modItem = new ModItem(mod, version, selfPage); - modItem.setMouseTransparent(true); // Item is displayed for info, clicking shouldn't open the dialog again - box.getChildren().setAll(modItem); + AddonItem addonItem = new AddonItem(mod, version, selfPage); + addonItem.setMouseTransparent(true); // Item is displayed for info, clicking shouldn't open the dialog again + box.getChildren().setAll(addonItem); SpinnerPane spinnerPane = new SpinnerPane(); ScrollPane scrollPane = new ScrollPane(); ComponentList dependenciesList = new ComponentList(); @@ -527,10 +527,10 @@ public ModVersion(RemoteMod mod, RemoteMod.Version version, DownloadPage selfPag JFXButton downloadButton = null; if (selfPage.callback != null) { - downloadButton = new JFXButton(type == RemoteModRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install")); + downloadButton = new JFXButton(type == RemoteAddonRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install")); downloadButton.getStyleClass().add("dialog-accept"); downloadButton.setOnAction(e -> { - if (type == RemoteModRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) { + if (type == RemoteAddonRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) { fireEvent(new DialogCloseEvent()); } selfPage.download(mod, version); @@ -562,20 +562,20 @@ public ModVersion(RemoteMod mod, RemoteMod.Version version, DownloadPage selfPag onEscPressed(this, cancelButton::fire); } - private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, SpinnerPane spinnerPane, ComponentList dependenciesList) { + private void loadDependencies(RemoteAddon.Version version, DownloadPage selfPage, SpinnerPane spinnerPane, ComponentList dependenciesList) { spinnerPane.setLoading(true); Task.composeAsync(() -> { // TODO: Massive tasks may cause OOM. - EnumMap> dependencies = new EnumMap<>(RemoteMod.DependencyType.class); - List> queue = new ArrayList<>(version.getDependencies().size()); - for (RemoteMod.Dependency dependency : version.getDependencies()) { - if (dependency.getType() == RemoteMod.DependencyType.INCOMPATIBLE || dependency.getType() == RemoteMod.DependencyType.BROKEN) { + EnumMap> dependencies = new EnumMap<>(RemoteAddon.DependencyType.class); + List> queue = new ArrayList<>(version.dependencies().size()); + for (RemoteAddon.Dependency dependency : version.dependencies()) { + if (dependency.getType() == RemoteAddon.DependencyType.INCOMPATIBLE || dependency.getType() == RemoteAddon.DependencyType.BROKEN) { continue; } if (!dependencies.containsKey(dependency.getType())) { List list = new ArrayList<>(); - Label title = new Label(i18n(DependencyModItem.I18N_KEY.get(dependency.getType()))); + Label title = new Label(i18n(DependencyAddonItem.I18N_KEY.get(dependency.getType()))); title.setPadding(new Insets(0, 8, 0, 8)); list.add(title); dependencies.put(dependency.getType(), list); @@ -584,11 +584,11 @@ private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, queue.add(Task.supplyAsync(Schedulers.io(), () -> dependency.load(selfPage.page.getDownloadProvider())) .setSignificance(Task.TaskSignificance.MINOR) .thenAcceptAsync(Schedulers.javafx(), dep -> { - if (dep == RemoteMod.BROKEN) { + if (dep == RemoteAddon.BROKEN) { return; } - DependencyModItem dependencyModItem = new DependencyModItem(selfPage.page, dep, selfPage.version); - dependencies.get(dependency.getType()).add(dependencyModItem); + DependencyAddonItem dependencyAddonItem = new DependencyAddonItem(selfPage.page, dep, selfPage.version); + dependencies.get(dependency.getType()).add(dependencyAddonItem); }) .setSignificance(Task.TaskSignificance.MINOR)); } @@ -611,6 +611,6 @@ private void loadDependencies(RemoteMod.Version version, DownloadPage selfPage, @FunctionalInterface public interface DownloadCallback { - void download(DownloadProvider downloadProvider, Profile profile, @Nullable String version, RemoteMod mod, RemoteMod.Version file); + void download(DownloadProvider downloadProvider, Profile profile, @Nullable String version, RemoteAddon addon, RemoteAddon.Version file); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java index ea9f48c7b94..d8a95f6d1f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java @@ -20,7 +20,7 @@ import javafx.beans.property.*; import javafx.scene.image.Image; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.util.i18n.I18n; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java index 9b458b52786..c35470bcb82 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java @@ -17,11 +17,10 @@ */ package org.jackhuang.hmcl.ui.versions; -import org.jackhuang.hmcl.game.LocalizedRemoteModRepository; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.mod.curse.CurseAddon; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.game.LocalizedRemoteAddonRepository; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.util.i18n.I18n; import java.util.MissingResourceException; @@ -32,86 +31,86 @@ public final class HMCLLocalizedDownloadListPage extends DownloadListPage { public static DownloadListPage ofMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.MOD, CurseForgeRemoteModRepository.MODS, ModrinthRemoteModRepository.MODS); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, ModrinthRemoteAddonRepository.MODS); } public static DownloadListPage ofCurseForgeMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.MOD, CurseForgeRemoteModRepository.MODS, null); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, null); } public static DownloadListPage ofModrinthMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.MOD, null, ModrinthRemoteModRepository.MODS); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, null, ModrinthRemoteAddonRepository.MODS); } public static DownloadListPage ofModPack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.MODPACK, CurseForgeRemoteModRepository.MODPACKS, ModrinthRemoteModRepository.MODPACKS); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MODPACK, CurseForgeRemoteAddonRepository.MODPACKS, ModrinthRemoteAddonRepository.MODPACKS); } public static DownloadListPage ofResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.RESOURCE_PACK, CurseForgeRemoteModRepository.RESOURCE_PACKS, ModrinthRemoteModRepository.RESOURCE_PACKS); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, ModrinthRemoteAddonRepository.RESOURCE_PACKS); } public static DownloadListPage ofCurseForgeResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.RESOURCE_PACK, CurseForgeRemoteModRepository.RESOURCE_PACKS, null); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, null); } public static DownloadListPage ofModrinthResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.RESOURCE_PACK, null, ModrinthRemoteModRepository.RESOURCE_PACKS); + return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, null, ModrinthRemoteAddonRepository.RESOURCE_PACKS); } public static DownloadListPage ofShaderPack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - var page = new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteModRepository.Type.SHADER_PACK, CurseForgeRemoteModRepository.SHADERS, ModrinthRemoteModRepository.SHADER_PACKS); + var page = new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.SHADER_PACK, CurseForgeRemoteAddonRepository.SHADERS, ModrinthRemoteAddonRepository.SHADER_PACKS); page.supportChinese.set(false); return page; } - private HMCLLocalizedDownloadListPage(DownloadPage.DownloadCallback callback, boolean versionSelection, RemoteModRepository.Type type, CurseForgeRemoteModRepository curseForge, ModrinthRemoteModRepository modrinth) { + private HMCLLocalizedDownloadListPage(DownloadPage.DownloadCallback callback, boolean versionSelection, RemoteAddonRepository.Type type, CurseForgeRemoteAddonRepository curseForge, ModrinthRemoteAddonRepository modrinth) { super(null, callback, versionSelection); repository = new Repository(type, curseForge, modrinth); supportChinese.set(true); - boolean supportedCurseForge = CurseForgeRemoteModRepository.isAvailable() && curseForge != null; + boolean supportedCurseForge = CurseForgeRemoteAddonRepository.isAvailable() && curseForge != null; - downloadSources.setAll("mods.modrinth"); + downloadSources.setAll("addon.modrinth"); if (supportedCurseForge) { - downloadSources.add("mods.curseforge"); + downloadSources.add("addon.curseforge"); } if ("curseforge".equalsIgnoreCase(config().getDefaultAddonSource())) { if (supportedCurseForge) { - downloadSource.set("mods.curseforge"); + downloadSource.set("addon.curseforge"); } else if (modrinth != null) { - downloadSource.set("mods.modrinth"); + downloadSource.set("addon.modrinth"); } else { throw new AssertionError("Should not be here."); } } else { if (modrinth != null) { - downloadSource.set("mods.modrinth"); + downloadSource.set("addon.modrinth"); } else if (supportedCurseForge) { - downloadSource.set("mods.curseforge"); + downloadSource.set("addon.curseforge"); } else { throw new AssertionError("Should not be here."); } } } - private class Repository extends LocalizedRemoteModRepository { - private final RemoteModRepository.Type type; - private final CurseForgeRemoteModRepository curseForge; - private final ModrinthRemoteModRepository modrinth; + private class Repository extends LocalizedRemoteAddonRepository { + private final RemoteAddonRepository.Type type; + private final CurseForgeRemoteAddonRepository curseForge; + private final ModrinthRemoteAddonRepository modrinth; - public Repository(Type type, CurseForgeRemoteModRepository curseForge, ModrinthRemoteModRepository modrinth) { + public Repository(Type type, CurseForgeRemoteAddonRepository curseForge, ModrinthRemoteAddonRepository modrinth) { this.type = type; this.curseForge = curseForge; this.modrinth = modrinth; } @Override - protected RemoteModRepository getBackedRemoteModRepository() { - if ("mods.modrinth".equals(downloadSource.get())) { + protected RemoteAddonRepository getBackedRemoteModRepository() { + if ("addon.modrinth".equals(downloadSource.get())) { return modrinth; } else { return curseForge; @@ -120,7 +119,7 @@ protected RemoteModRepository getBackedRemoteModRepository() { @Override protected SortType getBackedRemoteModRepositorySortOrder() { - if ("mods.modrinth".equals(downloadSource.get())) { + if ("addon.modrinth".equals(downloadSource.get())) { return SortType.NAME; } else { return SortType.POPULARITY; @@ -139,12 +138,12 @@ protected String getLocalizedCategory(String category, Object self) { return ""; } - String key = ("mods.modrinth".equals(downloadSource.get()) ? "modrinth" : "curse") + ".category." + category; + String key = ("addon.modrinth".equals(downloadSource.get()) ? "modrinth" : "curse") + ".category." + category; try { return I18n.getResourceBundle().getString(key); } catch (MissingResourceException e) { LOG.warning("Cannot find key " + key + " in resource bundle"); - if (self instanceof CurseAddon.Category curseCategory) { + if (self instanceof CurseForgeRemoteAddonRepository.Category curseCategory) { return curseCategory.getName(); } return category; @@ -153,10 +152,10 @@ protected String getLocalizedCategory(String category, Object self) { @Override protected String getLocalizedOfficialPage() { - if ("mods.modrinth".equals(downloadSource.get())) { - return i18n("mods.modrinth"); + if ("addon.modrinth".equals(downloadSource.get())) { + return i18n("addon.modrinth"); } else { - return i18n("mods.curseforge"); + return i18n("addon.curseforge"); } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index e870a259f81..2516c084a1e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -25,9 +25,9 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; 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.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.Schedulers; @@ -245,15 +245,15 @@ public void checkUpdates(Collection mods) { .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception instanceof CancellationException) return; if (exception != null || result == null) { - Controllers.dialog(i18n("mods.check_updates.failed_check"), i18n("message.failed"), MessageDialogPane.MessageType.ERROR); + Controllers.dialog(i18n("addon.check_update.failed_check"), i18n("message.failed"), MessageDialogPane.MessageType.ERROR); } else if (result.isEmpty()) { - Controllers.dialog(i18n("mods.check_updates.empty")); + Controllers.dialog(i18n("addon.check_update.empty")); } else { Controllers.navigateForward(new AddonUpdatesPage<>(modManager, result)); } }) .withStagesHints("update.checking"), - i18n("mods.check_updates"), TaskCancellationAction.NORMAL); + i18n("addon.check_update"), TaskCancellationAction.NORMAL); if (profile.getRepository().isModpack(instanceId)) { Controllers.confirm( diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 4383aa74373..76a7580fd00 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -37,12 +37,11 @@ import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javafx.util.Duration; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.addon.*; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.VersionIconType; @@ -144,7 +143,7 @@ final class ModListPageSkin extends SkinBase { createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("mods.add"), SVG.ADD, skinnable::add), createToolbarButton2(i18n("button.reveal_dir"), SVG.FOLDER_OPEN, skinnable::openModFolder), - createToolbarButton2(i18n("mods.check_updates.button"), SVG.UPDATE, () -> + createToolbarButton2(i18n("addon.check_update.button"), SVG.UPDATE, () -> skinnable.checkUpdates( listView.getItems().stream() .map(ModInfoObject::getModInfo) @@ -178,7 +177,7 @@ final class ModListPageSkin extends SkinBase { skinnable.enableSelected(listView.getSelectionModel().getSelectedItems())), createToolbarButton2(i18n("mods.disable"), SVG.CLOSE, () -> skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())), - createToolbarButton2(i18n("mods.check_updates.button"), SVG.UPDATE, () -> + createToolbarButton2(i18n("addon.check_update.button"), SVG.UPDATE, () -> skinnable.checkUpdates( listView.getSelectionModel().getSelectedItems().stream() .map(ModInfoObject::getModInfo) @@ -467,18 +466,18 @@ final class ModInfoDialog extends JFXDialogLayout { setBody(descriptionPane); if (StringUtils.isNotBlank(modInfo.getModInfo().getId())) { - for (Pair item : Arrays.asList( - pair("mods.curseforge", CurseForgeRemoteModRepository.MODS), - pair("mods.modrinth", ModrinthRemoteModRepository.MODS) + for (Pair item : Arrays.asList( + pair("addon.curseforge", CurseForgeRemoteAddonRepository.MODS), + pair("addon.modrinth", ModrinthRemoteAddonRepository.MODS) )) { - RemoteModRepository repository = item.getValue(); + RemoteAddonRepository repository = item.getValue(); JFXHyperlink button = new JFXHyperlink(i18n(item.getKey())); Task.runAsync(() -> { - Optional versionOptional = repository.getRemoteVersionByLocalFile(modInfo.getModInfo().getFile()); + Optional versionOptional = repository.getRemoteVersionByLocalFile(modInfo.getModInfo().getFile()); if (versionOptional.isPresent()) { - RemoteMod remoteMod = repository.getModById(DownloadProviders.getDownloadProvider(), versionOptional.get().getModid()); + RemoteAddon remoteAddon = repository.getModById(DownloadProviders.getDownloadProvider(), versionOptional.get().modid()); FXUtils.runInFX(() -> { - for (ModLoaderType modLoaderType : versionOptional.get().getLoaders()) { + for (ModLoaderType modLoaderType : versionOptional.get().loaders()) { String loaderName = switch (modLoaderType) { case FORGE -> i18n("install.installer.forge"); case CLEANROOM -> i18n("install.installer.cleanroom"); @@ -501,8 +500,8 @@ final class ModInfoDialog extends JFXDialogLayout { button.setOnAction(e -> { fireEvent(new DialogCloseEvent()); Controllers.navigate(new DownloadPage( - repository instanceof CurseForgeRemoteModRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false), - remoteMod, + repository instanceof CurseForgeRemoteAddonRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false), + remoteAddon, new Profile.ProfileVersion(ModListPageSkin.this.getSkinnable().getProfile(), ModListPageSkin.this.getSkinnable().getInstanceId()), org.jackhuang.hmcl.ui.download.DownloadPage.FOR_MOD )); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java index 73f168d0694..e7bb8328d46 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.ui.versions; -import org.jackhuang.hmcl.mod.RemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jetbrains.annotations.NotNull; @@ -56,7 +56,7 @@ public String getMcmodUrl(Mod mod) { } }; - public static ModTranslations getTranslationsByRepositoryType(RemoteModRepository.Type type) { + public static ModTranslations getTranslationsByRepositoryType(RemoteAddonRepository.Type type) { return switch (type) { case MOD -> MOD; case MODPACK -> MODPACK; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java index 120c1f3f7b9..37f6ca7ed03 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java @@ -37,11 +37,11 @@ import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.util.Duration; -import org.jackhuang.hmcl.mod.*; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; -import org.jackhuang.hmcl.resourcepack.ResourcePackFile; -import org.jackhuang.hmcl.resourcepack.ResourcePackManager; +import org.jackhuang.hmcl.addon.*; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; +import org.jackhuang.hmcl.addon.resourcepack.ResourcePackFile; +import org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager; import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.Profile; @@ -237,15 +237,15 @@ public void checkUpdates(Collection resourcePacks) { }) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception != null || result == null) { - Controllers.dialog(i18n("mods.check_updates.failed_check"), i18n("message.failed"), MessageDialogPane.MessageType.ERROR); + Controllers.dialog(i18n("addon.check_update.failed_check"), i18n("message.failed"), MessageDialogPane.MessageType.ERROR); } else if (result.isEmpty()) { - Controllers.dialog(i18n("mods.check_updates.empty")); + Controllers.dialog(i18n("addon.check_update.empty")); } else { Controllers.navigateForward(new AddonUpdatesPage<>(resourcePackManager, result)); } }) .withStagesHints("update.checking"), - i18n("mods.check_updates"), TaskCancellationAction.NORMAL); + i18n("addon.check_update"), TaskCancellationAction.NORMAL); if (profile.getRepository().isModpack(instanceId)) { Controllers.confirm( @@ -293,7 +293,7 @@ private ResourcePackListPageSkin(ResourcePackListPage control) { control.setSelectedEnabled(listView.getSelectionModel().getSelectedItems(), true)), createToolbarButton2(i18n("button.disable"), SVG.CLOSE, () -> control.setSelectedEnabled(listView.getSelectionModel().getSelectedItems(), false)), - createToolbarButton2(i18n("mods.check_updates.button"), SVG.UPDATE, () -> + createToolbarButton2(i18n("addon.check_update.button"), SVG.UPDATE, () -> control.checkUpdates( listView.getSelectionModel().getSelectedItems().stream().map(ResourcePackInfoObject::getFile).toList() ) @@ -336,7 +336,7 @@ private ResourcePackListPageSkin(ResourcePackListPage control) { createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, control::refresh), createToolbarButton2(i18n("resourcepack.add"), SVG.ADD, control::onAddFiles), createToolbarButton2(i18n("button.reveal_dir"), SVG.FOLDER_OPEN, control::onOpenFolder), - createToolbarButton2(i18n("mods.check_updates.button"), SVG.UPDATE, () -> + createToolbarButton2(i18n("addon.check_update.button"), SVG.UPDATE, () -> control.checkUpdates(listView.getItems().stream().map(ResourcePackInfoObject::getFile).toList()) ), createToolbarButton2(i18n("download"), SVG.DOWNLOAD, control::onDownload), @@ -601,24 +601,24 @@ private static final class ResourcePackInfoDialog extends JFXDialogLayout { setBody(descriptionPane); - for (Pair item : Arrays.asList( - pair("mods.curseforge", CurseForgeRemoteModRepository.RESOURCE_PACKS), - pair("mods.modrinth", ModrinthRemoteModRepository.RESOURCE_PACKS) + for (Pair item : Arrays.asList( + pair("addon.curseforge", CurseForgeRemoteAddonRepository.RESOURCE_PACKS), + pair("addon.modrinth", ModrinthRemoteAddonRepository.RESOURCE_PACKS) )) { - RemoteModRepository repository = item.getValue(); + RemoteAddonRepository repository = item.getValue(); JFXHyperlink button = new JFXHyperlink(i18n(item.getKey())); Task.runAsync(() -> { - Optional versionOptional = repository.getRemoteVersionByLocalFile(packInfoObject.getFile().getFile()); + Optional versionOptional = repository.getRemoteVersionByLocalFile(packInfoObject.getFile().getFile()); if (versionOptional.isPresent()) { - RemoteMod remoteMod = repository.getModById(DownloadProviders.getDownloadProvider(), versionOptional.get().getModid()); + RemoteAddon remoteAddon = repository.getModById(DownloadProviders.getDownloadProvider(), versionOptional.get().modid()); FXUtils.runInFX(() -> { button.setOnAction(e -> { fireEvent(new DialogCloseEvent()); Controllers.navigate(new DownloadPage( - repository instanceof CurseForgeRemoteModRepository + repository instanceof CurseForgeRemoteAddonRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeResourcePack(null, false) : HMCLLocalizedDownloadListPage.ofModrinthResourcePack(null, false), - remoteMod, + remoteAddon, new Profile.ProfileVersion(page.profile, page.instanceId), org.jackhuang.hmcl.ui.download.DownloadPage.FOR_RESOURCE_PACK )); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 1c9c79a0785..d364f7b093d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -28,7 +28,7 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.*; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Schedulers; @@ -76,15 +76,15 @@ public static void importModpack() { } } - public static void downloadModpackImpl(DownloadProvider downloadProvider, Profile profile, String version, RemoteMod mod, RemoteMod.Version file) { + public static void downloadModpackImpl(DownloadProvider downloadProvider, Profile profile, String version, RemoteAddon mod, RemoteAddon.Version file) { Path modpack; List downloadURLs; try { - downloadURLs = downloadProvider.injectURLWithCandidates(file.getFile().getUrl()); + downloadURLs = downloadProvider.injectURLWithCandidates(file.file().url()); modpack = Files.createTempFile("modpack", ".zip"); } catch (IOException | IllegalArgumentException e) { Controllers.dialog( - i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e), + i18n("install.failed.downloading.detail", file.file().url()) + "\n" + StringUtils.getStackTrace(e), i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR); return; } @@ -104,7 +104,7 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, Profil Controllers.showToast(i18n("message.cancelled")); } else { Controllers.dialog( - i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e), + i18n("install.failed.downloading.detail", file.file().url()) + "\n" + StringUtils.getStackTrace(e), i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR); } }), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index 5c545231ab3..efacae5c377 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -18,8 +18,8 @@ package org.jackhuang.hmcl.util; import org.jackhuang.hmcl.game.*; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.platform.Architecture; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 942fc5e9107..5d25ac215be 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -170,8 +170,33 @@ account.skin.upload.failed=Failed to upload skin. account.skin.invalid_skin=Invalid skin file. account.username=Username +addon.broken_dependency.title=Broken dependency +addon.broken_dependency.desc=This dependency existed before, but it does not exist anymore. Try using another download source. +addon.category=Category +addon.channel.alpha=Alpha +addon.channel.beta=Beta +addon.channel.release=Release +addon.check_update=File update process +addon.check_update.button=Update +addon.check_update.confirm=Update +addon.check_update.current_version=Current Version +addon.check_update.empty=All files are up-to-date +addon.check_update.failed_check=Failed to check for updates. +addon.check_update.failed_download=Failed to download some files. +addon.check_update.file=File +addon.check_update.source=Source +addon.check_update.target_version=Target Version +addon.curseforge=CurseForge +addon.dependency.embedded=Built-in Dependencies (Already packaged in the addon file by the author. No need to download separately) +addon.dependency.optional=Optional Dependencies (If missing, the game will run normally, but the addon features may be missing) +addon.dependency.required=Required Dependencies (Must be downloaded separately. Missing may prevent the game from launching) +addon.dependency.tool=Required Dependencies (Must be downloaded separately. Missing may prevent the game from launching) +addon.dependency.include=Built-in Dependencies (Already packaged in the addon file by the author. No need to download separately) +addon.dependency.incompatible=Incompatible Addons (Installing these addons at the same time will prevent the game from launching) +addon.dependency.broken=Broken Dependencies (This addon existed before, but it does not exist anymore. Try using another download source.) addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (Snapshots) +addon.modrinth=Modrinth archive.author=Author(s) archive.date=Publish Date @@ -1092,30 +1117,6 @@ mods.add=Add mods.add.failed=Failed to add mod %s. mods.add.success=%s was successfully added. mods.add.title=Choose mod file you want to add -mods.broken_dependency.title=Broken dependency -mods.broken_dependency.desc=This dependency existed before, but it does not exist anymore. Try using another download source. -mods.category=Category -mods.channel.alpha=Alpha -mods.channel.beta=Beta -mods.channel.release=Release -mods.check_updates=File update process -mods.check_updates.button=Update -mods.check_updates.confirm=Update -mods.check_updates.current_version=Current Version -mods.check_updates.empty=All files are up-to-date -mods.check_updates.failed_check=Failed to check for updates. -mods.check_updates.failed_download=Failed to download some files. -mods.check_updates.file=File -mods.check_updates.source=Source -mods.check_updates.target_version=Target Version -mods.curseforge=CurseForge -mods.dependency.embedded=Built-in Dependencies (Already packaged in the mod file by the author. No need to download separately) -mods.dependency.optional=Optional Dependencies (If missing, the game will run normally, but the mod features may be missing) -mods.dependency.required=Required Dependencies (Must be downloaded separately. Missing may prevent the game from launching) -mods.dependency.tool=Required Dependencies (Must be downloaded separately. Missing may prevent the game from launching) -mods.dependency.include=Built-in Dependencies (Already packaged in the mod file by the author. No need to download separately) -mods.dependency.incompatible=Incompatible Mods (Installing these mods at the same time will prevent the game from launching) -mods.dependency.broken=Broken Dependencies (This mod existed before, but it does not exist anymore. Try using another download source.) mods.disable=Disable mods.download=Download Mod mods.download.title=Download Mod - %1s @@ -1127,7 +1128,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMod mods.mcmod.page=MCMod Page mods.mcmod.search=Search in MCMod -mods.modrinth=Modrinth mods.name=Name mods.not_modded=You must install a modloader (Forge, NeoForge, Fabric, Legacy Fabric, Quilt, or LiteLoader) first to manage your mods! mods.restore=Restore diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 21084f05426..3de66c2c580 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -167,6 +167,24 @@ account.skin.upload.failed=فشل رفع المظهر. account.skin.invalid_skin=ملف المظهر غير صالح. account.username=اسم المستخدم +addon.broken_dependency.title=تبعية معطوبة +addon.broken_dependency.desc=كانت هذه التبعية موجودة مسبقاً، لكنها لم تعد كذلك. جرّب استخدام مصدر تنزيل آخر. +addon.category=الفئة +addon.channel.alpha=ألفا +addon.channel.beta=بيتا +addon.channel.release=إصدار رسمي +addon.check_update.button=تحديث +addon.check_update.confirm=تحديث +addon.check_update.current_version=الإصدار الحالي +addon.check_update.empty=جميع المودات محدّثة +addon.check_update.failed_check=فشل التحقق من التحديثات. +addon.check_update.failed_download=فشل تنزيل بعض الملفات. +addon.check_update.file=الملف +addon.check_update.source=المصدر +addon.check_update.target_version=الإصدار المستهدف +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=المؤلف/المؤلفون archive.date=تاريخ النشر archive.file.name=اسم الملف @@ -1060,30 +1078,6 @@ mods.add=إضافة mods.add.failed=فشل إضافة المود %s. mods.add.success=تمت إضافة %s بنجاح. mods.add.title=اختر ملف المود الذي تريد إضافته -mods.broken_dependency.title=تبعية معطوبة -mods.broken_dependency.desc=كانت هذه التبعية موجودة مسبقاً، لكنها لم تعد كذلك. جرّب استخدام مصدر تنزيل آخر. -mods.category=الفئة -mods.channel.alpha=ألفا -mods.channel.beta=بيتا -mods.channel.release=إصدار رسمي -mods.check_updates=عملية تحديث المودات -mods.check_updates.button=تحديث -mods.check_updates.confirm=تحديث -mods.check_updates.current_version=الإصدار الحالي -mods.check_updates.empty=جميع المودات محدّثة -mods.check_updates.failed_check=فشل التحقق من التحديثات. -mods.check_updates.failed_download=فشل تنزيل بعض الملفات. -mods.check_updates.file=الملف -mods.check_updates.source=المصدر -mods.check_updates.target_version=الإصدار المستهدف -mods.curseforge=CurseForge -mods.dependency.embedded=تبعيات مدمجة (مضمّنة مسبقاً في ملف المود من قِبل المطوّر، لا حاجة لتنزيلها) -mods.dependency.optional=تبعيات اختيارية (إذا كانت مفقودة، ستعمل اللعبة بشكل طبيعي لكن بعض ميزات المود قد تكون غائبة) -mods.dependency.required=تبعيات مطلوبة (يجب تنزيلها بشكل منفصل، غيابها قد يمنع تشغيل اللعبة) -mods.dependency.tool=تبعيات مطلوبة (يجب تنزيلها بشكل منفصل، غيابها قد يمنع تشغيل اللعبة) -mods.dependency.include=تبعيات مدمجة (مضمّنة مسبقاً في ملف المود من قِبل المطوّر، لا حاجة لتنزيلها) -mods.dependency.incompatible=مودات غير متوافقة (تثبيت هذه المودات معاً سيمنع تشغيل اللعبة) -mods.dependency.broken=تبعيات معطوبة (كان هذا المود موجوداً مسبقاً، لكنه لم يعد كذلك. جرّب استخدام مصدر تنزيل آخر.) mods.disable=تعطيل mods.download=تنزيل مود mods.download.title=تنزيل مود - %1s @@ -1095,7 +1089,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMod mods.mcmod.page=صفحة MCMod mods.mcmod.search=البحث في MCMod -mods.modrinth=Modrinth mods.name=الاسم mods.not_modded=يجب تثبيت محمّل مودات (Forge أو NeoForge أو Fabric أو Legacy Fabric أو Quilt أو LiteLoader) أولاً لإدارة موداتك! mods.restore=استعادة diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index f22265d1d95..c56648fea33 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -148,6 +148,24 @@ account.skin.upload.failed=No se pudo subir el aspecto. account.skin.invalid_skin=Archivo de aspecto inválido. account.username=Nombre de usuario +addon.broken_dependency.title=Dependencia rota +addon.broken_dependency.desc=Esta dependencia existía antes, pero ahora no existe. Intente utilizar otra fuente de descarga. +addon.category=Categoría +addon.channel.alpha=Alpha +addon.channel.beta=Beta +addon.channel.release=Release +addon.check_update.button=Actualizar +addon.check_update.confirm=Actualizar +addon.check_update.current_version=Versión actual +addon.check_update.empty=Todos los mods están actualizados +addon.check_update.failed_check=No se ha podido comprobar si hay actualizaciones. +addon.check_update.failed_download=No se han podido descargar algunos de los archivos. +addon.check_update.file=Archivo +addon.check_update.source=Fuente +addon.check_update.target_version=Versión de destino +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=Autor(es) archive.date=Fecha de publicación archive.file.name=Nombre de archivo @@ -1018,30 +1036,6 @@ modrinth.category.512x+=512x+ mods=Mods mods.add.failed=No se ha podido añadir el mod %s. mods.add.success=%s se ha añadido correctamente. -mods.broken_dependency.title=Dependencia rota -mods.broken_dependency.desc=Esta dependencia existía antes, pero ahora no existe. Intente utilizar otra fuente de descarga. -mods.category=Categoría -mods.channel.alpha=Alpha -mods.channel.beta=Beta -mods.channel.release=Release -mods.check_updates=Proceso de actualización de mods -mods.check_updates.button=Actualizar -mods.check_updates.confirm=Actualizar -mods.check_updates.current_version=Versión actual -mods.check_updates.empty=Todos los mods están actualizados -mods.check_updates.failed_check=No se ha podido comprobar si hay actualizaciones. -mods.check_updates.failed_download=No se han podido descargar algunos de los archivos. -mods.check_updates.file=Archivo -mods.check_updates.source=Fuente -mods.check_updates.target_version=Versión de destino -mods.curseforge=CurseForge -mods.dependency.embedded=Dependencias incorporadas (Already packaged in the mod file by the author. No need to download separately) -mods.dependency.optional=Dependencias opcionales (If missing, the game will run normally, but the mod features may be missing) -mods.dependency.required=Dependencias necesarias (Must be downloaded separately. Missing may cause the game to fail to launch) -mods.dependency.tool=Dependencias necesarias (Must be downloaded separately. Missing may cause the game to fail to launch) -mods.dependency.include=Dependencias incorporadas (Already packaged in the mod file by the author, no need to download separately) -mods.dependency.incompatible=Mods incompatibles (Installing these mods at the same time will cause the game to fail to launch) -mods.dependency.broken=Dependencias rotas (This mod existed before, but it does not exist now. Try using another download source.) mods.disable=Desactivar mods.download=Descargar mod mods.download.title=Descargar mod - %1s @@ -1053,7 +1047,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMod mods.mcmod.page=Página de MCMod mods.mcmod.search=Búsqueda en MCMod -mods.modrinth=Modrinth mods.name=Nombre mods.not_modded=¡Debes instalar primero un cargador de mods (Forge, NeoForge, Fabric, Legacy Fabric, Quilt o LiteLoader) para gestionar tus mods! mods.restore=Restaurar diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 0fb7ce8dd13..585d305d54a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -117,6 +117,18 @@ account.skin.upload.failed=スキンのアップロードに失敗しました account.skin.invalid_skin=認識されないスキンファイル account.username=ユーザー名 +addon.category=Category +addon.check_update.button=更新 +addon.check_update.confirm=更新 +addon.check_update.current_version=Current +addon.check_update.failed_check=更新のチェックに失敗しました +addon.check_update.failed_download=一部のファイルのダウンロードに失敗しました +addon.check_update.file=ファイル +addon.check_update.source=Source +addon.check_update.target_version=Target +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=作成者 archive.date=公開日 archive.file.name=名前 @@ -653,17 +665,6 @@ modrinth.category.folia=Folia mods=Mods mods.add.failed=mods %s の追加に失敗しました。 mods.add.success=mods %s が正常に追加されました。 -mods.category=Category -mods.check_updates=更新を確認 -mods.check_updates.button=更新 -mods.check_updates.confirm=更新 -mods.check_updates.current_version=Current -mods.check_updates.failed_check=更新のチェックに失敗しました -mods.check_updates.failed_download=一部のファイルのダウンロードに失敗しました -mods.check_updates.file=ファイル -mods.check_updates.source=Source -mods.check_updates.target_version=Target -mods.curseforge=CurseForge mods.disable=無効にする mods.download=Modのダウンロード mods.download.title=Modダウンロード- %1s @@ -673,7 +674,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMOD mods.mcmod.page=MCMOD mods.mcmod.search=MCMODで検索 -mods.modrinth=Modrinth mods.name=Name mods.not_modded=最初にmodloaderをインストールする必要があります(Fabric、Forge、またはLiteLoader) mods.restore=Restore diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 995dcf51346..4a26257ca42 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -149,6 +149,24 @@ account.skin.upload.failed=匯之外觀而未成 account.skin.invalid_skin=外觀之案有謬 account.username=戶名 +addon.broken_dependency.title=所依之壞者 +addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 +addon.category=類 +addon.channel.alpha=預版 +addon.channel.beta=試版 +addon.channel.release=當版 +addon.check_update.button=檢改囊之新 +addon.check_update.confirm=迭更 +addon.check_update.current_version=當版 +addon.check_update.empty=無改囊可迭更 +addon.check_update.failed_check=檢囊迭更未成 +addon.check_update.failed_download=有引案未成 +addon.check_update.file=案 +addon.check_update.source=源 +addon.check_update.target_version=將至之版 +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=作者 archive.date=廣佈之期 archive.file.name=案名 @@ -830,30 +848,6 @@ mods.add=增改囊 mods.add.failed=增改囊「%s」未成。\n君可求助於右上之鈕。 mods.add.success=增改囊 %s 畢。 mods.add.title=擇改囊 -mods.broken_dependency.title=所依之壞者 -mods.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 -mods.category=類 -mods.channel.alpha=預版 -mods.channel.beta=試版 -mods.channel.release=當版 -mods.check_updates=檢改囊之新 -mods.check_updates.button=檢改囊之新 -mods.check_updates.confirm=迭更 -mods.check_updates.current_version=當版 -mods.check_updates.empty=無改囊可迭更 -mods.check_updates.failed_check=檢囊迭更未成 -mods.check_updates.failed_download=有引案未成 -mods.check_updates.file=案 -mods.check_updates.source=源 -mods.check_updates.target_version=將至之版 -mods.curseforge=CurseForge -mods.dependency.embedded=既存之相依改囊 (既以內於改囊案,無須他引) -mods.dependency.optional=可選之相依改囊 (设若阙如,戲亦能行) -mods.dependency.required=所需之改囊 (須他引,失之則無啟戲也) -mods.dependency.tool=相依之庫 (須他引,失之則無啟戲也) -mods.dependency.include=既存之相依改囊 (既以內於改囊案,無須他引) -mods.dependency.incompatible=難兼之改囊 (並載此改囊及所引改囊將無啟戲也) -mods.dependency.broken=既損之相依改囊 (夫改囊素存於改囊庫,今闕矣,宜易他源) mods.disable=禁此改囊 mods.download=引改囊 mods.download.title=引改囊 - %1s @@ -865,7 +859,6 @@ mods.mcbbs=礦藝館肆 mods.mcmod=礦藝改囊典 mods.mcmod.page=礦藝改囊典頁 mods.mcmod.search=礦藝改囊典查 -mods.modrinth=Modrinth mods.name=名 mods.not_modded=君須先於「自裝」頁裝鍛、新鍛、緞、褥或輕載之屬,方司改囊。 mods.restore=復 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 8aea8b826a3..637af0b88ab 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -147,6 +147,24 @@ account.skin.upload.failed=Не удалось загрузить скин. account.skin.invalid_skin=Недопустимый файл скина. account.username=Имя пользователя +addon.broken_dependency.title=Сломанная зависимость +addon.broken_dependency.desc=Эта зависимость существовала раньше, но теперь ее нет. Попробуйте использовать другой источник скачивания. +addon.category=Категория +addon.channel.alpha=Альфа +addon.channel.beta=Бета +addon.channel.release=Релиз +addon.check_update.button=Обновить +addon.check_update.confirm=Обновить +addon.check_update.current_version=Текущая версия +addon.check_update.empty=Все моды новейшие +addon.check_update.failed_check=Не удалось проверить обновления. +addon.check_update.failed_download=Не удалось скачать некоторые файлы. +addon.check_update.file=Файл +addon.check_update.source=Источник +addon.check_update.target_version=Целевая версия +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=Автор(ы) archive.date=Дата публикации archive.file.name=Имя файла @@ -1011,30 +1029,6 @@ modrinth.category.512x+=512x+ mods=Моды mods.add.failed=Не удалось установить мод %s. mods.add.success=%s был успешно добавлен. -mods.broken_dependency.title=Сломанная зависимость -mods.broken_dependency.desc=Эта зависимость существовала раньше, но теперь ее нет. Попробуйте использовать другой источник скачивания. -mods.category=Категория -mods.channel.alpha=Альфа -mods.channel.beta=Бета -mods.channel.release=Релиз -mods.check_updates=Проверить обновления -mods.check_updates.button=Обновить -mods.check_updates.confirm=Обновить -mods.check_updates.current_version=Текущая версия -mods.check_updates.empty=Все моды новейшие -mods.check_updates.failed_check=Не удалось проверить обновления. -mods.check_updates.failed_download=Не удалось скачать некоторые файлы. -mods.check_updates.file=Файл -mods.check_updates.source=Источник -mods.check_updates.target_version=Целевая версия -mods.curseforge=CurseForge -mods.dependency.embedded=Встроенные зависимости (Уже упакован в файл мода автором. Нет необходимости скачивать отдельно.) -mods.dependency.optional=Необязательные зависимости (Если он отсутствует, игра будет работать нормально, но функции мода могут отсутствовать.) -mods.dependency.required=Необходимые зависимости (Необходимо скачивать отдельно, отсутствие может помешать запуску игры.) -mods.dependency.tool=Необходимые зависимости (Необходимо скачивать отдельно, отсутствие может помешать запуску игры.) -mods.dependency.include=Встроенные зависимости (Уже упакован в файл мода автором. Нет необходимости скачивать отдельно.) -mods.dependency.incompatible=Несовместимые моды (Одновременная установка этих модов не позволит запустить игру.) -mods.dependency.broken=Сломанные зависимости (Этот мод существовал раньше, но теперь его нет. Попробуйте использовать другой источник скачивания.) mods.disable=Отключить mods.download=Скачать мод mods.download.title=Скачать мод - %1s @@ -1046,7 +1040,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMod mods.mcmod.page=Страница MCMod mods.mcmod.search=Искать на MCMod -mods.modrinth=Modrinth mods.name=Название mods.not_modded=Для управления модами необходимо сначала установить загрузчик (Forge, NeoForge, Fabric, Legacy Fabric, Quilt или LiteLoader)! mods.restore=Восстановить diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index c50a1ec6f65..eb9c6310b96 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -147,6 +147,24 @@ account.skin.upload.failed=Не вдалося завантажити скін. account.skin.invalid_skin=Недійсний файл скіна. account.username=Ім'я користувача +addon.broken_dependency.title=Зламана залежність +addon.broken_dependency.desc=Ця залежність існувала раніше, але тепер її немає. Спробуйте використовувати інше джерело завантаження. +addon.category=Категорія +addon.channel.alpha=Альфа +addon.channel.beta=Бета +addon.channel.release=Реліз +addon.check_update.confirm=Оновити +addon.check_update.button=Оновити +addon.check_update.current_version=Поточна версія +addon.check_update.empty=Усі моди оновлені +addon.check_update.failed_check=Не вдалося перевірити оновлення. +addon.check_update.failed_download=Не вдалося завантажити деякі файли. +addon.check_update.file=Файл +addon.check_update.source=Джерело +addon.check_update.target_version=Цільова версія +addon.curseforge=CurseForge +addon.modrinth=Modrinth + archive.author=Автор(и) archive.date=Дата публікації archive.file.name=Ім'я файлу @@ -958,30 +976,6 @@ modrinth.category.512x+=512x+ mods=Моди mods.add.failed=Не вдалося додати мод %s. mods.add.success=%s успішно додано. -mods.broken_dependency.title=Зламана залежність -mods.broken_dependency.desc=Ця залежність існувала раніше, але тепер її немає. Спробуйте використовувати інше джерело завантаження. -mods.category=Категорія -mods.channel.alpha=Альфа -mods.channel.beta=Бета -mods.channel.release=Реліз -mods.check_updates=Перевірити оновлення -mods.check_updates.confirm=Оновити -mods.check_updates.button=Оновити -mods.check_updates.current_version=Поточна версія -mods.check_updates.empty=Усі моди оновлені -mods.check_updates.failed_check=Не вдалося перевірити оновлення. -mods.check_updates.failed_download=Не вдалося завантажити деякі файли. -mods.check_updates.file=Файл -mods.check_updates.source=Джерело -mods.check_updates.target_version=Цільова версія -mods.curseforge=CurseForge -mods.dependency.embedded=Вбудовані залежності (Вже запаковані в файл мода автором. Не потрібно завантажувати окремо) -mods.dependency.optional=Необов'язкові залежності (Якщо відсутні, гра буде працювати нормально, але функції мода можуть бути відсутні) -mods.dependency.required=Необхідні залежності (Потрібно завантажити окремо. Відсутність може завадити запуску гри) -mods.dependency.tool=Необхідні залежності (Потрібно завантажити окремо. Відсутність може завадити запуску гри) -mods.dependency.include=Вбудовані залежності (Вже запаковані в файл мода автором. Не потрібно завантажувати окремо) -mods.dependency.incompatible=Несумісні моди (Встановлення цих модів одночасно завадить запуску гри) -mods.dependency.broken=Зламані залежності (Цей мод існував раніше, але тепер його немає. Спробуйте використовувати інше джерело завантаження.) mods.disable=Вимкнути mods.download=Завантажити мод mods.download.title=Завантажити мод - %1s @@ -993,7 +987,6 @@ mods.mcbbs=MCBBS mods.mcmod=MCMod mods.mcmod.page=Сторінка MCMod mods.mcmod.search=Пошук в MCMod -mods.modrinth=Modrinth mods.name=Назва mods.not_modded=Ви повинні спочатку встановити завантажувач модів (Forge, NeoForge, Fabric, Legacy Fabric, Quilt або LiteLoader), щоб керувати своїми модами! mods.restore=Відновити diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 036f032f063..8c4d2296f19 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -168,8 +168,33 @@ account.skin.upload.failed=外觀上傳失敗 account.skin.invalid_skin=無法識別的外觀檔案 account.username=使用者名稱 +addon.broken_dependency.title=損壞的相依模組 +addon.broken_dependency.desc=該相依內容曾經存在於遊戲內容下載源中,但現在已被刪除,請嘗試其他下載源。 +addon.category=類別 +addon.channel.alpha=Alpha +addon.channel.beta=Beta +addon.channel.release=Release +addon.check_update=檔案更新檢查 +addon.check_update.button=檢查更新 +addon.check_update.confirm=更新 +addon.check_update.current_version=目前版本 +addon.check_update.empty=沒有需要更新的檔案 +addon.check_update.failed_check=檢查更新失敗 +addon.check_update.failed_download=部分檔案下載失敗 +addon.check_update.file=檔案 +addon.check_update.source=來源 +addon.check_update.target_version=目標版本 +addon.curseforge=CurseForge +addon.dependency.embedded=內建相依內容 (作者已經打包在檔案中,無需單獨下載) +addon.dependency.optional=可選相依內容 (如果不安裝,遊戲可以執行,但功能可能缺失) +addon.dependency.required=必需相依內容 (必須單獨下載,缺少可能會導致遊戲無法啟動) +addon.dependency.tool=相依庫 (必須單獨下載,缺少可能會導致遊戲無法啟動) +addon.dependency.include=內建相依內容 (作者已經打包在檔案中,無需單獨下載) +addon.dependency.incompatible=不相容內容 (與正在下載的檔案同時安裝會導致遊戲無法啟動) +addon.dependency.broken=損壞的相依內容 (該相依內容曾經存在於遊戲內容下載源中,但現在已被刪除,請嘗試其他下載源) addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (快照) +addon.modrinth=Modrinth archive.author=作者 archive.date=發布日期 @@ -889,30 +914,6 @@ mods.add=新增模組 mods.add.failed=新增模組「%s」失敗。 mods.add.success=成功新增模組「%s」。 mods.add.title=選取要新增的模組檔案 -mods.broken_dependency.title=損壞的相依模組 -mods.broken_dependency.desc=該相依模組曾經存在於模組儲存庫中,但現在已被刪除,請嘗試其他下載源。 -mods.category=類別 -mods.channel.alpha=Alpha -mods.channel.beta=Beta -mods.channel.release=Release -mods.check_updates=檔案更新檢查 -mods.check_updates.button=檢查更新 -mods.check_updates.confirm=更新 -mods.check_updates.current_version=目前版本 -mods.check_updates.empty=沒有需要更新的檔案 -mods.check_updates.failed_check=檢查更新失敗 -mods.check_updates.failed_download=部分檔案下載失敗 -mods.check_updates.file=檔案 -mods.check_updates.source=來源 -mods.check_updates.target_version=目標版本 -mods.curseforge=CurseForge -mods.dependency.embedded=內建相依模組 (作者已經打包在模組檔中,無需單獨下載) -mods.dependency.optional=可選相依模組 (如果不安裝,遊戲可以執行,但模組功能可能缺失) -mods.dependency.required=必需相依模組 (必須單獨下載,缺少可能會導致遊戲無法啟動) -mods.dependency.tool=相依庫 (必須單獨下載,缺少可能會導致遊戲無法啟動) -mods.dependency.include=內建相依模組 (作者已經打包在模組檔中,無需單獨下載) -mods.dependency.incompatible=不相容模組 (同時安裝此模組和正在下載的模組會導致遊戲無法啟動) -mods.dependency.broken=損壞的相依模組 (該模組曾經存在於模組儲存庫中,但現在已被刪除,請嘗試其他下載源) mods.disable=停用 mods.download=模組下載 mods.download.title=模組下載 - %1s @@ -924,7 +925,6 @@ mods.mcbbs=MCBBS mods.mcmod=MC 百科 mods.mcmod.page=MC 百科頁面 mods.mcmod.search=MC 百科檢索 -mods.modrinth=Modrinth mods.name=名稱 mods.not_modded=你需要先在「自動安裝」頁面安裝 Forge、NeoForge、Fabric、Legacy Fabric、Quilt 或 LiteLoader 才能管理模組。 mods.restore=回退 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 27e079e33fb..14c2903807a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -170,8 +170,33 @@ account.skin.upload.failed=皮肤上传失败 account.skin.invalid_skin=无法识别的皮肤文件 account.username=用户名 +addon.broken_dependency.title=损坏的前置 +addon.broken_dependency.desc=该前置内容曾经在该游戏内容下载源上存在过,但现在被删除了。换个下载源试试吧。 +addon.category=类别 +addon.channel.alpha=快照版本 +addon.channel.beta=测试版本 +addon.channel.release=稳定版本 +addon.check_update=文件更新检查 +addon.check_update.button=检查更新 +addon.check_update.confirm=更新 +addon.check_update.current_version=当前版本 +addon.check_update.empty=没有需要更新的文件 +addon.check_update.failed_check=检查更新失败 +addon.check_update.failed_download=部分文件下载失败 +addon.check_update.file=文件 +addon.check_update.source=来源 +addon.check_update.target_version=目标版本 +addon.curseforge=CurseForge +addon.dependency.embedded=内置的前置内容 (已经由作者打包在文件中,无需另外下载) +addon.dependency.optional=可选的前置内容 (若缺失游戏能够正常运行,但功能可能缺失) +addon.dependency.required=必须的前置内容 (必须另外下载,缺失可能会导致游戏无法启动) +addon.dependency.tool=前置库 (必须另外下载,缺失可能会导致游戏无法启动) +addon.dependency.include=内置的前置内容 (已经由作者打包在文件中,无需另外下载) +addon.dependency.incompatible=不兼容的游戏内容 (同时与正在下载的文件安装会导致游戏无法启动) +addon.dependency.broken=损坏的前置内容 (该前置内容曾经在该游戏内容下载源上存在过,但现在被删除了,换个下载源试试吧) addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (快照) +addon.modrinth=Modrinth archive.author=作者 archive.date=发布日期 @@ -894,30 +919,6 @@ mods.add=添加模组 mods.add.failed=添加模组“%s”失败。\n如遇到问题,你可以点击右上角帮助按钮进行求助。 mods.add.success=成功添加模组 %s。 mods.add.title=选择要添加的模组文件 -mods.broken_dependency.title=损坏的前置模组 -mods.broken_dependency.desc=该前置模组曾经在该模组仓库上存在过,但现在被删除了。换个下载源试试吧。 -mods.category=类别 -mods.channel.alpha=快照版本 -mods.channel.beta=测试版本 -mods.channel.release=稳定版本 -mods.check_updates=文件更新检查 -mods.check_updates.button=检查更新 -mods.check_updates.confirm=更新 -mods.check_updates.current_version=当前版本 -mods.check_updates.empty=没有需要更新的文件 -mods.check_updates.failed_check=检查更新失败 -mods.check_updates.failed_download=部分文件下载失败 -mods.check_updates.file=文件 -mods.check_updates.source=来源 -mods.check_updates.target_version=目标版本 -mods.curseforge=CurseForge -mods.dependency.embedded=内置的前置模组 (已经由作者打包在模组文件中,无需另外下载) -mods.dependency.optional=可选的前置模组 (若缺失游戏能够正常运行,但模组功能可能缺失) -mods.dependency.required=必须的前置模组 (必须另外下载,缺失可能会导致游戏无法启动) -mods.dependency.tool=前置库 (必须另外下载,缺失可能会导致游戏无法启动) -mods.dependency.include=内置的前置模组 (已经由作者打包在模组文件中,无需另外下载) -mods.dependency.incompatible=不兼容的模组 (同时安装该模组和正在下载的模组会导致游戏无法启动) -mods.dependency.broken=损坏的前置模组 (该前置模组曾经在该模组仓库上存在过,但现在被删除了,换个下载源试试吧) mods.disable=禁用 mods.download=模组下载 mods.download.title=模组下载 - %1s @@ -929,7 +930,6 @@ mods.mcbbs=MCBBS mods.mcmod=MC 百科 mods.mcmod.page=MC 百科页面 mods.mcmod.search=MC 百科搜索 -mods.modrinth=Modrinth mods.name=名称 mods.not_modded=你需要先在“自动安装”页面安装 Forge、NeoForge、Fabric、Legacy Fabric、Quilt 或 LiteLoader 才能管理模组。 mods.restore=回退 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonFile.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java index 15183a74d3e..ab6c9d4aeca 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.util.StringUtils; @@ -53,12 +53,12 @@ public boolean isDisabled() { public abstract void delete() throws IOException; @Nullable - public abstract AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteMod.Type type) throws IOException; + public abstract AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteAddon.Source source) throws IOException; public record AddonUpdate( LocalAddonFile localAddonFile, - RemoteMod.Version currentVersion, - RemoteMod.Version targetVersion, + RemoteAddon.Version currentVersion, + RemoteAddon.Version targetVersion, boolean useRemoteFileName ) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonManager.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java index c63b6fdca82..0520b67a8fc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddon.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddon.java new file mode 100644 index 00000000000..edc1f905317 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddon.java @@ -0,0 +1,283 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 huangyuhui 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 . + */ +package org.jackhuang.hmcl.addon; + +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; +import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public final class RemoteAddon { + + public static final RemoteAddon BROKEN = new RemoteAddon("", "", "RemoteAddon.BROKEN", "", Collections.emptyList(), "", "", new RemoteAddon.IMod() { + @Override + public List loadDependencies(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + throw new IOException(); + } + + @Override + public Stream loadVersions(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + throw new IOException(); + } + }, RemoteAddonRepository.Type.MOD); + + private final String slug; + private final String author; + private final String title; + private final String description; + private final List categories; + private final String pageUrl; + private final String iconUrl; + private final IMod data; + private final RemoteAddonRepository.Type repoType; + + public RemoteAddon(String slug, String author, String title, String description, List categories, String pageUrl, String iconUrl, IMod data, RemoteAddonRepository.Type repoType) { + this.slug = slug; + this.author = author; + this.title = title; + this.description = description; + this.categories = categories; + this.pageUrl = pageUrl; + this.iconUrl = iconUrl; + this.data = data; + this.repoType = repoType; + } + + public String getSlug() { + return slug; + } + + public String getAuthor() { + return author; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getCategories() { + return categories; + } + + public String getPageUrl() { + return pageUrl; + } + + public String getIconUrl() { + return iconUrl; + } + + public IMod getData() { + return data; + } + + public RemoteAddonRepository.Type getRepositoryType() { + return repoType; + } + + public enum VersionType { + Release, + Beta, + Alpha + } + + public enum DependencyType { + REQUIRED, + OPTIONAL, + TOOL, + INCLUDE, + EMBEDDED, + INCOMPATIBLE, + BROKEN + } + + public static final class Dependency { + private static Dependency BROKEN_DEPENDENCY = null; + + private final DependencyType type; + + private final RemoteAddonRepository remoteAddonRepository; + + private final String id; + + private transient RemoteAddon remoteAddon = null; + + private Dependency(DependencyType type, RemoteAddonRepository remoteAddonRepository, String modid) { + this.type = type; + this.remoteAddonRepository = remoteAddonRepository; + this.id = modid; + } + + public static Dependency ofGeneral(DependencyType type, RemoteAddonRepository remoteAddonRepository, String modid) { + if (type == DependencyType.BROKEN) { + return ofBroken(); + } else { + return new Dependency(type, remoteAddonRepository, modid); + } + } + + public static Dependency ofBroken() { + if (BROKEN_DEPENDENCY == null) { + BROKEN_DEPENDENCY = new Dependency(DependencyType.BROKEN, null, null); + } + return BROKEN_DEPENDENCY; + } + + public DependencyType getType() { + return this.type; + } + + public RemoteAddonRepository getRemoteModRepository() { + return this.remoteAddonRepository; + } + + public String getId() { + return this.id; + } + + public RemoteAddon load(DownloadProvider downloadProvider) throws IOException { + if (this.remoteAddon == null) { + if (this.type == DependencyType.BROKEN) { + this.remoteAddon = RemoteAddon.BROKEN; + } else { + this.remoteAddon = this.remoteAddonRepository.resolveDependency(downloadProvider, this.id); + } + } + return this.remoteAddon; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Dependency that = (Dependency) o; + + if (type != that.type) return false; + if (!remoteAddonRepository.equals(that.remoteAddonRepository)) return false; + return id.equals(that.id); + } + + @Override + public int hashCode() { + int result = type.hashCode(); + result = 31 * result + remoteAddonRepository.hashCode(); + result = 31 * result + id.hashCode(); + return result; + } + } + + public enum Source { + CURSEFORGE( + CurseForgeRemoteAddonRepository.MODS, + CurseForgeRemoteAddonRepository.RESOURCE_PACKS, + CurseForgeRemoteAddonRepository.SHADERS, + CurseForgeRemoteAddonRepository.WORLDS, + CurseForgeRemoteAddonRepository.MODPACKS, + CurseForgeRemoteAddonRepository.CUSTOMIZATIONS + ), + MODRINTH( + ModrinthRemoteAddonRepository.MODS, + ModrinthRemoteAddonRepository.RESOURCE_PACKS, + ModrinthRemoteAddonRepository.SHADER_PACKS, + null, + ModrinthRemoteAddonRepository.MODPACKS, + null + ); + + public final RemoteAddonRepository modRepo; + public final RemoteAddonRepository resourcePackRepo; + public final RemoteAddonRepository shaderPackRepo; + public final RemoteAddonRepository worldRepo; + public final RemoteAddonRepository modpackRepo; + public final RemoteAddonRepository customizationRepo; + + @Nullable + public RemoteAddonRepository getRepoForType(RemoteAddonRepository.Type type) { + return switch (type) { + case MOD -> modRepo; + case RESOURCE_PACK -> resourcePackRepo; + case SHADER_PACK -> shaderPackRepo; + case WORLD -> worldRepo; + case MODPACK -> modpackRepo; + case CUSTOMIZATION -> customizationRepo; + }; + } + + Source( + RemoteAddonRepository modRepo, + RemoteAddonRepository resourcePackRepo, + RemoteAddonRepository shaderPackRepo, + RemoteAddonRepository worldRepo, + RemoteAddonRepository modpackRepo, + RemoteAddonRepository customizationRepo + ) { + this.modRepo = modRepo; + this.resourcePackRepo = resourcePackRepo; + this.shaderPackRepo = shaderPackRepo; + this.worldRepo = worldRepo; + this.modpackRepo = modpackRepo; + this.customizationRepo = customizationRepo; + } + } + + public interface IMod { + List loadDependencies(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException; + + Stream loadVersions(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException; + } + + public interface IVersion { + Source getType(); + } + + public record Version(IVersion self, String modid, String name, String version, String changelog, + Instant datePublished, VersionType versionType, File file, List dependencies, + List gameVersions, List loaders) { + } + + public record File(Map hashes, String url, String filename) { + + public FileDownloadTask.IntegrityCheck getIntegrityCheck() { + if (hashes.containsKey("md5")) { + return new FileDownloadTask.IntegrityCheck("MD5", hashes.get("md5")); + } else if (hashes.containsKey("sha1")) { + return new FileDownloadTask.IntegrityCheck("SHA-1", hashes.get("sha1")); + } else if (hashes.containsKey("sha256")) { + return new FileDownloadTask.IntegrityCheck("SHA-256", hashes.get("sha256")); + } else if (hashes.containsKey("sha512")) { + return new FileDownloadTask.IntegrityCheck("SHA-512", hashes.get("sha512")); + } else { + return null; + } + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java similarity index 70% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java index 73bdad93658..f01ef60c293 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteModRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon; import org.jackhuang.hmcl.download.DownloadProvider; import org.jetbrains.annotations.Nullable; @@ -26,7 +26,7 @@ import java.util.Optional; import java.util.stream.Stream; -public interface RemoteModRepository { +public interface RemoteAddonRepository { enum Type { MOD, @@ -54,29 +54,29 @@ enum SortOrder { } class SearchResult { - private final Stream sortedResults; + private final Stream sortedResults; - private final Stream unsortedResults; + private final Stream unsortedResults; private final int totalPages; - public SearchResult(Stream sortedResults, Stream unsortedResults, int totalPages) { + public SearchResult(Stream sortedResults, Stream unsortedResults, int totalPages) { this.sortedResults = sortedResults; this.unsortedResults = unsortedResults; this.totalPages = totalPages; } - public SearchResult(Stream sortedResults, int pages) { + public SearchResult(Stream sortedResults, int pages) { this.sortedResults = sortedResults; this.unsortedResults = sortedResults; this.totalPages = pages; } - public Stream getResults() { + public Stream getResults() { return this.sortedResults; } - public Stream getUnsortedResults() { + public Stream getUnsortedResults() { return this.unsortedResults; } @@ -88,17 +88,17 @@ public int getTotalPages() { SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException; - Optional getRemoteVersionByLocalFile(Path file) throws IOException; + Optional getRemoteVersionByLocalFile(Path file) throws IOException; - RemoteMod getModById(DownloadProvider downloadProvider, String id) throws IOException; + RemoteAddon getModById(DownloadProvider downloadProvider, String id) throws IOException; - default RemoteMod resolveDependency(DownloadProvider downloadProvider, String id) throws IOException { + default RemoteAddon resolveDependency(DownloadProvider downloadProvider, String id) throws IOException { return getModById(downloadProvider, id); } - RemoteMod.File getModFile(String modId, String fileId) throws IOException; + RemoteAddon.File getModFile(String modId, String fileId) throws IOException; - Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException; + Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException; Stream getCategories() throws IOException; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/DataPack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/datapack/DataPack.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/DataPack.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/datapack/DataPack.java index 769f025e179..6c15efbe74d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/DataPack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/datapack/DataPack.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.datapack; import com.google.gson.JsonParseException; import javafx.application.Platform; @@ -23,7 +23,8 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.jackhuang.hmcl.mod.modinfo.PackMcMeta; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/FabricModMetadata.java similarity index 92% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/FabricModMetadata.java index 84589e5cd1f..4601ed1ca5d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/FabricModMetadata.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; import kala.compress.archivers.zip.ZipArchiveEntry; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.tree.ZipFileTree; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeNewModMetadata.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeNewModMetadata.java index a30f9960547..34d72756977 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeNewModMetadata.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; @@ -25,10 +25,10 @@ import com.google.gson.JsonPrimitive; import com.google.gson.annotations.JsonAdapter; import kala.compress.archivers.zip.ZipArchiveEntry; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java index 4bc64a6c4e8..81f358c94e6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,17 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import kala.compress.archivers.zip.ZipArchiveEntry; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadataLst.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadataLst.java similarity index 89% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadataLst.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadataLst.java index b0ac0e8bae5..25909ac0ccd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadataLst.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadataLst.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import org.jackhuang.hmcl.util.gson.JsonSerializable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/LiteModMetadata.java similarity index 92% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/LiteModMetadata.java index 36e43339b05..aea8c4fab2e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/LiteModMetadata.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveEntry; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.tree.ZipFileTree; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/PackMcMeta.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/PackMcMeta.java index fd8056f162a..3cc4d34e43c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/PackMcMeta.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.mod.LocalAddonFile; +import org.jackhuang.hmcl.addon.LocalAddonFile; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonSerializable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/QuiltModMetadata.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/QuiltModMetadata.java index 0b3af36d2ae..02e31ed7269 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/QuiltModMetadata.java @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modinfo; +package org.jackhuang.hmcl.addon.meta; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveEntry; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.ModManager; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.tree.ZipFileTree; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalMod.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalMod.java similarity index 93% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalMod.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalMod.java index 98233fa9f81..4a37e0d8aad 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalMod.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalMod.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.mod; import java.util.HashSet; import java.util.Objects; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalModFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java similarity index 84% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalModFile.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java index 235e5b36f9a..f4bfafab115 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalModFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,10 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.mod; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.util.io.FileUtils; @@ -191,16 +195,16 @@ public void delete() throws IOException { } @Override - public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteMod.Type type) throws IOException { - RemoteModRepository repository = type.getRepoForType(RemoteModRepository.Type.MOD); + public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteAddon.Source source) throws IOException { + RemoteAddonRepository repository = source.getRepoForType(RemoteAddonRepository.Type.MOD); if (repository == null) return null; - Optional currentVersion = repository.getRemoteVersionByLocalFile(file); + Optional currentVersion = repository.getRemoteVersionByLocalFile(file); if (currentVersion.isEmpty()) return null; - List remoteVersions = repository.getRemoteVersionsById(downloadProvider, currentVersion.get().getModid()) - .filter(version -> version.getGameVersions().contains(gameVersion)) - .filter(version -> version.getLoaders().contains(getModLoaderType())) - .filter(version -> version.getDatePublished().compareTo(currentVersion.get().getDatePublished()) > 0) - .sorted(Comparator.comparing(RemoteMod.Version::getDatePublished).reversed()) + List remoteVersions = repository.getRemoteVersionsById(downloadProvider, currentVersion.get().modid()) + .filter(version -> version.gameVersions().contains(gameVersion)) + .filter(version -> version.loaders().contains(getModLoaderType())) + .filter(version -> version.datePublished().compareTo(currentVersion.get().datePublished()) > 0) + .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; return new AddonUpdate(this, currentVersion.get(), remoteVersions.get(0), true); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModLoaderType.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModLoaderType.java similarity index 87% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModLoaderType.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModLoaderType.java index 9dcf7c00305..61ba46cea84 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModLoaderType.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModLoaderType.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.mod; public enum ModLoaderType { UNKNOWN, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index bf59e78cdee..8acc3151117 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,12 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.mod; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.addon.meta.*; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.GameRepository; -import org.jackhuang.hmcl.mod.modinfo.*; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MinecraftInstanceTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MinecraftInstanceTask.java index 88d8dc8d038..6c24c98ecea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MinecraftInstanceTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MinecraftInstanceTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.task.Task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MismatchedModpackTypeException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MismatchedModpackTypeException.java similarity index 90% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MismatchedModpackTypeException.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MismatchedModpackTypeException.java index 4f6dd2fa90b..3001e2e4509 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MismatchedModpackTypeException.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/MismatchedModpackTypeException.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; public class MismatchedModpackTypeException extends Exception { private final String required; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModAdviser.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModAdviser.java index 837bdbcb02d..678b7f25f7f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModAdviser.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModAdviser.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import org.jackhuang.hmcl.util.Lang; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/Modpack.java similarity index 96% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/Modpack.java index 07876ebf817..3731c5064bb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Modpack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/Modpack.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.task.Task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackCompletionException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackCompletionException.java similarity index 90% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackCompletionException.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackCompletionException.java index 238eca9172e..fcd782e8a1f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackCompletionException.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackCompletionException.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; public class ModpackCompletionException extends Exception { public ModpackCompletionException() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackConfiguration.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackConfiguration.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackConfiguration.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackConfiguration.java index 743f9304547..bc59fbc4637 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackConfiguration.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackConfiguration.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackExportInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackExportInfo.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackExportInfo.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackExportInfo.java index 49d77180609..144bab707b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackExportInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackExportInfo.java @@ -15,9 +15,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; -import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest; +import org.jackhuang.hmcl.addon.modpack.mcbbs.McbbsModpackManifest; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackInstallTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackInstallTask.java index 5e49194bceb..841092f227d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackManifest.java similarity index 86% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackManifest.java index 0dcd03dc13e..130555b2240 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; public interface ModpackManifest { ModpackProvider getProvider(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackProvider.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackProvider.java index d2440959046..536c883264d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackUpdateTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackUpdateTask.java similarity index 95% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackUpdateTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackUpdateTask.java index 72108e26bdc..f63b4f813e0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackUpdateTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/ModpackUpdateTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.task.Task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/UnsupportedModpackException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/UnsupportedModpackException.java similarity index 90% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/UnsupportedModpackException.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/UnsupportedModpackException.java index 61c378482e1..2c09a830e36 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/UnsupportedModpackException.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/UnsupportedModpackException.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod; +package org.jackhuang.hmcl.addon.modpack; public class UnsupportedModpackException extends Exception { public UnsupportedModpackException() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseCompletionTask.java similarity index 91% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseCompletionTask.java index 87a81716bd4..edba6d429bd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseCompletionTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModManager; -import org.jackhuang.hmcl.mod.ModpackCompletionException; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.addon.modpack.ModpackCompletionException; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.StringUtils; @@ -121,8 +122,8 @@ public void execute() throws Exception { updateProgress(finished.incrementAndGet(), manifest.files().size()); if (StringUtils.isBlank(file.fileName()) || file.url() == null) { try { - RemoteMod.File remoteFile = CurseForgeRemoteModRepository.MODS.getModFile(Integer.toString(file.projectID()), Integer.toString(file.fileID())); - return file.withFileName(remoteFile.getFilename()).withURL(remoteFile.getUrl()); + RemoteAddon.File remoteFile = CurseForgeRemoteAddonRepository.MODS.getModFile(Integer.toString(file.projectID()), Integer.toString(file.fileID())); + return file.withFileName(remoteFile.filename()).withURL(remoteFile.url()); } catch (FileNotFoundException fof) { LOG.warning("Could not query api.curseforge.com for deleted mods: " + file.projectID() + ", " + file.fileID(), fof); notFound.set(true); @@ -183,8 +184,8 @@ public void execute() throws Exception { * @throws IOException If IOException was encountered during getting data from CurseForge. */ private Path guessFilePath(CurseManifestFile file, DownloadProvider downloadProvider, Path resourcePacksRoot, Path shaderPacksRoot) throws IOException { - RemoteMod mod = CurseForgeRemoteModRepository.MODS.getModById(downloadProvider, Integer.toString(file.projectID())); - int classID = ((CurseAddon) mod.getData()).getClassId(); + RemoteAddon mod = CurseForgeRemoteAddonRepository.MODS.getModById(downloadProvider, Integer.toString(file.projectID())); + int classID = ((CurseForgeRemoteAddonRepository.CurseAddon) mod.getData()).classId(); String fileName = file.fileName(); return switch (classID) { case 12, // Resource pack diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseInstallTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseInstallTask.java index 60a2a1c5334..b7e01dd9713 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.modpack.*; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifest.java similarity index 89% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifest.java index 2fde2789a54..a766a8b1cd7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jetbrains.annotations.Unmodifiable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestFile.java similarity index 95% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestFile.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestFile.java index bc772760d62..d9db9e371db 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestFile.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestMinecraft.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestMinecraft.java similarity index 93% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestMinecraft.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestMinecraft.java index aed998a8c9a..37251ee61a6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestMinecraft.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestMinecraft.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestModLoader.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestModLoader.java similarity index 91% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestModLoader.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestModLoader.java index 319af366d1e..94c5109fd1b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseManifestModLoader.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseManifestModLoader.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseMetaMod.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseMetaMod.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseMetaMod.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseMetaMod.java index 40a9248df52..1b4d39c8135 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseMetaMod.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseMetaMod.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Immutable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseModpackProvider.java similarity index 89% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseModpackProvider.java index aae1b7ec83e..23ebb92d34f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/curse/CurseModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,16 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.modpack.curse; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackProvider; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackCompletionTask.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackCompletionTask.java index 3ca07c225d5..fe25e849f76 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackCompletionTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModManager; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackCompletionException; -import org.jackhuang.hmcl.mod.curse.CurseMetaMod; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackCompletionException; +import org.jackhuang.hmcl.addon.modpack.curse.CurseMetaMod; import org.jackhuang.hmcl.task.*; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackExportTask.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackExportTask.java index 2f8eccd2ae7..8fbc68029f0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackExportTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,17 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackExportInfo; -import org.jackhuang.hmcl.mod.curse.CurseManifest; -import org.jackhuang.hmcl.mod.curse.CurseManifestMinecraft; -import org.jackhuang.hmcl.mod.curse.CurseManifestModLoader; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.curse.CurseManifest; +import org.jackhuang.hmcl.addon.modpack.curse.CurseManifestMinecraft; +import org.jackhuang.hmcl.addon.modpack.curse.CurseManifestModLoader; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackLocalInstallTask.java similarity index 93% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackLocalInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 2c685b5a907..a68e9119b6a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,17 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.MinecraftInstanceTask; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackInstallTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackManifest.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackManifest.java index 403a30ff74c..d5504737f8b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,16 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.*; import org.jetbrains.annotations.Nullable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackProvider.java similarity index 95% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackProvider.java index ac04cf736db..a135ee30182 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; +import org.jackhuang.hmcl.addon.modpack.*; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.LaunchOptions; -import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackRemoteInstallTask.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackRemoteInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackRemoteInstallTask.java index b3a0e10e23e..a6190a14350 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/mcbbs/McbbsModpackRemoteInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.mcbbs; +package org.jackhuang.hmcl.addon.modpack.mcbbs; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthCompletionTask.java similarity index 95% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthCompletionTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthCompletionTask.java index 682690c3656..370e8bbccbb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthCompletionTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modrinth; +package org.jackhuang.hmcl.addon.modpack.modrinth; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModManager; -import org.jackhuang.hmcl.mod.ModpackCompletionException; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.addon.modpack.ModpackCompletionException; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthInstallTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthInstallTask.java index c977b101766..26990717017 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modrinth; +package org.jackhuang.hmcl.addon.modpack.modrinth; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.modpack.*; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthManifest.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthManifest.java index ee5ac48e6fa..3b70a001b3e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modrinth; +package org.jackhuang.hmcl.addon.modpack.modrinth; import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.util.gson.TolerableValidationException; import org.jackhuang.hmcl.util.gson.Validation; import org.jetbrains.annotations.Nullable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackExportTask.java similarity index 89% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackExportTask.java index b4f7d9fc29b..0d11dd65e38 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackExportTask.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modrinth; +package org.jackhuang.hmcl.addon.modpack.modrinth; import java.io.File; import java.io.IOException; @@ -24,18 +24,19 @@ import java.nio.file.Paths; import java.util.*; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.Zipper; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; +import org.jackhuang.hmcl.addon.mod.LocalModFile; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -74,18 +75,18 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t } LocalModFile localModFile = null; - Optional modrinthVersion = Optional.empty(); - Optional curseForgeVersion = Optional.empty(); + Optional modrinthVersion = Optional.empty(); + Optional curseForgeVersion = Optional.empty(); try { - modrinthVersion = ModrinthRemoteModRepository.MODS.getRemoteVersionByLocalFile(file); + modrinthVersion = ModrinthRemoteAddonRepository.MODS.getRemoteVersionByLocalFile(file); } catch (IOException e) { LOG.warning("Failed to get remote file from Modrinth for: " + file, e); } - if (!info.isSkipCurseForgeRemoteFiles() && CurseForgeRemoteModRepository.isAvailable()) { + if (!info.isSkipCurseForgeRemoteFiles() && CurseForgeRemoteAddonRepository.isAvailable()) { try { - curseForgeVersion = CurseForgeRemoteModRepository.MODS.getRemoteVersionByLocalFile(file); + curseForgeVersion = CurseForgeRemoteAddonRepository.MODS.getRemoteVersionByLocalFile(file); } catch (IOException e) { LOG.warning("Failed to get remote file from CurseForge for: " + file, e); } @@ -107,9 +108,9 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t List downloads = new ArrayList<>(); if (modrinthVersion.isPresent()) - downloads.add(modrinthVersion.get().getFile().getUrl()); + downloads.add(modrinthVersion.get().file().url()); if (curseForgeVersion.isPresent()) - downloads.add(curseForgeVersion.get().getFile().getUrl()); + downloads.add(curseForgeVersion.get().file().url()); long fileSize = Files.size(file); if (fileSize > Integer.MAX_VALUE) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackProvider.java similarity index 88% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackProvider.java index c8adc3da620..6417c29d8cf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/modrinth/ModrinthModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.modrinth; +package org.jackhuang.hmcl.addon.modpack.modrinth; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackProvider; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCComponents.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCComponents.java index 15758ff7cba..21f6251f4d6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCComponents.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCComponents.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.util.io.NetworkUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstanceConfiguration.java similarity index 98% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstanceConfiguration.java index 1dcc2ca35f4..855875a542b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstanceConfiguration.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstanceConfiguration.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.util.Lang; import java.io.IOException; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstancePatch.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstancePatch.java index 51483e4a38b..f6fc4e6f596 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCInstancePatch.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCManifest.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCManifest.java index a4ac2d1e433..36c63852a63 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import com.google.gson.annotations.SerializedName; import kala.compress.archivers.zip.ZipArchiveEntry; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackExportTask.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackExportTask.java index b83eda9dd1d..4cb6b63414f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackExportTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.Zipper; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackInstallTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackInstallTask.java index 9d3abd48f47..bbad41bf9ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; @@ -28,10 +28,10 @@ import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.MinecraftInstanceTask; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackInstallTask; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackProvider.java similarity index 91% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackProvider.java index 1bb7c8e939d..53f9ee80b39 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/multimc/MultiMCModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/multimc/MultiMCModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.multimc; +package org.jackhuang.hmcl.addon.modpack.multimc; import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackProvider; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackCompletionTask.java similarity index 97% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackCompletionTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackCompletionTask.java index 9cc0249f5fe..c64b0760400 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackCompletionTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.LocalAddonManager; -import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackExportTask.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackExportTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackExportTask.java index ce0e0f5d73d..e0f52b06481 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackExportTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModAdviser; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.addon.modpack.ModAdviser; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackExportInfo; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackLocalInstallTask.java similarity index 91% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackLocalInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackLocalInstallTask.java index 67a1f7dab5c..197cd1a5eb5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackLocalInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,16 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackInstallTask; +import org.jackhuang.hmcl.addon.modpack.MinecraftInstanceTask; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackInstallTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackManifest.java similarity index 92% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackManifest.java index b8075c09f08..6e5504d0a51 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackManifest.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackConfiguration; -import org.jackhuang.hmcl.mod.ModpackManifest; -import org.jackhuang.hmcl.mod.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackManifest; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.TolerableValidationException; import org.jackhuang.hmcl.util.gson.Validation; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackProvider.java similarity index 87% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackProvider.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackProvider.java index c29da8e5d26..078a573b9e3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackProvider.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2022 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,15 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.mod.MismatchedModpackTypeException; -import org.jackhuang.hmcl.mod.Modpack; -import org.jackhuang.hmcl.mod.ModpackProvider; -import org.jackhuang.hmcl.mod.ModpackUpdateTask; +import org.jackhuang.hmcl.addon.modpack.MismatchedModpackTypeException; +import org.jackhuang.hmcl.addon.modpack.Modpack; +import org.jackhuang.hmcl.addon.modpack.ModpackProvider; +import org.jackhuang.hmcl.addon.modpack.ModpackUpdateTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackRemoteInstallTask.java similarity index 94% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackRemoteInstallTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackRemoteInstallTask.java index f1dd1ee9bb8..093d2769548 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/server/ServerModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/modpack/server/ServerModpackRemoteInstallTask.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.server; +package org.jackhuang.hmcl.addon.modpack.server; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java new file mode 100644 index 00000000000..726792a22a2 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java @@ -0,0 +1,600 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ +package org.jackhuang.hmcl.addon.repository; + +import com.google.gson.reflect.TypeToken; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.MurmurHash2; +import org.jackhuang.hmcl.util.Pair; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.HttpRequest; +import org.jackhuang.hmcl.util.io.JarUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.Semaphore; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.jackhuang.hmcl.util.Lang.mapOf; +import static org.jackhuang.hmcl.util.Pair.pair; +import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +public final class CurseForgeRemoteAddonRepository implements RemoteAddonRepository { + + private static final String PREFIX = "https://api.curseforge.com"; + private static final String apiKey = System.getProperty("hmcl.curseforge.apikey", JarUtils.getAttribute("hmcl.curseforge.apikey", "")); + private static final Semaphore SEMAPHORE = new Semaphore(16); + + private static final int WORD_PERFECT_MATCH_WEIGHT = 5; + + private static R withApiKey(R request) { + if (request.getUrl().startsWith(PREFIX) && !apiKey.isEmpty()) { + request.header("X-API-KEY", apiKey); + } + return request; + } + + public static boolean isAvailable() { + return !apiKey.isEmpty(); + } + + private final Type type; + private final int section; + + public CurseForgeRemoteAddonRepository(Type type, int section) { + this.type = type; + this.section = section; + } + + @Override + public Type getType() { + return type; + } + + private int toModsSearchSortField(SortType sort) { + // https://docs.curseforge.com/#tocS_ModsSearchSortField + switch (sort) { + case DATE_CREATED: + return 1; + case POPULARITY: + return 2; + case LAST_UPDATED: + return 3; + case NAME: + return 4; + case AUTHOR: + return 5; + case TOTAL_DOWNLOADS: + return 6; + default: + return 8; + } + } + + private String toSortOrder(SortOrder sortOrder) { + // https://docs.curseforge.com/#tocS_SortOrder + switch (sortOrder) { + case ASC: + return "asc"; + case DESC: + return "desc"; + } + return "asc"; + } + + private int calculateTotalPages(Response> response, int pageSize) { + return (int) Math.ceil((double) Math.min(response.pagination.totalCount, 10000) / pageSize); + } + + @Override + public SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable RemoteAddonRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + int categoryId = 0; + if (category != null && category.self() instanceof Category) { + categoryId = ((Category) category.self()).getId(); + } + + var query = new LinkedHashMap(); + query.put("gameId", "432"); + query.put("classId", Integer.toString(section)); + if (categoryId != 0) + query.put("categoryId", Integer.toString(categoryId)); + query.put("gameVersion", gameVersion); + query.put("searchFilter", searchFilter); + query.put("sortField", Integer.toString(toModsSearchSortField(sortType))); + query.put("sortOrder", toSortOrder(sortOrder)); + query.put("index", Integer.toString(pageOffset * pageSize)); + query.put("pageSize", Integer.toString(pageSize)); + + Response> response = null; + + IOException exception = null; + List candidates = downloadProvider.injectURLWithCandidates(NetworkUtils.withQuery(PREFIX + "/v1/mods/search", query)); + for (URI candidate : candidates) { + LOG.info("Fetching " + candidate); + try { + response = withApiKey(HttpRequest.GET(candidate.toString())) + .getJson(Response.typeOf(listTypeOf(CurseAddon.class))); + if (searchFilter.isEmpty()) { + return new SearchResult(response.data().stream().map(addon -> addon.toMod(type)), calculateTotalPages(response, pageSize)); + } + break; + } catch (IOException e) { + LOG.warning("Failed to search addons: " + candidate, e); + if (candidates.size() == 1) { + exception = e; + } else { + if (exception == null) { + exception = new IOException("Failed to search addons"); + } + exception.addSuppressed(e); + } + } + } + + if (response == null) { + throw exception != null ? exception : new IOException("No candidates found"); + } + + // https://github.com/HMCL-dev/HMCL/issues/1549 + String lowerCaseSearchFilter = searchFilter.toLowerCase(Locale.ROOT); + Map searchFilterWords = new HashMap<>(); + for (String s : StringUtils.tokenize(lowerCaseSearchFilter)) { + searchFilterWords.put(s, searchFilterWords.getOrDefault(s, 0) + 1); + } + + StringUtils.LevCalculator levCalculator = new StringUtils.LevCalculator(); + + return new SearchResult(response.data().stream().map(addon -> addon.toMod(type)).map(remoteMod -> { + String lowerCaseResult = remoteMod.getTitle().toLowerCase(Locale.ROOT); + int diff = levCalculator.calc(lowerCaseSearchFilter, lowerCaseResult); + + for (String s : StringUtils.tokenize(lowerCaseResult)) { + if (searchFilterWords.containsKey(s)) { + diff -= WORD_PERFECT_MATCH_WEIGHT * searchFilterWords.get(s) * s.length(); + } + } + + return pair(remoteMod, diff); + }).sorted(Comparator.comparingInt(Pair::getValue)).map(Pair::getKey), response.data().stream().map(addon -> addon.toMod(type)), calculateTotalPages(response, pageSize)); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public Optional getRemoteVersionByLocalFile(Path file) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (InputStream stream = Files.newInputStream(file)) { + byte[] buf = new byte[1024]; + int len; + while ((len = stream.read(buf, 0, buf.length)) != -1) { + for (int i = 0; i < len; i++) { + byte b = buf[i]; + if (b != 0x9 && b != 0xa && b != 0xd && b != 0x20) { + baos.write(b); + } + } + } + } + + long hash = Integer.toUnsignedLong(MurmurHash2.hash32(baos.toByteArray(), baos.size(), 1)); + if (hash == 811513880) { // Workaround for https://github.com/HMCL-dev/HMCL/issues/4597 + return Optional.empty(); + } + + SEMAPHORE.acquireUninterruptibly(); + try { + Response response = withApiKey(HttpRequest.POST(PREFIX + "/v1/fingerprints/432")) + .json(mapOf(pair("fingerprints", Collections.singletonList(hash)))) + .getJson(Response.typeOf(FingerprintMatchesResult.class)); + + if (response.data().exactMatches() == null || response.data().exactMatches().isEmpty()) { + return Optional.empty(); + } + + return Optional.of(response.data().exactMatches().get(0).file().toVersion()); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public RemoteAddon getModById(DownloadProvider downloadProvider, String id) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + Response response = withApiKey(HttpRequest.GET(PREFIX + "/v1/mods/" + id)) + .getJson(Response.typeOf(CurseAddon.class)); + return response.data.toMod(type); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public RemoteAddon.File getModFile(String modId, String fileId) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + Response response = withApiKey(HttpRequest.GET(String.format("%s/v1/mods/%s/files/%s", PREFIX, modId, fileId))) + .getJson(Response.typeOf(CurseAddon.LatestFile.class)); + return response.data().toVersion().file(); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + Response> response = withApiKey(HttpRequest.GET(PREFIX + "/v1/mods/" + id + "/files", + pair("pageSize", "10000"))) + .getJson(Response.typeOf(listTypeOf(CurseAddon.LatestFile.class))); + return response.data().stream().map(CurseAddon.LatestFile::toVersion); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public Stream getCategories() throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + Response> categories = withApiKey(HttpRequest.GET(PREFIX + "/v1/categories", pair("gameId", "432"))) + .getJson(Response.typeOf(listTypeOf(Category.class))); + return reorganizeCategories(categories.data(), section).stream().map(Category::toCategory); + } finally { + SEMAPHORE.release(); + } + } + + private List reorganizeCategories(List categories, int rootId) { + List result = new ArrayList<>(); + + Map categoryMap = new HashMap<>(); + for (Category category : categories) { + categoryMap.put(category.getId(), category); + } + for (Category category : categories) { + if (category.getParentCategoryId() == rootId) { + result.add(category); + } else { + Category parentCategory = categoryMap.get(category.getParentCategoryId()); + if (parentCategory == null) { + // Category list is not correct, so we ignore this item. + continue; + } + parentCategory.getSubcategories().add(category); + } + } + return result; + } + + public static final int SECTION_BUKKIT_PLUGIN = 5; + public static final int SECTION_MOD = 6; + public static final int SECTION_RESOURCE_PACK = 12; + public static final int SECTION_WORLD = 17; + public static final int SECTION_MODPACK = 4471; + public static final int SECTION_SHADER = 6552; + public static final int SECTION_CUSTOMIZATION = 4546; + public static final int SECTION_ADDONS = 4559; // For Pocket Edition + public static final int SECTION_UNKNOWN1 = 4944; + public static final int SECTION_UNKNOWN2 = 4979; + public static final int SECTION_UNKNOWN3 = 4984; + + public static final CurseForgeRemoteAddonRepository MODS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.MOD, SECTION_MOD); + public static final CurseForgeRemoteAddonRepository MODPACKS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.MODPACK, SECTION_MODPACK); + public static final CurseForgeRemoteAddonRepository RESOURCE_PACKS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.RESOURCE_PACK, SECTION_RESOURCE_PACK); + public static final CurseForgeRemoteAddonRepository WORLDS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.WORLD, SECTION_WORLD); + public static final CurseForgeRemoteAddonRepository CUSTOMIZATIONS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.CUSTOMIZATION, SECTION_CUSTOMIZATION); + public static final CurseForgeRemoteAddonRepository SHADERS = new CurseForgeRemoteAddonRepository(RemoteAddonRepository.Type.SHADER_PACK, SECTION_SHADER); + + public record Pagination(int index, int pageSize, int resultCount, int totalCount) { + } + + public record Response(T data, Pagination pagination) { + + @SuppressWarnings("unchecked") + public static TypeToken> typeOf(Class responseType) { + return (TypeToken>) TypeToken.getParameterized(Response.class, responseType); + } + + @SuppressWarnings("unchecked") + public static TypeToken> typeOf(TypeToken responseType) { + return (TypeToken>) TypeToken.getParameterized(Response.class, responseType.getType()); + } + + } + + /** + * @see Schema + */ + private record FingerprintMatchesResult(boolean isCacheBuilt, List exactMatches, + List exactFingerprints) { + } + + /** + * @see Schema + */ + private record FingerprintMatch(int id, CurseAddon.LatestFile file, List latestFiles) { + } + + @Immutable + public record CurseAddon(int id, int gameId, String name, String slug, Links links, String summary, + int status, + int downloadCount, boolean isFeatured, int primaryCategoryId, + List categories, + int classId, List authors, Logo logo, int mainFileId, + List latestFiles, + List latestFileIndices, Instant dateCreated, Instant dateModified, + Instant dateReleased, boolean allowModDistribution, int gamePopularityRank, + boolean isAvailable, int thumbsUpCount) implements RemoteAddon.IMod { + public static final Map RELATION_TYPE = mapOf( + pair(1, RemoteAddon.DependencyType.EMBEDDED), + pair(2, RemoteAddon.DependencyType.OPTIONAL), + pair(3, RemoteAddon.DependencyType.REQUIRED), + pair(4, RemoteAddon.DependencyType.TOOL), + pair(5, RemoteAddon.DependencyType.INCOMPATIBLE), + pair(6, RemoteAddon.DependencyType.INCLUDE) + ); + + @Override + public List loadDependencies(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + Set dependencies = latestFiles.stream() + .flatMap(latestFile -> latestFile.dependencies().stream()) + .filter(dep -> dep.relationType() == 3) + .map(Dependency::modId) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (int dependencyId : dependencies) { + mods.add(modRepository.getModById(downloadProvider, Integer.toString(dependencyId))); + } + return mods; + } + + @Override + public Stream loadVersions(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + return modRepository.getRemoteVersionsById(downloadProvider, Integer.toString(id)); + } + + public RemoteAddon toMod(Type type) { + String iconUrl = ""; + if (logo != null) { + if (StringUtils.isNotBlank(logo.thumbnailUrl())) + iconUrl = logo.thumbnailUrl(); + else if (StringUtils.isNotBlank(logo.url())) + iconUrl = logo.url(); + } + + return new RemoteAddon( + slug, + "", + name, + summary, + categories.stream().map(category -> Integer.toString(category.getId())).collect(Collectors.toList()), + links.websiteUrl, + iconUrl, + this, + type + ); + } + + @Immutable + public record Links(String websiteUrl, String wikiUrl, @Nullable String issuesUrl, @Nullable String sourceUrl) { + } + + @Immutable + public record Author(int id, String name, String url) { + } + + @Immutable + public record Logo(int id, int modId, String title, String description, String thumbnailUrl, String url) { + } + + @Immutable + public record Attachment(int id, int projectId, String description, boolean isDefault, String thumbnailUrl, + String title, String url, int status) { + } + + @Immutable + public record Dependency(int modId, int relationType) { + public Dependency() { + this(0, 1); + } + } + + /** + * @see Schema + */ + @Immutable + public record LatestFileHash(String value, int algo) { + } + + /** + * @see Schema + */ + @Immutable + public record LatestFile(int id, int gameId, int modId, boolean isAvailable, String displayName, + String fileName, + int releaseType, int fileStatus, List hashes, Instant fileDate, + int fileLength, int downloadCount, String downloadUrl, List gameVersions, + List dependencies, int alternateFileId, boolean isServerPack, + long fileFingerprint) implements RemoteAddon.IVersion { + + @Override + public String downloadUrl() { + if (downloadUrl == null) { + // This addon is not allowed for distribution, and downloadUrl will be null. + // We try to find its download url. + return String.format("https://edge.forgecdn.net/files/%d/%d/%s", id / 1000, id % 1000, fileName); + } + return downloadUrl; + } + + @Override + public RemoteAddon.Source getType() { + return RemoteAddon.Source.CURSEFORGE; + } + + public RemoteAddon.Version toVersion() { + RemoteAddon.VersionType versionType = switch (releaseType()) { + case 1 -> RemoteAddon.VersionType.Release; + case 2 -> RemoteAddon.VersionType.Beta; + case 3 -> RemoteAddon.VersionType.Alpha; + default -> RemoteAddon.VersionType.Release; + }; + + return new RemoteAddon.Version( + this, + Integer.toString(modId), + displayName(), + fileName(), + null, + fileDate(), + versionType, + new RemoteAddon.File(Collections.emptyMap(), downloadUrl(), fileName()), + dependencies.stream().map(dependency -> { + if (!RELATION_TYPE.containsKey(dependency.relationType())) { + throw new IllegalStateException("Broken datas."); + } + return RemoteAddon.Dependency.ofGeneral(RELATION_TYPE.get(dependency.relationType()), MODS, Integer.toString(dependency.modId())); + }).distinct().filter(Objects::nonNull).collect(Collectors.toList()), + gameVersions.stream().filter(GameVersionNumber::isKnown).toList(), + gameVersions.stream().flatMap(version -> { + if ("fabric".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FABRIC); + else if ("forge".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FORGE); + else if ("quilt".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.QUILT); + else if ("neoforge".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.NEO_FORGE); + else return Stream.empty(); + }).collect(Collectors.toList()) + ); + } + } + + /** + * @see Schema + */ + @Immutable + public record LatestFileIndex(String gameVersion, int fileId, String filename, int releaseType, + int gameVersionTypeId, int modLoader) { + } + + } + + @Immutable + public static class Category { + private final int id; + private final int gameId; + private final String name; + private final String slug; + private final String url; + private final String iconUrl; + private final Instant dateModified; + private final boolean isClass; + private final int classId; + private final int parentCategoryId; + + private transient final List subcategories; + + public Category() { + this(0, 0, "", "", "", "", Instant.now(), false, 0, 0); + } + + public Category(int id, int gameId, String name, String slug, String url, String iconUrl, Instant dateModified, boolean isClass, int classId, int parentCategoryId) { + this.id = id; + this.gameId = gameId; + this.name = name; + this.slug = slug; + this.url = url; + this.iconUrl = iconUrl; + this.dateModified = dateModified; + this.isClass = isClass; + this.classId = classId; + this.parentCategoryId = parentCategoryId; + + this.subcategories = new ArrayList<>(); + } + + public int getId() { + return id; + } + + public int getGameId() { + return gameId; + } + + public String getName() { + return name; + } + + public String getSlug() { + return slug; + } + + public String getUrl() { + return url; + } + + public String getIconUrl() { + return iconUrl; + } + + public Instant getDateModified() { + return dateModified; + } + + public boolean isClass() { + return isClass; + } + + public int getClassId() { + return classId; + } + + public int getParentCategoryId() { + return parentCategoryId; + } + + public List getSubcategories() { + return subcategories; + } + + public RemoteAddonRepository.Category toCategory() { + return new RemoteAddonRepository.Category( + this, + Integer.toString(id), + getSubcategories().stream().map(Category::toCategory).collect(Collectors.toList())); + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java new file mode 100644 index 00000000000..ba48fdf1aa7 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java @@ -0,0 +1,537 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui 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 . + */ +package org.jackhuang.hmcl.addon.repository; + +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; +import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.HttpRequest; +import org.jackhuang.hmcl.util.io.NetworkUtils; +import org.jackhuang.hmcl.util.io.ResponseCodeException; +import org.jetbrains.annotations.Nullable; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.Semaphore; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.jackhuang.hmcl.util.Lang.mapOf; +import static org.jackhuang.hmcl.util.Pair.pair; +import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +public final class ModrinthRemoteAddonRepository implements RemoteAddonRepository { + public static final ModrinthRemoteAddonRepository MODS = new ModrinthRemoteAddonRepository("mod"); + public static final ModrinthRemoteAddonRepository MODPACKS = new ModrinthRemoteAddonRepository("modpack"); + public static final ModrinthRemoteAddonRepository RESOURCE_PACKS = new ModrinthRemoteAddonRepository("resourcepack"); + public static final ModrinthRemoteAddonRepository SHADER_PACKS = new ModrinthRemoteAddonRepository("shader"); + + private static final Comparator TAG_COMPARATOR = PriorityComparator.of( + List.of("babric", + "bta-babric", + "bukkit", + "bungeecord", + "canvas", + "datapack", + "fabric", + "folia", + "forge", + "geyser", + "iris", + "java-agent", + "legacy-fabric", + "liteloader", + "minecraft", + "modloader", + "mrpack", + "neoforge", + "nilloader", + "optifine", + "ornith", + "paper", + "purpur", + "quilt", + "rift", + "spigot", + "sponge", + "vanilla", + "velocity", + "waterfall"), + Comparator.naturalOrder(), + false + ); + + private static final Semaphore SEMAPHORE = new Semaphore(16); + + private static final String PREFIX = "https://api.modrinth.com"; + + private final String projectType; + + private final RemoteAddonRepository.Type type; + + private ModrinthRemoteAddonRepository(String projectType) { + this.projectType = projectType; + this.type = switch (projectType) { + case "modpack" -> Type.MODPACK; + case "resourcepack" -> Type.RESOURCE_PACK; + case "shader" -> Type.SHADER_PACK; + default -> Type.MOD; + }; + } + + @Override + public Type getType() { + return this.type; + } + + private static String convertSortType(SortType sortType) { + switch (sortType) { + case DATE_CREATED: + return "newest"; + case POPULARITY: + case NAME: + case AUTHOR: + return "relevance"; + case LAST_UPDATED: + return "updated"; + case TOTAL_DOWNLOADS: + return "downloads"; + default: + throw new IllegalArgumentException("Unsupported sort type " + sortType); + } + } + + static List sortDisplayCategories(List displayCategories) { + return displayCategories != null && !displayCategories.isEmpty() + ? displayCategories.stream().sorted(TAG_COMPARATOR).toList() + : List.of(); + } + + @Override + public SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable RemoteAddonRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sort, SortOrder sortOrder) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + List> facets = new ArrayList<>(); + facets.add(Collections.singletonList("project_type:" + projectType)); + if (StringUtils.isNotBlank(gameVersion)) { + facets.add(Collections.singletonList("versions:" + gameVersion)); + } + if (category != null && StringUtils.isNotBlank(category.id())) { + facets.add(Collections.singletonList("categories:" + category.id())); + } + Map query = mapOf( + pair("query", searchFilter), + pair("facets", JsonUtils.UGLY_GSON.toJson(facets)), + pair("offset", Integer.toString(pageOffset * pageSize)), + pair("limit", Integer.toString(pageSize)), + pair("index", convertSortType(sort)) + ); + + List candidates = downloadProvider.injectURLWithCandidates(NetworkUtils.withQuery(PREFIX + "/v2/search", query)); + IOException exception = null; + for (URI candidate : candidates) { + try { + LOG.info("Fetching " + candidate); + Response response = HttpRequest.GET(candidate.toString()) + .getJson(Response.typeOf(ProjectSearchResult.class)); + return new SearchResult(response.getHits().stream().map(ProjectSearchResult::toMod), (int) Math.ceil((double) response.totalHits / pageSize)); + } catch (IOException e) { + LOG.warning("Failed to search addons: " + candidate, e); + + IOException wrapper = new IOException("Failed to search addons: " + candidate, e); + if (candidates.size() == 1) { + exception = wrapper; + } else { + if (exception == null) { + exception = new IOException("Failed to search addons"); + } + exception.addSuppressed(wrapper); + } + } + } + + throw exception != null ? exception : new IOException("No candidates found"); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public Optional getRemoteVersionByLocalFile(Path file) throws IOException { + String sha1 = DigestUtils.digestToString("SHA-1", file); + + SEMAPHORE.acquireUninterruptibly(); + try { + ProjectVersion mod = HttpRequest.GET(PREFIX + "/v2/version_file/" + sha1, + pair("algorithm", "sha1")) + .getJson(ProjectVersion.class); + return mod.toVersion(); + } catch (ResponseCodeException e) { + if (e.getResponseCode() == 404) { + return Optional.empty(); + } else { + throw e; + } + } catch (NoSuchFileException e) { + return Optional.empty(); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public RemoteAddon getModById(DownloadProvider downloadProvider, String id) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + id = StringUtils.removePrefix(id, "local-"); + List candidates = downloadProvider.injectURLWithCandidates(PREFIX + "/v2/project/" + id); + IOException exception = null; + + for (URI candidate : candidates) { + try { + Project project = HttpRequest.GET(candidate.toString()).getJson(Project.class); + return project.toMod(); + } catch (IOException e) { + IOException wrapper = new IOException("Failed to get mod: " + candidate, e); + if (candidates.size() == 1) { + exception = wrapper; + } else { + if (exception == null) { + exception = new IOException("Failed to get mod"); + } + exception.addSuppressed(wrapper); + } + } + } + + throw exception != null ? exception : new IOException("No candidates found"); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public RemoteAddon resolveDependency(DownloadProvider downloadProvider, String id) throws IOException { + try { + return getModById(downloadProvider, id); + } catch (ResponseCodeException e) { + if (e.getResponseCode() == 502 || e.getResponseCode() == 404) { + return RemoteAddon.BROKEN; + } + throw e; + } catch (FileNotFoundException e) { + return RemoteAddon.BROKEN; + } + } + + @Override + public RemoteAddon.File getModFile(String modId, String fileId) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + id = StringUtils.removePrefix(id, "local-"); + + List candidates = downloadProvider.injectURLWithCandidates(PREFIX + "/v2/project/" + id + "/version?include_changelog=false"); + IOException exception = null; + + for (URI candidate : candidates) { + try { + List versions = HttpRequest.GET(candidate.toString()) + .getJson(listTypeOf(ProjectVersion.class)); + return versions.stream().map(ProjectVersion::toVersion).flatMap(Lang::toStream); + } catch (IOException e) { + IOException wrapper = new IOException("Failed to get remote versions: " + candidate, e); + if (candidates.size() == 1) { + exception = wrapper; + } else { + if (exception == null) { + exception = new IOException("Failed to get remote versions"); + } + exception.addSuppressed(wrapper); + } + } + } + + throw exception != null ? exception : new IOException("No candidates found"); + } finally { + SEMAPHORE.release(); + } + } + + @Override + public Stream getCategories() throws IOException { + SEMAPHORE.acquireUninterruptibly(); + try { + List categories = HttpRequest.GET(PREFIX + "/v2/tag/category").getJson(listTypeOf(Category.class)); + return categories.stream() + .filter(category -> category.projectType().equals(projectType)) + .map(Category::toCategory); + } finally { + SEMAPHORE.release(); + } + } + + public record Category(String icon, String name, @SerializedName("project_type") String projectType) { + public Category() { + this("", "", ""); + } + + public RemoteAddonRepository.Category toCategory() { + return new RemoteAddonRepository.Category( + this, + name, + Collections.emptyList()); + } + } + + /** + * @param body A long body describing project in detail. + */ + public record Project(String slug, String title, String description, List categories, String body, + @SerializedName("project_type") String projectType, int downloads, + @SerializedName("icon_url") String iconUrl, String id, String team, Instant published, + Instant updated, List versions) implements RemoteAddon.IMod { + + @Override + public List loadDependencies(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + Set dependencies = modRepository.getRemoteVersionsById(downloadProvider, id()) + .flatMap(version -> version.dependencies().stream()) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (RemoteAddon.Dependency dependency : dependencies) { + mods.add(dependency.load(downloadProvider)); + } + return mods; + } + + @Override + public Stream loadVersions(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + return modRepository.getRemoteVersionsById(downloadProvider, id()); + } + + public RemoteAddon toMod() { + Type type = switch (projectType) { + case "modpack" -> Type.MODPACK; + case "resourcepack" -> Type.RESOURCE_PACK; + case "shader" -> Type.SHADER_PACK; + default -> Type.MOD; + }; + return new RemoteAddon( + slug, + "", + title, + description, + categories, + String.format("https://modrinth.com/%s/%s", projectType, id), + iconUrl, + this, + type + ); + } + } + + @Immutable + public record Dependency(@SerializedName("version_id") String versionId, + @SerializedName("project_id") String projectId, + @SerializedName("dependency_type") String dependencyType) { + } + + public record ProjectVersion(String name, @SerializedName("version_number") String versionNumber, String changelog, + List dependencies, + @SerializedName("game_versions") List gameVersions, + @SerializedName("version_type") String versionType, List loaders, + boolean featured, String id, @SerializedName("project_id") String projectId, + @SerializedName("author_id") String authorId, + @SerializedName("date_published") Instant datePublished, int downloads, + @SerializedName("changelog_url") String changelogUrl, + List files) implements RemoteAddon.IVersion { + private static final Map DEPENDENCY_TYPE = mapOf( + pair("required", RemoteAddon.DependencyType.REQUIRED), + pair("optional", RemoteAddon.DependencyType.OPTIONAL), + pair("embedded", RemoteAddon.DependencyType.EMBEDDED), + pair("incompatible", RemoteAddon.DependencyType.INCOMPATIBLE) + ); + + @Override + public RemoteAddon.Source getType() { + return RemoteAddon.Source.MODRINTH; + } + + public Optional toVersion() { + RemoteAddon.VersionType type; + if ("release".equals(versionType)) { + type = RemoteAddon.VersionType.Release; + } else if ("beta".equals(versionType)) { + type = RemoteAddon.VersionType.Beta; + } else if ("alpha".equals(versionType)) { + type = RemoteAddon.VersionType.Alpha; + } else { + type = RemoteAddon.VersionType.Release; + } + + if (files.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(new RemoteAddon.Version( + this, + projectId, + name, + versionNumber, + changelog, + datePublished, + type, + files.get(0).toFile(), + dependencies.stream().map(dependency -> { + if (dependency.projectId == null) { + return RemoteAddon.Dependency.ofBroken(); + } + + if (!DEPENDENCY_TYPE.containsKey(dependency.dependencyType)) { + throw new IllegalStateException("Broken datas"); + } + + return RemoteAddon.Dependency.ofGeneral(DEPENDENCY_TYPE.get(dependency.dependencyType), MODS, dependency.projectId); + }).filter(Objects::nonNull).collect(Collectors.toList()), + gameVersions, + loaders.stream().flatMap(loader -> { + if ("fabric".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FABRIC); + else if ("forge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FORGE); + else if ("neoforge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.NEO_FORGE); + else if ("quilt".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.QUILT); + else if ("liteloader".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.LITE_LOADER); + else return Stream.empty(); + }).collect(Collectors.toList()) + )); + } + } + + public record ProjectVersionFile(Map hashes, String url, String filename, boolean primary, + int size) { + + public RemoteAddon.File toFile() { + return new RemoteAddon.File(hashes, url, filename); + } + } + + public record ProjectSearchResult(String slug, String title, String description, List categories, + @SerializedName("display_categories") List displayCategories, + @SerializedName("project_type") String projectType, int downloads, + @SerializedName("icon_url") String iconUrl, + @SerializedName("project_id") String projectId, String author, + List versions, @SerializedName("date_created") Instant dateCreated, + @SerializedName("date_modified") Instant dateModified, + @SerializedName("latest_version") String latestVersion) implements RemoteAddon.IMod { + + @Override + public List loadDependencies(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + Set dependencies = modRepository.getRemoteVersionsById(downloadProvider, projectId()) + .flatMap(version -> version.dependencies().stream()) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (RemoteAddon.Dependency dependency : dependencies) { + mods.add(dependency.load(downloadProvider)); + } + return mods; + } + + @Override + public Stream loadVersions(RemoteAddonRepository modRepository, DownloadProvider downloadProvider) throws IOException { + return modRepository.getRemoteVersionsById(downloadProvider, projectId()); + } + + public RemoteAddon toMod() { + Type type = switch (projectType) { + case "modpack" -> Type.MODPACK; + case "resourcepack" -> Type.RESOURCE_PACK; + case "shader" -> Type.SHADER_PACK; + default -> Type.MOD; + }; + return new RemoteAddon( + slug, + author, + title, + description, + sortDisplayCategories(displayCategories), + String.format("https://modrinth.com/%s/%s", projectType, projectId), + iconUrl, + this, + type + ); + } + } + + public static class Response { + + @SuppressWarnings("unchecked") + public static TypeToken> typeOf(Class responseType) { + return (TypeToken>) TypeToken.getParameterized(Response.class, responseType); + } + + private final int offset; + + private final int limit; + + @SerializedName("total_hits") + private final int totalHits; + + private final List hits; + + public Response() { + this(0, 0, Collections.emptyList()); + } + + public Response(int offset, int limit, List hits) { + this.offset = offset; + this.limit = limit; + this.totalHits = hits.size(); + this.hits = hits; + } + + public int getOffset() { + return offset; + } + + public int getLimit() { + return limit; + } + + public int getTotalHits() { + return totalHits; + } + + public List getHits() { + return hits; + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFile.java similarity index 95% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFile.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFile.java index fb70dd8f6f8..ffa35e4c227 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFile.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.resourcepack; +package org.jackhuang.hmcl.addon.resourcepack; import javafx.scene.image.Image; -import org.jackhuang.hmcl.mod.LocalAddonFile; -import org.jackhuang.hmcl.mod.modinfo.PackMcMeta; +import org.jackhuang.hmcl.addon.LocalAddonFile; +import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.Contract; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFolder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFolder.java similarity index 90% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFolder.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFolder.java index 7deeaeec2e1..7267d04923d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFolder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackFolder.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.resourcepack; +package org.jackhuang.hmcl.addon.resourcepack; import javafx.scene.image.Image; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.modinfo.PackMcMeta; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.Nullable; @@ -79,7 +79,7 @@ public void delete() throws IOException { } @Override - public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteMod.Type type) { + public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteAddon.Source source) { return null; } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index 7f1c9adad8c..d571f6422cf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.resourcepack; +package org.jackhuang.hmcl.addon.resourcepack; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.game.GameRepository; -import org.jackhuang.hmcl.mod.LocalAddonManager; -import org.jackhuang.hmcl.mod.modinfo.PackMcMeta; +import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonSerializable; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackZipFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java similarity index 76% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackZipFile.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java index 838702bf235..7b733539f41 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackZipFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors + * Copyright (C) 2026 huangyuhui 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 @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.resourcepack; +package org.jackhuang.hmcl.addon.resourcepack; import javafx.scene.image.Image; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.mod.modinfo.PackMcMeta; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.RemoteAddonRepository; +import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jetbrains.annotations.Nullable; @@ -90,15 +90,15 @@ public void delete() throws IOException { } @Override - public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteMod.Type type) throws IOException { - RemoteModRepository repository = type.getRepoForType(RemoteModRepository.Type.RESOURCE_PACK); + public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVersion, RemoteAddon.Source source) throws IOException { + RemoteAddonRepository repository = source.getRepoForType(RemoteAddonRepository.Type.RESOURCE_PACK); if (repository == null) return null; - Optional currentVersion = repository.getRemoteVersionByLocalFile(file); + Optional currentVersion = repository.getRemoteVersionByLocalFile(file); if (currentVersion.isEmpty()) return null; - List remoteVersions = repository.getRemoteVersionsById(downloadProvider, currentVersion.get().getModid()) - .filter(version -> version.getGameVersions().contains(gameVersion)) - .filter(version -> version.getDatePublished().compareTo(currentVersion.get().getDatePublished()) > 0) - .sorted(Comparator.comparing(RemoteMod.Version::getDatePublished).reversed()) + List remoteVersions = repository.getRemoteVersionsById(downloadProvider, currentVersion.get().modid()) + .filter(version -> version.gameVersions().contains(gameVersion)) + .filter(version -> version.datePublished().compareTo(currentVersion.get().datePublished()) > 0) + .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; return new AddonUpdate(this, currentVersion.get(), remoteVersions.get(0), false); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java index 16c6463108a..97a1cbb01da 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -19,7 +19,7 @@ import org.intellij.lang.annotations.Language; import org.jackhuang.hmcl.game.*; -import org.jackhuang.hmcl.mod.ModLoaderType; +import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jackhuang.hmcl.util.versioning.VersionRange; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java index e74c8bf6219..d8e09955711 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java @@ -58,9 +58,9 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws IOException { dependencies.add(new FileDownloadTask( - remote.getVersion().getFile().getUrl(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar"), - remote.getVersion().getFile().getIntegrityCheck()) + remote.getVersion().file().url(), + dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("fabric-api-" + remote.getVersion().version() + ".jar"), + remote.getVersion().file().getIntegrityCheck()) ); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java index e1522bc93d6..3cbe9569053 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; import java.time.Instant; @@ -29,7 +29,7 @@ public class FabricAPIRemoteVersion extends RemoteVersion { private final String fullVersion; - private final RemoteMod.Version version; + private final RemoteAddon.Version version; /** * Constructor. @@ -38,7 +38,7 @@ public class FabricAPIRemoteVersion extends RemoteVersion { * @param selfVersion the version string of the remote version. * @param urls the installer or universal jar original URL. */ - FabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteMod.Version version, List urls) { + FabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteAddon.Version version, List urls) { super(LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId(), gameVersion, selfVersion, datePublished, urls); this.fullVersion = fullVersion; @@ -50,7 +50,7 @@ public String getFullVersion() { return fullVersion; } - public RemoteMod.Version getVersion() { + public RemoteAddon.Version getVersion() { return version; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIVersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIVersionList.java index 0fde60902bd..623240a2001 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIVersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIVersionList.java @@ -19,8 +19,8 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.VersionList; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; @@ -42,10 +42,10 @@ public boolean hasType() { @Override public Task refreshAsync() { return Task.runAsync(() -> { - for (RemoteMod.Version modVersion : Lang.toIterable(ModrinthRemoteModRepository.MODS.getRemoteVersionsById(downloadProvider, "P7dR8mSH"))) { - for (String gameVersion : modVersion.getGameVersions()) { - versions.put(gameVersion, new FabricAPIRemoteVersion(gameVersion, modVersion.getVersion(), modVersion.getName(), modVersion.getDatePublished(), modVersion, - Collections.singletonList(modVersion.getFile().getUrl()))); + for (RemoteAddon.Version modVersion : Lang.toIterable(ModrinthRemoteAddonRepository.MODS.getRemoteVersionsById(downloadProvider, "P7dR8mSH"))) { + for (String gameVersion : modVersion.gameVersions()) { + versions.put(gameVersion, new FabricAPIRemoteVersion(gameVersion, modVersion.version(), modVersion.name(), modVersion.datePublished(), modVersion, + Collections.singletonList(modVersion.file().url()))); } } }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java index 1a9c2de6a05..1a19df5953f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java @@ -53,9 +53,9 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws IOException { dependencies.add(new FileDownloadTask( - remote.getVersion().getFile().getUrl(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("legacy-fabric-api-" + remote.getVersion().getVersion() + ".jar"), - remote.getVersion().getFile().getIntegrityCheck()) + remote.getVersion().file().url(), + dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("legacy-fabric-api-" + remote.getVersion().version() + ".jar"), + remote.getVersion().file().getIntegrityCheck()) ); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java index eb21c07aabd..1ae30d48856 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; import java.time.Instant; @@ -29,7 +29,7 @@ public class LegacyFabricAPIRemoteVersion extends RemoteVersion { private final String fullVersion; - private final RemoteMod.Version version; + private final RemoteAddon.Version version; /** * Constructor. @@ -38,7 +38,7 @@ public class LegacyFabricAPIRemoteVersion extends RemoteVersion { * @param selfVersion the version string of the remote version. * @param urls the installer or universal jar original URL. */ - LegacyFabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteMod.Version version, List urls) { + LegacyFabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteAddon.Version version, List urls) { super(LibraryAnalyzer.LibraryType.LEGACY_FABRIC_API.getPatchId(), gameVersion, selfVersion, datePublished, urls); this.fullVersion = fullVersion; @@ -50,7 +50,7 @@ public String getFullVersion() { return fullVersion; } - public RemoteMod.Version getVersion() { + public RemoteAddon.Version getVersion() { return version; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIVersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIVersionList.java index d7b6588e435..9c5a9408170 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIVersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIVersionList.java @@ -19,8 +19,8 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.VersionList; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; @@ -42,10 +42,10 @@ public boolean hasType() { @Override public Task refreshAsync() { return Task.runAsync(() -> { - for (RemoteMod.Version modVersion : Lang.toIterable(ModrinthRemoteModRepository.MODS.getRemoteVersionsById(downloadProvider, "legacy-fabric-api"))) { - for (String gameVersion : modVersion.getGameVersions()) { - versions.put(gameVersion, new LegacyFabricAPIRemoteVersion(gameVersion, modVersion.getVersion(), modVersion.getName(), modVersion.getDatePublished(), modVersion, - Collections.singletonList(modVersion.getFile().getUrl()))); + for (RemoteAddon.Version modVersion : Lang.toIterable(ModrinthRemoteAddonRepository.MODS.getRemoteVersionsById(downloadProvider, "legacy-fabric-api"))) { + for (String gameVersion : modVersion.gameVersions()) { + versions.put(gameVersion, new LegacyFabricAPIRemoteVersion(gameVersion, modVersion.version(), modVersion.name(), modVersion.datePublished(), modVersion, + Collections.singletonList(modVersion.file().url()))); } } }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java index 5f06cccba5b..245280e58be 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java @@ -58,9 +58,9 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws IOException { dependencies.add(new FileDownloadTask( - remote.getVersion().getFile().getUrl(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar"), - remote.getVersion().getFile().getIntegrityCheck()) + remote.getVersion().file().url(), + dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("quilt-api-" + remote.getVersion().version() + ".jar"), + remote.getVersion().file().getIntegrityCheck()) ); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java index c67572f39eb..bf229454a26 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; import java.time.Instant; @@ -29,7 +29,7 @@ public class QuiltAPIRemoteVersion extends RemoteVersion { private final String fullVersion; - private final RemoteMod.Version version; + private final RemoteAddon.Version version; /** * Constructor. @@ -38,7 +38,7 @@ public class QuiltAPIRemoteVersion extends RemoteVersion { * @param selfVersion the version string of the remote version. * @param urls the installer or universal jar original URL. */ - QuiltAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteMod.Version version, List urls) { + QuiltAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteAddon.Version version, List urls) { super(LibraryAnalyzer.LibraryType.QUILT_API.getPatchId(), gameVersion, selfVersion, datePublished, urls); this.fullVersion = fullVersion; @@ -50,7 +50,7 @@ public String getFullVersion() { return fullVersion; } - public RemoteMod.Version getVersion() { + public RemoteAddon.Version getVersion() { return version; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIVersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIVersionList.java index d0b23fa8cf1..28a2301e734 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIVersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIVersionList.java @@ -19,8 +19,8 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.VersionList; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; +import org.jackhuang.hmcl.addon.RemoteAddon; +import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; @@ -42,10 +42,10 @@ public boolean hasType() { @Override public Task refreshAsync() { return Task.runAsync(() -> { - for (RemoteMod.Version modVersion : Lang.toIterable(ModrinthRemoteModRepository.MODS.getRemoteVersionsById(downloadProvider, "qsl"))) { - for (String gameVersion : modVersion.getGameVersions()) { - versions.put(gameVersion, new QuiltAPIRemoteVersion(gameVersion, modVersion.getVersion(), modVersion.getName(), modVersion.getDatePublished(), modVersion, - Collections.singletonList(modVersion.getFile().getUrl()))); + for (RemoteAddon.Version modVersion : Lang.toIterable(ModrinthRemoteAddonRepository.MODS.getRemoteVersionsById(downloadProvider, "qsl"))) { + for (String gameVersion : modVersion.gameVersions()) { + versions.put(gameVersion, new QuiltAPIRemoteVersion(gameVersion, modVersion.version(), modVersion.name(), modVersion.datePublished(), modVersion, + Collections.singletonList(modVersion.file().url()))); } } }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 3f2407a5ff0..c5dbd71a207 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -22,8 +22,8 @@ import org.jackhuang.hmcl.download.game.VersionJsonSaveTask; import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.game.tlauncher.TLauncherVersion; -import org.jackhuang.hmcl.mod.ModManager; -import org.jackhuang.hmcl.mod.ModpackConfiguration; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.addon.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.ToStringBuilder; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java deleted file mode 100644 index 45e65243d18..00000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/RemoteMod.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui 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 . - */ -package org.jackhuang.hmcl.mod; - -import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; -import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository; -import org.jackhuang.hmcl.task.FileDownloadTask; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.time.Instant; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -public final class RemoteMod { - - public static final RemoteMod BROKEN = new RemoteMod("", "", "RemoteMod.BROKEN", "", Collections.emptyList(), "", "", new RemoteMod.IMod() { - @Override - public List loadDependencies(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - throw new IOException(); - } - - @Override - public Stream loadVersions(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - throw new IOException(); - } - }, RemoteModRepository.Type.MOD); - - private final String slug; - private final String author; - private final String title; - private final String description; - private final List categories; - private final String pageUrl; - private final String iconUrl; - private final IMod data; - private final RemoteModRepository.Type repoType; - - public RemoteMod(String slug, String author, String title, String description, List categories, String pageUrl, String iconUrl, IMod data, RemoteModRepository.Type repoType) { - this.slug = slug; - this.author = author; - this.title = title; - this.description = description; - this.categories = categories; - this.pageUrl = pageUrl; - this.iconUrl = iconUrl; - this.data = data; - this.repoType = repoType; - } - - public String getSlug() { - return slug; - } - - public String getAuthor() { - return author; - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - public List getCategories() { - return categories; - } - - public String getPageUrl() { - return pageUrl; - } - - public String getIconUrl() { - return iconUrl; - } - - public IMod getData() { - return data; - } - - public RemoteModRepository.Type getRepositoryType() { - return repoType; - } - - public enum VersionType { - Release, - Beta, - Alpha - } - - public enum DependencyType { - REQUIRED, - OPTIONAL, - TOOL, - INCLUDE, - EMBEDDED, - INCOMPATIBLE, - BROKEN - } - - public static final class Dependency { - private static Dependency BROKEN_DEPENDENCY = null; - - private final DependencyType type; - - private final RemoteModRepository remoteModRepository; - - private final String id; - - private transient RemoteMod remoteMod = null; - - private Dependency(DependencyType type, RemoteModRepository remoteModRepository, String modid) { - this.type = type; - this.remoteModRepository = remoteModRepository; - this.id = modid; - } - - public static Dependency ofGeneral(DependencyType type, RemoteModRepository remoteModRepository, String modid) { - if (type == DependencyType.BROKEN) { - return ofBroken(); - } else { - return new Dependency(type, remoteModRepository, modid); - } - } - - public static Dependency ofBroken() { - if (BROKEN_DEPENDENCY == null) { - BROKEN_DEPENDENCY = new Dependency(DependencyType.BROKEN, null, null); - } - return BROKEN_DEPENDENCY; - } - - public DependencyType getType() { - return this.type; - } - - public RemoteModRepository getRemoteModRepository() { - return this.remoteModRepository; - } - - public String getId() { - return this.id; - } - - public RemoteMod load(DownloadProvider downloadProvider) throws IOException { - if (this.remoteMod == null) { - if (this.type == DependencyType.BROKEN) { - this.remoteMod = RemoteMod.BROKEN; - } else { - this.remoteMod = this.remoteModRepository.resolveDependency(downloadProvider, this.id); - } - } - return this.remoteMod; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Dependency that = (Dependency) o; - - if (type != that.type) return false; - if (!remoteModRepository.equals(that.remoteModRepository)) return false; - return id.equals(that.id); - } - - @Override - public int hashCode() { - int result = type.hashCode(); - result = 31 * result + remoteModRepository.hashCode(); - result = 31 * result + id.hashCode(); - return result; - } - } - - public enum Type { - CURSEFORGE( - CurseForgeRemoteModRepository.MODS, - CurseForgeRemoteModRepository.RESOURCE_PACKS, - CurseForgeRemoteModRepository.SHADERS, - CurseForgeRemoteModRepository.WORLDS, - CurseForgeRemoteModRepository.MODPACKS, - CurseForgeRemoteModRepository.CUSTOMIZATIONS - ), - MODRINTH( - ModrinthRemoteModRepository.MODS, - ModrinthRemoteModRepository.RESOURCE_PACKS, - ModrinthRemoteModRepository.SHADER_PACKS, - null, - ModrinthRemoteModRepository.MODPACKS, - null - ); - - public final RemoteModRepository modRepo; - public final RemoteModRepository resourcePackRepo; - public final RemoteModRepository shaderPackRepo; - public final RemoteModRepository worldRepo; - public final RemoteModRepository modpackRepo; - public final RemoteModRepository customizationRepo; - - @Nullable - public RemoteModRepository getRepoForType(RemoteModRepository.Type type) { - return switch (type) { - case MOD -> modRepo; - case RESOURCE_PACK -> resourcePackRepo; - case SHADER_PACK -> shaderPackRepo; - case WORLD -> worldRepo; - case MODPACK -> modpackRepo; - case CUSTOMIZATION -> customizationRepo; - }; - } - - Type( - RemoteModRepository modRepo, - RemoteModRepository resourcePackRepo, - RemoteModRepository shaderPackRepo, - RemoteModRepository worldRepo, - RemoteModRepository modpackRepo, - RemoteModRepository customizationRepo - ) { - this.modRepo = modRepo; - this.resourcePackRepo = resourcePackRepo; - this.shaderPackRepo = shaderPackRepo; - this.worldRepo = worldRepo; - this.modpackRepo = modpackRepo; - this.customizationRepo = customizationRepo; - } - } - - public interface IMod { - List loadDependencies(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException; - - Stream loadVersions(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException; - } - - public interface IVersion { - Type getType(); - } - - public static class Version { - private final IVersion self; - private final String modid; - private final String name; - private final String version; - private final String changelog; - private final Instant datePublished; - private final VersionType versionType; - private final File file; - private final List dependencies; - private final List gameVersions; - private final List loaders; - - public Version(IVersion self, String modid, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List dependencies, List gameVersions, List loaders) { - this.self = self; - this.modid = modid; - this.name = name; - this.version = version; - this.changelog = changelog; - this.datePublished = datePublished; - this.versionType = versionType; - this.file = file; - this.dependencies = dependencies; - this.gameVersions = gameVersions; - this.loaders = loaders; - } - - public IVersion getSelf() { - return self; - } - - public String getModid() { - return modid; - } - - public String getName() { - return name; - } - - public String getVersion() { - return version; - } - - public String getChangelog() { - return changelog; - } - - public Instant getDatePublished() { - return datePublished; - } - - public VersionType getVersionType() { - return versionType; - } - - public File getFile() { - return file; - } - - public List getDependencies() { - return dependencies; - } - - public List getGameVersions() { - return gameVersions; - } - - public List getLoaders() { - return loaders; - } - } - - public static class File { - private final Map hashes; - private final String url; - private final String filename; - - public File(Map hashes, String url, String filename) { - this.hashes = hashes; - this.url = url; - this.filename = filename; - } - - public Map getHashes() { - return hashes; - } - - public FileDownloadTask.IntegrityCheck getIntegrityCheck() { - if (hashes.containsKey("md5")) { - return new FileDownloadTask.IntegrityCheck("MD5", hashes.get("md5")); - } else if (hashes.containsKey("sha1")) { - return new FileDownloadTask.IntegrityCheck("SHA-1", hashes.get("sha1")); - } else if (hashes.containsKey("sha256")) { - return new FileDownloadTask.IntegrityCheck("SHA-256", hashes.get("sha256")); - } else if (hashes.containsKey("sha512")) { - return new FileDownloadTask.IntegrityCheck("SHA-512", hashes.get("sha512")); - } else { - return null; - } - } - - public String getUrl() { - return url; - } - - public String getFilename() { - return filename; - } - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseAddon.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseAddon.java deleted file mode 100644 index ad80270bb63..00000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseAddon.java +++ /dev/null @@ -1,741 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui 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 . - */ -package org.jackhuang.hmcl.mod.curse; - -import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.util.Immutable; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.Pair; -import org.jackhuang.hmcl.util.StringUtils; -import org.jackhuang.hmcl.util.versioning.GameVersionNumber; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.time.Instant; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@Immutable -public class CurseAddon implements RemoteMod.IMod { - public static final Map RELATION_TYPE = Lang.mapOf( - Pair.pair(1, RemoteMod.DependencyType.EMBEDDED), - Pair.pair(2, RemoteMod.DependencyType.OPTIONAL), - Pair.pair(3, RemoteMod.DependencyType.REQUIRED), - Pair.pair(4, RemoteMod.DependencyType.TOOL), - Pair.pair(5, RemoteMod.DependencyType.INCOMPATIBLE), - Pair.pair(6, RemoteMod.DependencyType.INCLUDE) - ); - - private final int id; - private final int gameId; - private final String name; - private final String slug; - private final Links links; - private final String summary; - private final int status; - private final int downloadCount; - private final boolean isFeatured; - private final int primaryCategoryId; - private final List categories; - private final int classId; - private final List authors; - private final Logo logo; - private final int mainFileId; - private final List latestFiles; - private final List latestFileIndices; - private final Instant dateCreated; - private final Instant dateModified; - private final Instant dateReleased; - private final boolean allowModDistribution; - private final int gamePopularityRank; - private final boolean isAvailable; - private final int thumbsUpCount; - - public CurseAddon(int id, int gameId, String name, String slug, Links links, String summary, int status, int downloadCount, boolean isFeatured, int primaryCategoryId, List categories, int classId, List authors, Logo logo, int mainFileId, List latestFiles, List latestFileIndices, Instant dateCreated, Instant dateModified, Instant dateReleased, boolean allowModDistribution, int gamePopularityRank, boolean isAvailable, int thumbsUpCount) { - this.id = id; - this.gameId = gameId; - this.name = name; - this.slug = slug; - this.links = links; - this.summary = summary; - this.status = status; - this.downloadCount = downloadCount; - this.isFeatured = isFeatured; - this.primaryCategoryId = primaryCategoryId; - this.categories = categories; - this.classId = classId; - this.authors = authors; - this.logo = logo; - this.mainFileId = mainFileId; - this.latestFiles = latestFiles; - this.latestFileIndices = latestFileIndices; - this.dateCreated = dateCreated; - this.dateModified = dateModified; - this.dateReleased = dateReleased; - this.allowModDistribution = allowModDistribution; - this.gamePopularityRank = gamePopularityRank; - this.isAvailable = isAvailable; - this.thumbsUpCount = thumbsUpCount; - } - - public int getId() { - return id; - } - - public int getGameId() { - return gameId; - } - - public String getName() { - return name; - } - - public String getSlug() { - return slug; - } - - public Links getLinks() { - return links; - } - - public String getSummary() { - return summary; - } - - public int getStatus() { - return status; - } - - public int getDownloadCount() { - return downloadCount; - } - - public boolean isFeatured() { - return isFeatured; - } - - public int getPrimaryCategoryId() { - return primaryCategoryId; - } - - public List getCategories() { - return categories; - } - - public int getClassId() { - return classId; - } - - public List getAuthors() { - return authors; - } - - public Logo getLogo() { - return logo; - } - - public int getMainFileId() { - return mainFileId; - } - - public List getLatestFiles() { - return latestFiles; - } - - public List getLatestFileIndices() { - return latestFileIndices; - } - - public Instant getDateCreated() { - return dateCreated; - } - - public Instant getDateModified() { - return dateModified; - } - - public Instant getDateReleased() { - return dateReleased; - } - - public boolean isAllowModDistribution() { - return allowModDistribution; - } - - public int getGamePopularityRank() { - return gamePopularityRank; - } - - public boolean isAvailable() { - return isAvailable; - } - - public int getThumbsUpCount() { - return thumbsUpCount; - } - - @Override - public List loadDependencies(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - Set dependencies = latestFiles.stream() - .flatMap(latestFile -> latestFile.getDependencies().stream()) - .filter(dep -> dep.getRelationType() == 3) - .map(Dependency::getModId) - .collect(Collectors.toSet()); - List mods = new ArrayList<>(); - for (int dependencyId : dependencies) { - mods.add(modRepository.getModById(downloadProvider, Integer.toString(dependencyId))); - } - return mods; - } - - @Override - public Stream loadVersions(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - return modRepository.getRemoteVersionsById(downloadProvider, Integer.toString(id)); - } - - public RemoteMod toMod(RemoteModRepository.Type type) { - String iconUrl = ""; - if (logo != null) { - if (StringUtils.isNotBlank(logo.getThumbnailUrl())) - iconUrl = logo.getThumbnailUrl(); - else if (StringUtils.isNotBlank(logo.getUrl())) - iconUrl = logo.getUrl(); - } - - return new RemoteMod( - slug, - "", - name, - summary, - categories.stream().map(category -> Integer.toString(category.getId())).collect(Collectors.toList()), - links.websiteUrl, - iconUrl, - this, - type - ); - } - - @Immutable - public static class Links { - private final String websiteUrl; - private final String wikiUrl; - private final String issuesUrl; - private final String sourceUrl; - - public Links(String websiteUrl, String wikiUrl, String issuesUrl, String sourceUrl) { - this.websiteUrl = websiteUrl; - this.wikiUrl = wikiUrl; - this.issuesUrl = issuesUrl; - this.sourceUrl = sourceUrl; - } - - public String getWebsiteUrl() { - return websiteUrl; - } - - public String getWikiUrl() { - return wikiUrl; - } - - @Nullable - public String getIssuesUrl() { - return issuesUrl; - } - - @Nullable - public String getSourceUrl() { - return sourceUrl; - } - } - - @Immutable - public static class Author { - private final int id; - private final String name; - private final String url; - - public Author(int id, String name, String url) { - this.id = id; - this.name = name; - this.url = url; - } - - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public String getUrl() { - return url; - } - } - - @Immutable - public static class Logo { - private final int id; - private final int modId; - private final String title; - private final String description; - private final String thumbnailUrl; - private final String url; - - public Logo(int id, int modId, String title, String description, String thumbnailUrl, String url) { - this.id = id; - this.modId = modId; - this.title = title; - this.description = description; - this.thumbnailUrl = thumbnailUrl; - this.url = url; - } - - public int getId() { - return id; - } - - public int getModId() { - return modId; - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - public String getThumbnailUrl() { - return thumbnailUrl; - } - - public String getUrl() { - return url; - } - } - - @Immutable - public static class Attachment { - private final int id; - private final int projectId; - private final String description; - private final boolean isDefault; - private final String thumbnailUrl; - private final String title; - private final String url; - private final int status; - - public Attachment(int id, int projectId, String description, boolean isDefault, String thumbnailUrl, String title, String url, int status) { - this.id = id; - this.projectId = projectId; - this.description = description; - this.isDefault = isDefault; - this.thumbnailUrl = thumbnailUrl; - this.title = title; - this.url = url; - this.status = status; - } - - public int getId() { - return id; - } - - public int getProjectId() { - return projectId; - } - - public String getDescription() { - return description; - } - - public boolean isDefault() { - return isDefault; - } - - public String getThumbnailUrl() { - return thumbnailUrl; - } - - public String getTitle() { - return title; - } - - public String getUrl() { - return url; - } - - public int getStatus() { - return status; - } - } - - @Immutable - public static class Dependency { - private final int modId; - private final int relationType; - - public Dependency() { - this(0, 1); - } - - public Dependency(int modId, int relationType) { - this.modId = modId; - this.relationType = relationType; - } - - public int getModId() { - return modId; - } - - public int getRelationType() { - return relationType; - } - } - - /** - * @see Schema - */ - @Immutable - public static class LatestFileHash { - private final String value; - private final int algo; - - public LatestFileHash(String value, int algo) { - this.value = value; - this.algo = algo; - } - - public String getValue() { - return value; - } - - public int getAlgo() { - return algo; - } - } - - /** - * @see Schema - */ - @Immutable - public static class LatestFile implements RemoteMod.IVersion { - private final int id; - private final int gameId; - private final int modId; - private final boolean isAvailable; - private final String displayName; - private final String fileName; - private final int releaseType; - private final int fileStatus; - private final List hashes; - private final Instant fileDate; - private final int fileLength; - private final int downloadCount; - private final String downloadUrl; - private final List gameVersions; - private final List dependencies; - private final int alternateFileId; - private final boolean isServerPack; - private final long fileFingerprint; - - public LatestFile(int id, int gameId, int modId, boolean isAvailable, String displayName, String fileName, int releaseType, int fileStatus, List hashes, Instant fileDate, int fileLength, int downloadCount, String downloadUrl, List gameVersions, List dependencies, int alternateFileId, boolean isServerPack, long fileFingerprint) { - this.id = id; - this.gameId = gameId; - this.modId = modId; - this.isAvailable = isAvailable; - this.displayName = displayName; - this.fileName = fileName; - this.releaseType = releaseType; - this.fileStatus = fileStatus; - this.hashes = hashes; - this.fileDate = fileDate; - this.fileLength = fileLength; - this.downloadCount = downloadCount; - this.downloadUrl = downloadUrl; - this.gameVersions = gameVersions; - this.dependencies = dependencies; - this.alternateFileId = alternateFileId; - this.isServerPack = isServerPack; - this.fileFingerprint = fileFingerprint; - } - - public int getId() { - return id; - } - - public int getGameId() { - return gameId; - } - - public int getModId() { - return modId; - } - - public boolean isAvailable() { - return isAvailable; - } - - public String getDisplayName() { - return displayName; - } - - public String getFileName() { - return fileName; - } - - public int getReleaseType() { - return releaseType; - } - - public int getFileStatus() { - return fileStatus; - } - - public List getHashes() { - return hashes; - } - - public Instant getFileDate() { - return fileDate; - } - - public int getFileLength() { - return fileLength; - } - - public int getDownloadCount() { - return downloadCount; - } - - public String getDownloadUrl() { - if (downloadUrl == null) { - // This addon is not allowed for distribution, and downloadUrl will be null. - // We try to find its download url. - return String.format("https://edge.forgecdn.net/files/%d/%d/%s", id / 1000, id % 1000, fileName); - } - return downloadUrl; - } - - public List getGameVersions() { - return gameVersions; - } - - public List getDependencies() { - return dependencies; - } - - public int getAlternateFileId() { - return alternateFileId; - } - - public boolean isServerPack() { - return isServerPack; - } - - public long getFileFingerprint() { - return fileFingerprint; - } - - @Override - public RemoteMod.Type getType() { - return RemoteMod.Type.CURSEFORGE; - } - - public RemoteMod.Version toVersion() { - RemoteMod.VersionType versionType; - switch (getReleaseType()) { - case 1: - versionType = RemoteMod.VersionType.Release; - break; - case 2: - versionType = RemoteMod.VersionType.Beta; - break; - case 3: - versionType = RemoteMod.VersionType.Alpha; - break; - default: - versionType = RemoteMod.VersionType.Release; - break; - } - - return new RemoteMod.Version( - this, - Integer.toString(modId), - getDisplayName(), - getFileName(), - null, - getFileDate(), - versionType, - new RemoteMod.File(Collections.emptyMap(), getDownloadUrl(), getFileName()), - dependencies.stream().map(dependency -> { - if (!RELATION_TYPE.containsKey(dependency.getRelationType())) { - throw new IllegalStateException("Broken datas."); - } - return RemoteMod.Dependency.ofGeneral(RELATION_TYPE.get(dependency.getRelationType()), CurseForgeRemoteModRepository.MODS, Integer.toString(dependency.getModId())); - }).distinct().filter(Objects::nonNull).collect(Collectors.toList()), - gameVersions.stream().filter(GameVersionNumber::isKnown).toList(), - gameVersions.stream().flatMap(version -> { - if ("fabric".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FABRIC); - else if ("forge".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FORGE); - else if ("quilt".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.QUILT); - else if ("neoforge".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.NEO_FORGE); - else return Stream.empty(); - }).collect(Collectors.toList()) - ); - } - } - - /** - * @see Schema - */ - @Immutable - public static class LatestFileIndex { - private final String gameVersion; - private final int fileId; - private final String filename; - private final int releaseType; - private final int gameVersionTypeId; - private final int modLoader; - - public LatestFileIndex(String gameVersion, int fileId, String filename, int releaseType, int gameVersionTypeId, int modLoader) { - this.gameVersion = gameVersion; - this.fileId = fileId; - this.filename = filename; - this.releaseType = releaseType; - this.gameVersionTypeId = gameVersionTypeId; - this.modLoader = modLoader; - } - - public String getGameVersion() { - return gameVersion; - } - - public int getFileId() { - return fileId; - } - - public String getFilename() { - return filename; - } - - public int getReleaseType() { - return releaseType; - } - - @Nullable - public int getGameVersionTypeId() { - return gameVersionTypeId; - } - - public int getModLoader() { - return modLoader; - } - } - - @Immutable - public static class Category { - private final int id; - private final int gameId; - private final String name; - private final String slug; - private final String url; - private final String iconUrl; - private final Instant dateModified; - private final boolean isClass; - private final int classId; - private final int parentCategoryId; - - private transient final List subcategories; - - public Category() { - this(0, 0, "", "", "", "", Instant.now(), false, 0, 0); - } - - public Category(int id, int gameId, String name, String slug, String url, String iconUrl, Instant dateModified, boolean isClass, int classId, int parentCategoryId) { - this.id = id; - this.gameId = gameId; - this.name = name; - this.slug = slug; - this.url = url; - this.iconUrl = iconUrl; - this.dateModified = dateModified; - this.isClass = isClass; - this.classId = classId; - this.parentCategoryId = parentCategoryId; - - this.subcategories = new ArrayList<>(); - } - - public int getId() { - return id; - } - - public int getGameId() { - return gameId; - } - - public String getName() { - return name; - } - - public String getSlug() { - return slug; - } - - public String getUrl() { - return url; - } - - public String getIconUrl() { - return iconUrl; - } - - public Instant getDateModified() { - return dateModified; - } - - public boolean isClass() { - return isClass; - } - - public int getClassId() { - return classId; - } - - public int getParentCategoryId() { - return parentCategoryId; - } - - public List getSubcategories() { - return subcategories; - } - - public RemoteModRepository.Category toCategory() { - return new RemoteModRepository.Category( - this, - Integer.toString(id), - getSubcategories().stream().map(Category::toCategory).collect(Collectors.toList())); - } - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java deleted file mode 100644 index be13565678a..00000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui 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 . - */ -package org.jackhuang.hmcl.mod.curse; - -import com.google.gson.reflect.TypeToken; -import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.util.MurmurHash2; -import org.jackhuang.hmcl.util.Pair; -import org.jackhuang.hmcl.util.StringUtils; -import org.jackhuang.hmcl.util.io.HttpRequest; -import org.jackhuang.hmcl.util.io.JarUtils; -import org.jackhuang.hmcl.util.io.NetworkUtils; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.Semaphore; -import java.util.stream.Stream; - -import static org.jackhuang.hmcl.util.Lang.mapOf; -import static org.jackhuang.hmcl.util.Pair.pair; -import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - -public final class CurseForgeRemoteModRepository implements RemoteModRepository { - - private static final String PREFIX = "https://api.curseforge.com"; - private static final String apiKey = System.getProperty("hmcl.curseforge.apikey", JarUtils.getAttribute("hmcl.curseforge.apikey", "")); - private static final Semaphore SEMAPHORE = new Semaphore(16); - - private static final int WORD_PERFECT_MATCH_WEIGHT = 5; - - private static R withApiKey(R request) { - if (request.getUrl().startsWith(PREFIX) && !apiKey.isEmpty()) { - request.header("X-API-KEY", apiKey); - } - return request; - } - - public static boolean isAvailable() { - return !apiKey.isEmpty(); - } - - private final Type type; - private final int section; - - public CurseForgeRemoteModRepository(Type type, int section) { - this.type = type; - this.section = section; - } - - @Override - public Type getType() { - return type; - } - - private int toModsSearchSortField(SortType sort) { - // https://docs.curseforge.com/#tocS_ModsSearchSortField - switch (sort) { - case DATE_CREATED: - return 1; - case POPULARITY: - return 2; - case LAST_UPDATED: - return 3; - case NAME: - return 4; - case AUTHOR: - return 5; - case TOTAL_DOWNLOADS: - return 6; - default: - return 8; - } - } - - private String toSortOrder(SortOrder sortOrder) { - // https://docs.curseforge.com/#tocS_SortOrder - switch (sortOrder) { - case ASC: - return "asc"; - case DESC: - return "desc"; - } - return "asc"; - } - - private int calculateTotalPages(Response> response, int pageSize) { - return (int) Math.ceil((double) Math.min(response.pagination.totalCount, 10000) / pageSize); - } - - @Override - public SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - int categoryId = 0; - if (category != null && category.self() instanceof CurseAddon.Category) { - categoryId = ((CurseAddon.Category) category.self()).getId(); - } - - var query = new LinkedHashMap(); - query.put("gameId", "432"); - query.put("classId", Integer.toString(section)); - if (categoryId != 0) - query.put("categoryId", Integer.toString(categoryId)); - query.put("gameVersion", gameVersion); - query.put("searchFilter", searchFilter); - query.put("sortField", Integer.toString(toModsSearchSortField(sortType))); - query.put("sortOrder", toSortOrder(sortOrder)); - query.put("index", Integer.toString(pageOffset * pageSize)); - query.put("pageSize", Integer.toString(pageSize)); - - Response> response = null; - - IOException exception = null; - List candidates = downloadProvider.injectURLWithCandidates(NetworkUtils.withQuery(PREFIX + "/v1/mods/search", query)); - for (URI candidate : candidates) { - LOG.info("Fetching " + candidate); - try { - response = withApiKey(HttpRequest.GET(candidate.toString())) - .getJson(Response.typeOf(listTypeOf(CurseAddon.class))); - if (searchFilter.isEmpty()) { - return new SearchResult(response.getData().stream().map(addon -> addon.toMod(type)), calculateTotalPages(response, pageSize)); - } - break; - } catch (IOException e) { - LOG.warning("Failed to search addons: " + candidate, e); - if (candidates.size() == 1) { - exception = e; - } else { - if (exception == null) { - exception = new IOException("Failed to search addons"); - } - exception.addSuppressed(e); - } - } - } - - if (response == null) { - throw exception != null ? exception : new IOException("No candidates found"); - } - - // https://github.com/HMCL-dev/HMCL/issues/1549 - String lowerCaseSearchFilter = searchFilter.toLowerCase(Locale.ROOT); - Map searchFilterWords = new HashMap<>(); - for (String s : StringUtils.tokenize(lowerCaseSearchFilter)) { - searchFilterWords.put(s, searchFilterWords.getOrDefault(s, 0) + 1); - } - - StringUtils.LevCalculator levCalculator = new StringUtils.LevCalculator(); - - return new SearchResult(response.getData().stream().map(addon -> addon.toMod(type)).map(remoteMod -> { - String lowerCaseResult = remoteMod.getTitle().toLowerCase(Locale.ROOT); - int diff = levCalculator.calc(lowerCaseSearchFilter, lowerCaseResult); - - for (String s : StringUtils.tokenize(lowerCaseResult)) { - if (searchFilterWords.containsKey(s)) { - diff -= WORD_PERFECT_MATCH_WEIGHT * searchFilterWords.get(s) * s.length(); - } - } - - return pair(remoteMod, diff); - }).sorted(Comparator.comparingInt(Pair::getValue)).map(Pair::getKey), response.getData().stream().map(addon -> addon.toMod(type)), calculateTotalPages(response, pageSize)); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public Optional getRemoteVersionByLocalFile(Path file) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (InputStream stream = Files.newInputStream(file)) { - byte[] buf = new byte[1024]; - int len; - while ((len = stream.read(buf, 0, buf.length)) != -1) { - for (int i = 0; i < len; i++) { - byte b = buf[i]; - if (b != 0x9 && b != 0xa && b != 0xd && b != 0x20) { - baos.write(b); - } - } - } - } - - long hash = Integer.toUnsignedLong(MurmurHash2.hash32(baos.toByteArray(), baos.size(), 1)); - if (hash == 811513880) { // Workaround for https://github.com/HMCL-dev/HMCL/issues/4597 - return Optional.empty(); - } - - SEMAPHORE.acquireUninterruptibly(); - try { - Response response = withApiKey(HttpRequest.POST(PREFIX + "/v1/fingerprints/432")) - .json(mapOf(pair("fingerprints", Collections.singletonList(hash)))) - .getJson(Response.typeOf(FingerprintMatchesResult.class)); - - if (response.getData().getExactMatches() == null || response.getData().getExactMatches().isEmpty()) { - return Optional.empty(); - } - - return Optional.of(response.getData().getExactMatches().get(0).getFile().toVersion()); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public RemoteMod getModById(DownloadProvider downloadProvider, String id) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - Response response = withApiKey(HttpRequest.GET(PREFIX + "/v1/mods/" + id)) - .getJson(Response.typeOf(CurseAddon.class)); - return response.data.toMod(type); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public RemoteMod.File getModFile(String modId, String fileId) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - Response response = withApiKey(HttpRequest.GET(String.format("%s/v1/mods/%s/files/%s", PREFIX, modId, fileId))) - .getJson(Response.typeOf(CurseAddon.LatestFile.class)); - return response.getData().toVersion().getFile(); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - Response> response = withApiKey(HttpRequest.GET(PREFIX + "/v1/mods/" + id + "/files", - pair("pageSize", "10000"))) - .getJson(Response.typeOf(listTypeOf(CurseAddon.LatestFile.class))); - return response.getData().stream().map(CurseAddon.LatestFile::toVersion); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public Stream getCategories() throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - Response> categories = withApiKey(HttpRequest.GET(PREFIX + "/v1/categories", pair("gameId", "432"))) - .getJson(Response.typeOf(listTypeOf(CurseAddon.Category.class))); - return reorganizeCategories(categories.getData(), section).stream().map(CurseAddon.Category::toCategory); - } finally { - SEMAPHORE.release(); - } - } - - private List reorganizeCategories(List categories, int rootId) { - List result = new ArrayList<>(); - - Map categoryMap = new HashMap<>(); - for (CurseAddon.Category category : categories) { - categoryMap.put(category.getId(), category); - } - for (CurseAddon.Category category : categories) { - if (category.getParentCategoryId() == rootId) { - result.add(category); - } else { - CurseAddon.Category parentCategory = categoryMap.get(category.getParentCategoryId()); - if (parentCategory == null) { - // Category list is not correct, so we ignore this item. - continue; - } - parentCategory.getSubcategories().add(category); - } - } - return result; - } - - public static final int SECTION_BUKKIT_PLUGIN = 5; - public static final int SECTION_MOD = 6; - public static final int SECTION_RESOURCE_PACK = 12; - public static final int SECTION_WORLD = 17; - public static final int SECTION_MODPACK = 4471; - public static final int SECTION_SHADER = 6552; - public static final int SECTION_CUSTOMIZATION = 4546; - public static final int SECTION_ADDONS = 4559; // For Pocket Edition - public static final int SECTION_UNKNOWN1 = 4944; - public static final int SECTION_UNKNOWN2 = 4979; - public static final int SECTION_UNKNOWN3 = 4984; - - public static final CurseForgeRemoteModRepository MODS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.MOD, SECTION_MOD); - public static final CurseForgeRemoteModRepository MODPACKS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.MODPACK, SECTION_MODPACK); - public static final CurseForgeRemoteModRepository RESOURCE_PACKS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.RESOURCE_PACK, SECTION_RESOURCE_PACK); - public static final CurseForgeRemoteModRepository WORLDS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.WORLD, SECTION_WORLD); - public static final CurseForgeRemoteModRepository CUSTOMIZATIONS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.CUSTOMIZATION, SECTION_CUSTOMIZATION); - public static final CurseForgeRemoteModRepository SHADERS = new CurseForgeRemoteModRepository(RemoteModRepository.Type.SHADER_PACK, SECTION_SHADER); - - public static class Pagination { - private final int index; - private final int pageSize; - private final int resultCount; - private final int totalCount; - - public Pagination(int index, int pageSize, int resultCount, int totalCount) { - this.index = index; - this.pageSize = pageSize; - this.resultCount = resultCount; - this.totalCount = totalCount; - } - - public int getIndex() { - return index; - } - - public int getPageSize() { - return pageSize; - } - - public int getResultCount() { - return resultCount; - } - - public int getTotalCount() { - return totalCount; - } - } - - public static class Response { - - @SuppressWarnings("unchecked") - public static TypeToken> typeOf(Class responseType) { - return (TypeToken>) TypeToken.getParameterized(Response.class, responseType); - } - - @SuppressWarnings("unchecked") - public static TypeToken> typeOf(TypeToken responseType) { - return (TypeToken>) TypeToken.getParameterized(Response.class, responseType.getType()); - } - - private final T data; - private final Pagination pagination; - - public Response(T data, Pagination pagination) { - this.data = data; - this.pagination = pagination; - } - - public T getData() { - return data; - } - - public Pagination getPagination() { - return pagination; - } - } - - /** - * @see Schema - */ - private static class FingerprintMatchesResult { - private final boolean isCacheBuilt; - private final List exactMatches; - private final List exactFingerprints; - - public FingerprintMatchesResult(boolean isCacheBuilt, List exactMatches, List exactFingerprints) { - this.isCacheBuilt = isCacheBuilt; - this.exactMatches = exactMatches; - this.exactFingerprints = exactFingerprints; - } - - public boolean isCacheBuilt() { - return isCacheBuilt; - } - - public List getExactMatches() { - return exactMatches; - } - - public List getExactFingerprints() { - return exactFingerprints; - } - } - - /** - * @see Schema - */ - private static class FingerprintMatch { - private final int id; - private final CurseAddon.LatestFile file; - private final List latestFiles; - - public FingerprintMatch(int id, CurseAddon.LatestFile file, List latestFiles) { - this.id = id; - this.file = file; - this.latestFiles = latestFiles; - } - - public int getId() { - return id; - } - - public CurseAddon.LatestFile getFile() { - return file; - } - - public List getLatestFiles() { - return latestFiles; - } - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java deleted file mode 100644 index 0b160c33b3c..00000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthRemoteModRepository.java +++ /dev/null @@ -1,917 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui 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 . - */ -package org.jackhuang.hmcl.mod.modrinth; - -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.mod.ModLoaderType; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.RemoteModRepository; -import org.jackhuang.hmcl.util.*; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.HttpRequest; -import org.jackhuang.hmcl.util.io.NetworkUtils; -import org.jackhuang.hmcl.util.io.ResponseCodeException; -import org.jetbrains.annotations.Nullable; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URI; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.time.Instant; -import java.util.*; -import java.util.concurrent.Semaphore; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.jackhuang.hmcl.util.Lang.mapOf; -import static org.jackhuang.hmcl.util.Pair.pair; -import static org.jackhuang.hmcl.util.gson.JsonUtils.listTypeOf; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - -public final class ModrinthRemoteModRepository implements RemoteModRepository { - public static final ModrinthRemoteModRepository MODS = new ModrinthRemoteModRepository("mod"); - public static final ModrinthRemoteModRepository MODPACKS = new ModrinthRemoteModRepository("modpack"); - public static final ModrinthRemoteModRepository RESOURCE_PACKS = new ModrinthRemoteModRepository("resourcepack"); - public static final ModrinthRemoteModRepository SHADER_PACKS = new ModrinthRemoteModRepository("shader"); - - private static final Comparator TAG_COMPARATOR = PriorityComparator.of( - List.of("babric", - "bta-babric", - "bukkit", - "bungeecord", - "canvas", - "datapack", - "fabric", - "folia", - "forge", - "geyser", - "iris", - "java-agent", - "legacy-fabric", - "liteloader", - "minecraft", - "modloader", - "mrpack", - "neoforge", - "nilloader", - "optifine", - "ornith", - "paper", - "purpur", - "quilt", - "rift", - "spigot", - "sponge", - "vanilla", - "velocity", - "waterfall"), - Comparator.naturalOrder(), - false - ); - - private static final Semaphore SEMAPHORE = new Semaphore(16); - - private static final String PREFIX = "https://api.modrinth.com"; - - private final String projectType; - - private final RemoteModRepository.Type type; - - private ModrinthRemoteModRepository(String projectType) { - this.projectType = projectType; - this.type = switch (projectType) { - case "modpack" -> Type.MODPACK; - case "resourcepack" -> Type.RESOURCE_PACK; - case "shader" -> Type.SHADER_PACK; - default -> Type.MOD; - }; - } - - @Override - public Type getType() { - return this.type; - } - - private static String convertSortType(SortType sortType) { - switch (sortType) { - case DATE_CREATED: - return "newest"; - case POPULARITY: - case NAME: - case AUTHOR: - return "relevance"; - case LAST_UPDATED: - return "updated"; - case TOTAL_DOWNLOADS: - return "downloads"; - default: - throw new IllegalArgumentException("Unsupported sort type " + sortType); - } - } - - static List sortDisplayCategories(List displayCategories) { - return displayCategories != null && !displayCategories.isEmpty() - ? displayCategories.stream().sorted(TAG_COMPARATOR).toList() - : List.of(); - } - - @Override - public SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sort, SortOrder sortOrder) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - List> facets = new ArrayList<>(); - facets.add(Collections.singletonList("project_type:" + projectType)); - if (StringUtils.isNotBlank(gameVersion)) { - facets.add(Collections.singletonList("versions:" + gameVersion)); - } - if (category != null && StringUtils.isNotBlank(category.id())) { - facets.add(Collections.singletonList("categories:" + category.id())); - } - Map query = mapOf( - pair("query", searchFilter), - pair("facets", JsonUtils.UGLY_GSON.toJson(facets)), - pair("offset", Integer.toString(pageOffset * pageSize)), - pair("limit", Integer.toString(pageSize)), - pair("index", convertSortType(sort)) - ); - - List candidates = downloadProvider.injectURLWithCandidates(NetworkUtils.withQuery(PREFIX + "/v2/search", query)); - IOException exception = null; - for (URI candidate : candidates) { - try { - LOG.info("Fetching " + candidate); - Response response = HttpRequest.GET(candidate.toString()) - .getJson(Response.typeOf(ProjectSearchResult.class)); - return new SearchResult(response.getHits().stream().map(ProjectSearchResult::toMod), (int) Math.ceil((double) response.totalHits / pageSize)); - } catch (IOException e) { - LOG.warning("Failed to search addons: " + candidate, e); - - IOException wrapper = new IOException("Failed to search addons: " + candidate, e); - if (candidates.size() == 1) { - exception = wrapper; - } else { - if (exception == null) { - exception = new IOException("Failed to search addons"); - } - exception.addSuppressed(wrapper); - } - } - } - - throw exception != null ? exception : new IOException("No candidates found"); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public Optional getRemoteVersionByLocalFile(Path file) throws IOException { - String sha1 = DigestUtils.digestToString("SHA-1", file); - - SEMAPHORE.acquireUninterruptibly(); - try { - ProjectVersion mod = HttpRequest.GET(PREFIX + "/v2/version_file/" + sha1, - pair("algorithm", "sha1")) - .getJson(ProjectVersion.class); - return mod.toVersion(); - } catch (ResponseCodeException e) { - if (e.getResponseCode() == 404) { - return Optional.empty(); - } else { - throw e; - } - } catch (NoSuchFileException e) { - return Optional.empty(); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public RemoteMod getModById(DownloadProvider downloadProvider, String id) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - id = StringUtils.removePrefix(id, "local-"); - List candidates = downloadProvider.injectURLWithCandidates(PREFIX + "/v2/project/" + id); - IOException exception = null; - - for (URI candidate : candidates) { - try { - Project project = HttpRequest.GET(candidate.toString()).getJson(Project.class); - return project.toMod(); - } catch (IOException e) { - IOException wrapper = new IOException("Failed to get mod: " + candidate, e); - if (candidates.size() == 1) { - exception = wrapper; - } else { - if (exception == null) { - exception = new IOException("Failed to get mod"); - } - exception.addSuppressed(wrapper); - } - } - } - - throw exception != null ? exception : new IOException("No candidates found"); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public RemoteMod resolveDependency(DownloadProvider downloadProvider, String id) throws IOException { - try { - return getModById(downloadProvider, id); - } catch (ResponseCodeException e) { - if (e.getResponseCode() == 502 || e.getResponseCode() == 404) { - return RemoteMod.BROKEN; - } - throw e; - } catch (FileNotFoundException e) { - return RemoteMod.BROKEN; - } - } - - @Override - public RemoteMod.File getModFile(String modId, String fileId) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Stream getRemoteVersionsById(DownloadProvider downloadProvider, String id) throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - id = StringUtils.removePrefix(id, "local-"); - - List candidates = downloadProvider.injectURLWithCandidates(PREFIX + "/v2/project/" + id + "/version?include_changelog=false"); - IOException exception = null; - - for (URI candidate : candidates) { - try { - List versions = HttpRequest.GET(candidate.toString()) - .getJson(listTypeOf(ProjectVersion.class)); - return versions.stream().map(ProjectVersion::toVersion).flatMap(Lang::toStream); - } catch (IOException e) { - IOException wrapper = new IOException("Failed to get remote versions: " + candidate, e); - if (candidates.size() == 1) { - exception = wrapper; - } else { - if (exception == null) { - exception = new IOException("Failed to get remote versions"); - } - exception.addSuppressed(wrapper); - } - } - } - - throw exception != null ? exception : new IOException("No candidates found"); - } finally { - SEMAPHORE.release(); - } - } - - @Override - public Stream getCategories() throws IOException { - SEMAPHORE.acquireUninterruptibly(); - try { - List categories = HttpRequest.GET(PREFIX + "/v2/tag/category").getJson(listTypeOf(Category.class)); - return categories.stream() - .filter(category -> category.getProjectType().equals(projectType)) - .map(Category::toCategory); - } finally { - SEMAPHORE.release(); - } - } - - public static class Category { - private final String icon; - - private final String name; - - @SerializedName("project_type") - private final String projectType; - - public Category() { - this("", "", ""); - } - - public Category(String icon, String name, String projectType) { - this.icon = icon; - this.name = name; - this.projectType = projectType; - } - - public String getIcon() { - return icon; - } - - public String getName() { - return name; - } - - public String getProjectType() { - return projectType; - } - - public RemoteModRepository.Category toCategory() { - return new RemoteModRepository.Category( - this, - name, - Collections.emptyList()); - } - } - - public static class Project implements RemoteMod.IMod { - private final String slug; - - private final String title; - - private final String description; - - private final List categories; - - /** - * A long body describing project in detail. - */ - private final String body; - - @SerializedName("project_type") - private final String projectType; - - private final int downloads; - - @SerializedName("icon_url") - private final String iconUrl; - - private final String id; - - private final String team; - - private final Instant published; - - private final Instant updated; - - private final List versions; - - public Project(String slug, String title, String description, List categories, String body, String projectType, int downloads, String iconUrl, String id, String team, Instant published, Instant updated, List versions) { - this.slug = slug; - this.title = title; - this.description = description; - this.categories = categories; - this.body = body; - this.projectType = projectType; - this.downloads = downloads; - this.iconUrl = iconUrl; - this.id = id; - this.team = team; - this.published = published; - this.updated = updated; - this.versions = versions; - } - - public String getSlug() { - return slug; - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - public List getCategories() { - return categories; - } - - public String getBody() { - return body; - } - - public String getProjectType() { - return projectType; - } - - public int getDownloads() { - return downloads; - } - - public String getIconUrl() { - return iconUrl; - } - - public String getId() { - return id; - } - - public String getTeam() { - return team; - } - - public Instant getPublished() { - return published; - } - - public Instant getUpdated() { - return updated; - } - - public List getVersions() { - return versions; - } - - @Override - public List loadDependencies(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - Set dependencies = modRepository.getRemoteVersionsById(downloadProvider, getId()) - .flatMap(version -> version.getDependencies().stream()) - .collect(Collectors.toSet()); - List mods = new ArrayList<>(); - for (RemoteMod.Dependency dependency : dependencies) { - mods.add(dependency.load(downloadProvider)); - } - return mods; - } - - @Override - public Stream loadVersions(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - return modRepository.getRemoteVersionsById(downloadProvider, getId()); - } - - public RemoteMod toMod() { - RemoteModRepository.Type type = switch (projectType) { - case "modpack" -> RemoteModRepository.Type.MODPACK; - case "resourcepack" -> RemoteModRepository.Type.RESOURCE_PACK; - case "shader" -> RemoteModRepository.Type.SHADER_PACK; - default -> RemoteModRepository.Type.MOD; - }; - return new RemoteMod( - slug, - "", - title, - description, - categories, - String.format("https://modrinth.com/%s/%s", projectType, id), - iconUrl, - this, - type - ); - } - } - - @Immutable - public static class Dependency { - @SerializedName("version_id") - private final String versionId; - - @SerializedName("project_id") - private final String projectId; - - @SerializedName("dependency_type") - private final String dependencyType; - - public Dependency(String versionId, String projectId, String dependencyType) { - this.versionId = versionId; - this.projectId = projectId; - this.dependencyType = dependencyType; - } - - public String getVersionId() { - return versionId; - } - - public String getProjectId() { - return projectId; - } - - public String getDependencyType() { - return dependencyType; - } - } - - public static class ProjectVersion implements RemoteMod.IVersion { - private static final Map DEPENDENCY_TYPE = Lang.mapOf( - Pair.pair("required", RemoteMod.DependencyType.REQUIRED), - Pair.pair("optional", RemoteMod.DependencyType.OPTIONAL), - Pair.pair("embedded", RemoteMod.DependencyType.EMBEDDED), - Pair.pair("incompatible", RemoteMod.DependencyType.INCOMPATIBLE) - ); - - private final String name; - - @SerializedName("version_number") - private final String versionNumber; - - private final String changelog; - - private final List dependencies; - - @SerializedName("game_versions") - private final List gameVersions; - - @SerializedName("version_type") - private final String versionType; - - private final List loaders; - - private final boolean featured; - - private final String id; - - @SerializedName("project_id") - private final String projectId; - - @SerializedName("author_id") - private final String authorId; - - @SerializedName("date_published") - private final Instant datePublished; - - private final int downloads; - - @SerializedName("changelog_url") - private final String changelogUrl; - - private final List files; - - public ProjectVersion(String name, String versionNumber, String changelog, List dependencies, List gameVersions, String versionType, List loaders, boolean featured, String id, String projectId, String authorId, Instant datePublished, int downloads, String changelogUrl, List files) { - this.name = name; - this.versionNumber = versionNumber; - this.changelog = changelog; - this.dependencies = dependencies; - this.gameVersions = gameVersions; - this.versionType = versionType; - this.loaders = loaders; - this.featured = featured; - this.id = id; - this.projectId = projectId; - this.authorId = authorId; - this.datePublished = datePublished; - this.downloads = downloads; - this.changelogUrl = changelogUrl; - this.files = files; - } - - public String getName() { - return name; - } - - public String getVersionNumber() { - return versionNumber; - } - - public String getChangelog() { - return changelog; - } - - public List getDependencies() { - return dependencies; - } - - public List getGameVersions() { - return gameVersions; - } - - public String getVersionType() { - return versionType; - } - - public List getLoaders() { - return loaders; - } - - public boolean isFeatured() { - return featured; - } - - public String getId() { - return id; - } - - public String getProjectId() { - return projectId; - } - - public String getAuthorId() { - return authorId; - } - - public Instant getDatePublished() { - return datePublished; - } - - public int getDownloads() { - return downloads; - } - - public String getChangelogUrl() { - return changelogUrl; - } - - public List getFiles() { - return files; - } - - @Override - public RemoteMod.Type getType() { - return RemoteMod.Type.MODRINTH; - } - - public Optional toVersion() { - RemoteMod.VersionType type; - if ("release".equals(versionType)) { - type = RemoteMod.VersionType.Release; - } else if ("beta".equals(versionType)) { - type = RemoteMod.VersionType.Beta; - } else if ("alpha".equals(versionType)) { - type = RemoteMod.VersionType.Alpha; - } else { - type = RemoteMod.VersionType.Release; - } - - if (files.size() == 0) { - return Optional.empty(); - } - - return Optional.of(new RemoteMod.Version( - this, - projectId, - name, - versionNumber, - changelog, - datePublished, - type, - files.get(0).toFile(), - dependencies.stream().map(dependency -> { - if (dependency.projectId == null) { - return RemoteMod.Dependency.ofBroken(); - } - - if (!DEPENDENCY_TYPE.containsKey(dependency.dependencyType)) { - throw new IllegalStateException("Broken datas"); - } - - return RemoteMod.Dependency.ofGeneral(DEPENDENCY_TYPE.get(dependency.dependencyType), MODS, dependency.projectId); - }).filter(Objects::nonNull).collect(Collectors.toList()), - gameVersions, - loaders.stream().flatMap(loader -> { - if ("fabric".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FABRIC); - else if ("forge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FORGE); - else if ("neoforge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.NEO_FORGE); - else if ("quilt".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.QUILT); - else if ("liteloader".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.LITE_LOADER); - else return Stream.empty(); - }).collect(Collectors.toList()) - )); - } - } - - public static class ProjectVersionFile { - private final Map hashes; - private final String url; - private final String filename; - private final boolean primary; - private final int size; - - public ProjectVersionFile(Map hashes, String url, String filename, boolean primary, int size) { - this.hashes = hashes; - this.url = url; - this.filename = filename; - this.primary = primary; - this.size = size; - } - - public Map getHashes() { - return hashes; - } - - public String getUrl() { - return url; - } - - public String getFilename() { - return filename; - } - - public boolean isPrimary() { - return primary; - } - - public int getSize() { - return size; - } - - public RemoteMod.File toFile() { - return new RemoteMod.File(hashes, url, filename); - } - } - - public static class ProjectSearchResult implements RemoteMod.IMod { - private final String slug; - - private final String title; - - private final String description; - - private final List categories; - - @SerializedName("display_categories") - private final List displayCategories; - - @SerializedName("project_type") - private final String projectType; - - private final int downloads; - - @SerializedName("icon_url") - private final String iconUrl; - - @SerializedName("project_id") - private final String projectId; - - private final String author; - - private final List versions; - - @SerializedName("date_created") - private final Instant dateCreated; - - @SerializedName("date_modified") - private final Instant dateModified; - - @SerializedName("latest_version") - private final String latestVersion; - - public ProjectSearchResult(String slug, String title, String description, List categories, List displayCategories, String projectType, int downloads, String iconUrl, String projectId, String author, List versions, Instant dateCreated, Instant dateModified, String latestVersion) { - this.slug = slug; - this.title = title; - this.description = description; - this.categories = categories; - this.displayCategories = displayCategories; - this.projectType = projectType; - this.downloads = downloads; - this.iconUrl = iconUrl; - this.projectId = projectId; - this.author = author; - this.versions = versions; - this.dateCreated = dateCreated; - this.dateModified = dateModified; - this.latestVersion = latestVersion; - } - - public String getSlug() { - return slug; - } - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - public List getCategories() { - return categories; - } - - public List getDisplayCategories() { - return displayCategories; - } - - public String getProjectType() { - return projectType; - } - - public int getDownloads() { - return downloads; - } - - public String getIconUrl() { - return iconUrl; - } - - public String getProjectId() { - return projectId; - } - - public String getAuthor() { - return author; - } - - public List getVersions() { - return versions; - } - - public Instant getDateCreated() { - return dateCreated; - } - - public Instant getDateModified() { - return dateModified; - } - - public String getLatestVersion() { - return latestVersion; - } - - @Override - public List loadDependencies(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - Set dependencies = modRepository.getRemoteVersionsById(downloadProvider, getProjectId()) - .flatMap(version -> version.getDependencies().stream()) - .collect(Collectors.toSet()); - List mods = new ArrayList<>(); - for (RemoteMod.Dependency dependency : dependencies) { - mods.add(dependency.load(downloadProvider)); - } - return mods; - } - - @Override - public Stream loadVersions(RemoteModRepository modRepository, DownloadProvider downloadProvider) throws IOException { - return modRepository.getRemoteVersionsById(downloadProvider, getProjectId()); - } - - public RemoteMod toMod() { - RemoteModRepository.Type type = switch (projectType) { - case "modpack" -> RemoteModRepository.Type.MODPACK; - case "resourcepack" -> RemoteModRepository.Type.RESOURCE_PACK; - case "shader" -> RemoteModRepository.Type.SHADER_PACK; - default -> RemoteModRepository.Type.MOD; - }; - return new RemoteMod( - slug, - author, - title, - description, - sortDisplayCategories(displayCategories), - String.format("https://modrinth.com/%s/%s", projectType, projectId), - iconUrl, - this, - type - ); - } - } - - public static class Response { - - @SuppressWarnings("unchecked") - public static TypeToken> typeOf(Class responseType) { - return (TypeToken>) TypeToken.getParameterized(Response.class, responseType); - } - - private final int offset; - - private final int limit; - - @SerializedName("total_hits") - private final int totalHits; - - private final List hits; - - public Response() { - this(0, 0, Collections.emptyList()); - } - - public Response(int offset, int limit, List hits) { - this.offset = offset; - this.limit = limit; - this.totalHits = hits.size(); - this.hits = hits; - } - - public int getOffset() { - return offset; - } - - public int getLimit() { - return limit; - } - - public int getTotalHits() { - return totalHits; - } - - public List getHits() { - return hits; - } - } -} diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepositoryTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/addon/curse/CurseForgeRemoteAddonRepositoryTest.java similarity index 95% rename from HMCLCore/src/test/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepositoryTest.java rename to HMCLCore/src/test/java/org/jackhuang/hmcl/addon/curse/CurseForgeRemoteAddonRepositoryTest.java index aa54776307e..6ded248177e 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepositoryTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/addon/curse/CurseForgeRemoteAddonRepositoryTest.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.mod.curse; +package org.jackhuang.hmcl.addon.curse; import org.jackhuang.hmcl.util.MurmurHash2; import org.junit.jupiter.api.Disabled; @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class CurseForgeRemoteModRepositoryTest { +public class CurseForgeRemoteAddonRepositoryTest { @Test @Disabled diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/resourcepack/ResourcePackManagerTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/resourcepack/ResourcePackManagerTest.java index 9a1dd27f740..26ab05a748b 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/resourcepack/ResourcePackManagerTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/resourcepack/ResourcePackManagerTest.java @@ -20,8 +20,8 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.junit.jupiter.api.Test; -import static org.jackhuang.hmcl.resourcepack.ResourcePackManager.isMcVersionSupported; -import static org.jackhuang.hmcl.resourcepack.ResourcePackManager.isMcVersionSupportsNewOptionsFormat; +import static org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager.isMcVersionSupported; +import static org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager.isMcVersionSupportsNewOptionsFormat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue;