mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-08 19:35:36 -04:00
Support Neoforge (#2548)
* Support #2376 * Add necessary @Nullable annotations * Display different types of dependencies in different sections. * Fix checkstyle * Add I18N for different types of dependencies. * Enhance UI * Code cleanup * Enhance UI * Manually sort the result from curseforge when searching mods by name. * Render the search results from remote mod repositories in several pages. * Fix merge * Fix * Add a button which navigates to the modpack download page in the modpack installl page * Fix I18N * Render the mod loaders supported by the version in mod info page. * Fix #2104 * Enhance TwoLineListItem * Render the mod loader supported by this mod file on the ModListPage * Fix chinese searching and curseforge searching * Update I18N * Fix * Fix * Select the specific game version when clicking the 'download' button on ModListPage * Support HMCL to update mod_data and mod_pack data from https://github.com/huanghongxun/HMCL/raw/javafx/data-json/dynamic-remote-resources.json * Enhance :HMCL:build.gradle.kts * Revert parse_mcmod_data.py * Abstract 'new Image' to FXUtils.newBuiltinImage and FXUtils.newRemoteImage FXUtils.newBuiltinImage is used to load image which is supposed to be correct definitely and is a file within the jar. Or, it will throw ResourceNotFoundError. FXUtils.newRemoteImage is used to load image from the internet. It will cache the data of images for the further usage. The cached data will be deleted when HMCL is closed or hidden. * Add javadoc for FXUtils.newBuiltinImage and FXUtils.newRemoteImage. * Fix checkstyle * Fix * Fix * Fix * Add license for RemoteResourceManager * Remove TODO * Enhance Chinese searching * Support to decode metadata for local quilt mod. * Enhance ModManager * Fix checkstyle * Refactor * Fix * Fix * Refactor DownloadPage * Fix * Revert "Refactor DownloadPage" This reverts commit 953558da77af5a0fe3153e77cdcb9b6affa30ffa. * Refactor DownloadPage * Refactor * Fix * Fix checkstyle * Set org.jackhuang.hmcl.ui.construct.TwoLineListItem.TagChangeListener as a private static inner class. * Fix * Fix * Fix * Enhance SimpleMultimap * Revert TwoLineListItem * Fix * Code cleanup * Code cleanup * Fix * Code cleanup * Add license for IModMetadataReader * Add prefix 'Minecraft' at the supported minecrft version list in DownloadPage * Fix #2498 * Update README_cn.md * Opti ModMananger * Log a warning message when 'hmcl.update_source.override' is used. * Fix chinese searching * Enhance chinese searching. * Enhance memory usage * Add basic support for NeoForge. * Enable HMCl to show the version list of NeoForged. * Fix checkstyle * Enable HMCL to download and install NeoForged from BMCL. Fix some bugs. * Close the mod version dialog window after clicking the downloading / save as button if the dependency list is empty. * Fix * Enable HMCL to install NeoForge from modpacks. * Fix the dirty implememtation * Fix UI * Cache builtin images. * Enhance FXUtils (Make tooltip installer faster). * Fix * Fix * Fix #2560 * Fix typo * Fix remote image cache. * Fix javadoc * Fix checkstyle * Optimize FXUtils::shutdown * Support NeoForged 1.20.2 * Fix merge * I have no idea on why the sha1 was matched. * Revert "Enhance FXUtils (Make tooltip installer faster)." This reverts commit 0a49eb2c1204e4be7dc0df3084faa59fdf9b0394. * Support multi download source in order balance the traffic of hmcl.huangyuhui.net and the download speed in China Mainland. * Modify dynamic remote resource urls. * Optimize codes with StringUtils.DynamicCommonSubsequence. * Prevent unofficial HMCL to access HMCL Resource Update URL. * Zip the dynamic-remote-resources json by Gradle automatically. * Remove unnecessary getters. * Fix * Fix LibraryAnalyzer: Cannot analyze the version number of NeoForge correctly. * Support to choose the NeoForge icon if a game is installed with NeoForge. * Fix checkstyle * Fix: The version number on DownloadPage of NeoForge is incorrect, which has leading string 'neoforge-'. * Fix. * Rename references. Fix: Invalid spaces in VersionIconType. --------- Co-authored-by: Burning_TNT <pangyl08@163.com“>
This commit is contained in:
parent
e6e4e36537
commit
4149876e04
@ -279,6 +279,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return newBuiltinImage("/assets/img/fabric.png");
|
||||
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FORGE))
|
||||
return newBuiltinImage("/assets/img/forge.png");
|
||||
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.NEO_FORGE))
|
||||
return newBuiltinImage("/assets/img/neoforge.png");
|
||||
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.QUILT))
|
||||
return newBuiltinImage("/assets/img/quilt.png");
|
||||
else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE))
|
||||
|
@ -510,9 +510,7 @@ public final class LauncherHelper {
|
||||
break;
|
||||
case MODDED_JAVA_16:
|
||||
// Minecraft<=1.17.1+Forge[37.0.0,37.0.60) not compatible with Java 17
|
||||
String forgePatchVersion = analyzer.getVersion(LibraryAnalyzer.LibraryType.FORGE)
|
||||
.map(LibraryAnalyzer.LibraryType.FORGE::patchVersion)
|
||||
.orElse(null);
|
||||
String forgePatchVersion = analyzer.getVersion(LibraryAnalyzer.LibraryType.FORGE).orElse(null);
|
||||
if (forgePatchVersion != null && VersionNumber.VERSION_COMPARATOR.compare(forgePatchVersion, "37.0.60") < 0)
|
||||
suggestions.add(i18n("launch.advice.forge37_0_60"));
|
||||
else
|
||||
|
@ -27,6 +27,7 @@ public enum VersionIconType {
|
||||
CRAFT_TABLE("/assets/img/craft_table.png"),
|
||||
FABRIC("/assets/img/fabric.png"),
|
||||
FORGE("/assets/img/forge.png"),
|
||||
NEO_FORGE("/assets/img/neoforge.png"),
|
||||
FURNACE("/assets/img/furnace.png"),
|
||||
QUILT("/assets/img/quilt.png");
|
||||
|
||||
|
@ -92,6 +92,9 @@ public class InstallerItem extends Control {
|
||||
case "quilt-api":
|
||||
imageUrl = "/assets/img/quilt.png";
|
||||
break;
|
||||
case "neoforge":
|
||||
imageUrl = "/assets/img/neoforge.png";
|
||||
break;
|
||||
default:
|
||||
imageUrl = null;
|
||||
break;
|
||||
@ -122,6 +125,7 @@ public class InstallerItem extends Control {
|
||||
public final InstallerItem fabric = new InstallerItem(FABRIC);
|
||||
public final InstallerItem fabricApi = new InstallerItem(FABRIC_API);
|
||||
public final InstallerItem forge = new InstallerItem(FORGE);
|
||||
public final InstallerItem neoForge = new InstallerItem(NEO_FORGE);
|
||||
public final InstallerItem liteLoader = new InstallerItem(LITELOADER);
|
||||
public final InstallerItem optiFine = new InstallerItem(OPTIFINE);
|
||||
public final InstallerItem quilt = new InstallerItem(QUILT);
|
||||
@ -131,30 +135,41 @@ public class InstallerItem extends Control {
|
||||
forge.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> {
|
||||
if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId();
|
||||
if (quilt.libraryVersion.get() != null) return QUILT.getPatchId();
|
||||
if (neoForge.libraryVersion.get() != null) return NEO_FORGE.getPatchId();
|
||||
return null;
|
||||
}, fabric.libraryVersion, quilt.libraryVersion));
|
||||
}, fabric.libraryVersion, quilt.libraryVersion, neoForge.libraryVersion));
|
||||
|
||||
neoForge.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> {
|
||||
if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId();
|
||||
if (quilt.libraryVersion.get() != null) return QUILT.getPatchId();
|
||||
if (forge.libraryVersion.get() != null) return FORGE.getPatchId();
|
||||
return null;
|
||||
}, fabric.libraryVersion, quilt.libraryVersion, forge.libraryVersion));
|
||||
|
||||
liteLoader.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> {
|
||||
if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId();
|
||||
if (quilt.libraryVersion.get() != null) return QUILT.getPatchId();
|
||||
if (neoForge.libraryVersion.get() != null) return NEO_FORGE.getPatchId();
|
||||
return null;
|
||||
}, fabric.libraryVersion, quilt.libraryVersion));
|
||||
}, fabric.libraryVersion, quilt.libraryVersion, neoForge.libraryVersion));
|
||||
|
||||
optiFine.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> {
|
||||
if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId();
|
||||
if (quilt.libraryVersion.get() != null) return QUILT.getPatchId();
|
||||
if (neoForge.libraryVersion.get() != null) return NEO_FORGE.getPatchId();
|
||||
return null;
|
||||
}, fabric.libraryVersion, quilt.libraryVersion));
|
||||
}, fabric.libraryVersion, quilt.libraryVersion, neoForge.libraryVersion));
|
||||
|
||||
for (InstallerItem fabric : new InstallerItem[]{fabric, fabricApi}) {
|
||||
fabric.incompatibleLibraryName.bind(Bindings.createStringBinding(() -> {
|
||||
if (forge.libraryVersion.get() != null) return FORGE.getPatchId();
|
||||
if (neoForge.libraryVersion.get() != null) return NEO_FORGE.getPatchId();
|
||||
if (liteLoader.libraryVersion.get() != null) return LITELOADER.getPatchId();
|
||||
if (optiFine.libraryVersion.get() != null) return OPTIFINE.getPatchId();
|
||||
if (quilt.libraryVersion.get() != null) return QUILT.getPatchId();
|
||||
if (quiltApi.libraryVersion.get() != null) return QUILT_API.getPatchId();
|
||||
return null;
|
||||
}, forge.libraryVersion, liteLoader.libraryVersion, optiFine.libraryVersion, quilt.libraryVersion, quiltApi.libraryVersion));
|
||||
}, forge.libraryVersion, neoForge.libraryVersion, liteLoader.libraryVersion, optiFine.libraryVersion, quilt.libraryVersion, quiltApi.libraryVersion));
|
||||
}
|
||||
|
||||
fabricApi.dependencyName.bind(Bindings.createStringBinding(() -> {
|
||||
@ -167,10 +182,11 @@ public class InstallerItem extends Control {
|
||||
if (fabric.libraryVersion.get() != null) return FABRIC.getPatchId();
|
||||
if (fabricApi.libraryVersion.get() != null) return FABRIC_API.getPatchId();
|
||||
if (forge.libraryVersion.get() != null) return FORGE.getPatchId();
|
||||
if (neoForge.libraryVersion.get() != null) return NEO_FORGE.getPatchId();
|
||||
if (liteLoader.libraryVersion.get() != null) return LITELOADER.getPatchId();
|
||||
if (optiFine.libraryVersion.get() != null) return OPTIFINE.getPatchId();
|
||||
return null;
|
||||
}, fabric.libraryVersion, fabricApi.libraryVersion, forge.libraryVersion, liteLoader.libraryVersion, optiFine.libraryVersion));
|
||||
}, fabric.libraryVersion, fabricApi.libraryVersion, forge.libraryVersion, neoForge.libraryVersion, liteLoader.libraryVersion, optiFine.libraryVersion));
|
||||
}
|
||||
|
||||
quiltApi.dependencyName.bind(Bindings.createStringBinding(() -> {
|
||||
@ -180,7 +196,7 @@ public class InstallerItem extends Control {
|
||||
}
|
||||
|
||||
public InstallerItem[] getLibraries() {
|
||||
return new InstallerItem[]{game, forge, liteLoader, optiFine, fabric, fabricApi, quilt, quiltApi};
|
||||
return new InstallerItem[]{game, forge, neoForge, liteLoader, optiFine, fabric, fabricApi, quilt, quiltApi};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameInstallTask;
|
||||
import org.jackhuang.hmcl.download.java.JavaDownloadTask;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||
import org.jackhuang.hmcl.download.neoforge.NeoForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIInstallTask;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltInstallTask;
|
||||
@ -123,6 +124,8 @@ public final class TaskListPane extends StackPane {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.game")));
|
||||
} else if (task instanceof ForgeNewInstallTask || task instanceof ForgeOldInstallTask) {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.forge")));
|
||||
} else if (task instanceof NeoForgeInstallTask) {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.neoforge")));
|
||||
} else if (task instanceof LiteLoaderInstallTask) {
|
||||
task.setName(i18n("install.installer.install", i18n("install.installer.liteloader")));
|
||||
} else if (task instanceof OptiFineInstallTask) {
|
||||
@ -248,6 +251,7 @@ public final class TaskListPane extends StackPane {
|
||||
case "hmcl.install.assets": message = i18n("assets.download"); break;
|
||||
case "hmcl.install.game": message = i18n("install.installer.install", i18n("install.installer.game") + " " + stageValue); break;
|
||||
case "hmcl.install.forge": message = i18n("install.installer.install", i18n("install.installer.forge") + " " + stageValue); break;
|
||||
case "hmcl.install.neoforge": message = i18n("install.installer.install", i18n("install.installer.neoforge") + " " + stageValue); break;
|
||||
case "hmcl.install.liteloader": message = i18n("install.installer.install", i18n("install.installer.liteloader") + " " + stageValue); break;
|
||||
case "hmcl.install.optifine": message = i18n("install.installer.install", i18n("install.installer.optifine") + " " + stageValue); break;
|
||||
case "hmcl.install.fabric": message = i18n("install.installer.install", i18n("install.installer.fabric") + " " + stageValue); break;
|
||||
|
@ -23,14 +23,12 @@ import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
@ -72,9 +70,9 @@ public class InstallersPage extends Control implements WizardPage {
|
||||
|
||||
for (InstallerItem library : group.getLibraries()) {
|
||||
String libraryId = library.getLibraryId();
|
||||
if (libraryId.equals("game")) continue;
|
||||
if (libraryId.equals(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())) continue;
|
||||
library.action.set(e -> {
|
||||
if ("fabric-api".equals(libraryId)) {
|
||||
if (LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId().equals(libraryId)) {
|
||||
Controllers.dialog(i18n("install.installer.fabric-api.warning"), i18n("message.warning"), MessageDialogPane.MessageType.WARNING);
|
||||
}
|
||||
|
||||
@ -155,10 +153,13 @@ public class InstallersPage extends Control implements WizardPage {
|
||||
|
||||
{
|
||||
FlowPane libraryPane = new FlowPane(control.group.getLibraries());
|
||||
BorderPane.setMargin(libraryPane, new Insets(16, 0, 16, 0));
|
||||
libraryPane.setVgap(16);
|
||||
libraryPane.setHgap(16);
|
||||
root.setCenter(libraryPane);
|
||||
ScrollPane scrollPane = new ScrollPane(libraryPane);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
BorderPane.setMargin(scrollPane, new Insets(16, 0, 16, 0));
|
||||
root.setCenter(scrollPane);
|
||||
}
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@ import org.jackhuang.hmcl.download.fabric.FabricRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.neoforge.NeoForgeRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltRemoteVersion;
|
||||
@ -335,6 +336,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
iconType = VersionIconType.COMMAND;
|
||||
else if (remoteVersion instanceof ForgeRemoteVersion)
|
||||
iconType = VersionIconType.FORGE;
|
||||
else if (remoteVersion instanceof NeoForgeRemoteVersion)
|
||||
iconType = VersionIconType.NEO_FORGE;
|
||||
else if (remoteVersion instanceof FabricRemoteVersion || remoteVersion instanceof FabricAPIRemoteVersion)
|
||||
iconType = VersionIconType.FABRIC;
|
||||
else if (remoteVersion instanceof QuiltRemoteVersion || remoteVersion instanceof QuiltAPIRemoteVersion)
|
||||
|
@ -399,6 +399,9 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
case FORGE:
|
||||
content.getTags().add(i18n("install.installer.forge"));
|
||||
break;
|
||||
case NEO_FORGED:
|
||||
content.getTags().add(i18n("install.installer.neoforge"));
|
||||
break;
|
||||
case FABRIC:
|
||||
content.getTags().add(i18n("install.installer.fabric"));
|
||||
break;
|
||||
|
@ -86,7 +86,7 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
|
||||
|
||||
InstallerItem.InstallerItemGroup group = new InstallerItem.InstallerItemGroup();
|
||||
|
||||
// Conventional libraries: game, fabric, forge, liteloader, optifine
|
||||
// Conventional libraries: game, fabric, forge, neoforge, liteloader, optifine
|
||||
for (InstallerItem installerItem : group.getLibraries()) {
|
||||
String libraryId = installerItem.getLibraryId();
|
||||
String libraryVersion = analyzer.getVersion(libraryId).orElse(null);
|
||||
@ -96,7 +96,7 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
|
||||
installerItem.action.set(e -> {
|
||||
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion));
|
||||
});
|
||||
boolean removable = !"game".equals(libraryId) && libraryVersion != null;
|
||||
boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryVersion != null;
|
||||
installerItem.removable.set(removable);
|
||||
if (removable) {
|
||||
Runnable action = removeAction.apply(libraryId);
|
||||
|
@ -335,15 +335,28 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
RemoteMod remoteMod = remoteModRepository.getModById(versionOptional.get().getModid());
|
||||
FXUtils.runInFX(() -> {
|
||||
for (ModLoaderType modLoaderType : versionOptional.get().getLoaders()) {
|
||||
String loaderName;
|
||||
switch (modLoaderType) {
|
||||
case FABRIC:
|
||||
case FORGE:
|
||||
loaderName = i18n("install.installer.forge");
|
||||
break;
|
||||
case NEO_FORGED:
|
||||
loaderName = i18n("install.installer.neoforge");
|
||||
break;
|
||||
case FABRIC:
|
||||
loaderName = i18n("install.installer.fabric");
|
||||
break;
|
||||
case LITE_LOADER:
|
||||
case QUILT: {
|
||||
if (!title.getTags().contains(modLoaderType.getLoaderName())) {
|
||||
title.getTags().add(modLoaderType.getLoaderName());
|
||||
}
|
||||
}
|
||||
loaderName = i18n("install.installer.liteloader");
|
||||
break;
|
||||
case QUILT:
|
||||
loaderName = i18n("install.installer.quilt");
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (!title.getTags().contains(loaderName)) {
|
||||
title.getTags().add(loaderName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -457,7 +470,23 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
if (empty) return;
|
||||
content.setTitle(dataItem.getTitle());
|
||||
content.getTags().clear();
|
||||
content.getTags().add(dataItem.getModInfo().getModLoaderType().getLoaderName());
|
||||
switch (dataItem.getModInfo().getModLoaderType()) {
|
||||
case FORGE:
|
||||
content.getTags().add(i18n("install.installer.forge"));
|
||||
break;
|
||||
case NEO_FORGED:
|
||||
content.getTags().add(i18n("install.installer.neoforge"));
|
||||
break;
|
||||
case FABRIC:
|
||||
content.getTags().add(i18n("install.installer.fabric"));
|
||||
break;
|
||||
case LITE_LOADER:
|
||||
content.getTags().add(i18n("install.installer.liteloader"));
|
||||
break;
|
||||
case QUILT:
|
||||
content.getTags().add(i18n("install.installer.quilt"));
|
||||
break;
|
||||
}
|
||||
if (dataItem.getMod() != null) {
|
||||
if (I18n.getCurrentLocale().getLocale() == Locale.CHINA) {
|
||||
content.getTags().add(dataItem.getMod().getDisplayName());
|
||||
|
@ -65,6 +65,7 @@ public class VersionIconDialog extends DialogPane {
|
||||
createIcon(VersionIconType.CRAFT_TABLE),
|
||||
createIcon(VersionIconType.FABRIC),
|
||||
createIcon(VersionIconType.FORGE),
|
||||
createIcon(VersionIconType.NEO_FORGE),
|
||||
createIcon(VersionIconType.FURNACE),
|
||||
createIcon(VersionIconType.QUILT)
|
||||
);
|
||||
|
BIN
HMCL/src/main/resources/assets/img/neoforge.png
Normal file
BIN
HMCL/src/main/resources/assets/img/neoforge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
HMCL/src/main/resources/assets/img/neoforged@2x.png
Normal file
BIN
HMCL/src/main/resources/assets/img/neoforged@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -617,6 +617,7 @@ install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=Warning: Fabric API is a mod, and will be installed into the mod directory of the game instance. Please do not change the working directory of the game, or the Fabric API will not work. If you do want to change these settings, you should reinstall it.
|
||||
install.installer.forge=Forge
|
||||
install.installer.neoforge=NeoForge
|
||||
install.installer.game=Minecraft
|
||||
install.installer.incompatible=Incompatible with %s
|
||||
install.installer.install=Install %s
|
||||
@ -838,6 +839,7 @@ modrinth.category.misc=Misc
|
||||
modrinth.category.mobs=Mobs
|
||||
modrinth.category.modloader=Modloader
|
||||
modrinth.category.multiplayer=Multiplayer
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optimization=Optimization
|
||||
modrinth.category.paper=Paper
|
||||
modrinth.category.purpur=Purpur
|
||||
|
@ -497,6 +497,7 @@ install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是 Mod,將會被安裝到新遊戲的 Mod 資料夾,請你在安裝遊戲後不要修改當前遊戲的版本隔離/遊戲運行路徑設置,如果你在之後修改了相關設置,Fabric API 需要被重新安裝。
|
||||
install.installer.forge=Forge
|
||||
install.installer.neoforge=NeoForge
|
||||
install.installer.game=Minecraft
|
||||
install.installer.incompatible=與 %s 不相容
|
||||
install.installer.install=安裝%s
|
||||
@ -709,6 +710,7 @@ modrinth.category.misc=其他
|
||||
modrinth.category.mobs=生物
|
||||
modrinth.category.modloader=Modloader
|
||||
modrinth.category.multiplayer=多人
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optimization=最佳化
|
||||
modrinth.category.paper=Paper
|
||||
modrinth.category.purpur=Purpur
|
||||
|
@ -496,6 +496,7 @@ install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是一个 Mod,将会被安装到新游戏的 Mod 文件夹,请你在安装游戏后不要修改当前游戏的版本隔离/游戏运行路径设置,如果你在之后修改了相关设置,Fabric API 需要被重新安装。
|
||||
install.installer.forge=Forge
|
||||
install.installer.neoforge=NeoForge
|
||||
install.installer.game=Minecraft
|
||||
install.installer.incompatible=与 %s 不兼容
|
||||
install.installer.install=安装 %s
|
||||
@ -708,6 +709,7 @@ modrinth.category.misc=其他
|
||||
modrinth.category.mobs=生物
|
||||
modrinth.category.modloader=Modloader
|
||||
modrinth.category.multiplayer=多人
|
||||
modrinth.category.neoforge=NeoForge
|
||||
modrinth.category.optimization=优化
|
||||
modrinth.category.paper=Paper
|
||||
modrinth.category.purpur=Purpur
|
||||
|
@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.fabric.FabricVersionList;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.game.GameVersionList;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.neoforge.NeoForgeBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
||||
@ -36,6 +37,7 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
private final FabricVersionList fabric;
|
||||
private final FabricAPIVersionList fabricApi;
|
||||
private final ForgeBMCLVersionList forge;
|
||||
private final NeoForgeBMCLVersionList neoforge;
|
||||
private final LiteLoaderBMCLVersionList liteLoader;
|
||||
private final OptiFineBMCLVersionList optifine;
|
||||
private final QuiltVersionList quilt;
|
||||
@ -47,6 +49,7 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
this.fabric = new FabricVersionList(this);
|
||||
this.fabricApi = new FabricAPIVersionList(this);
|
||||
this.forge = new ForgeBMCLVersionList(apiRoot);
|
||||
this.neoforge = new NeoForgeBMCLVersionList(apiRoot);
|
||||
this.liteLoader = new LiteLoaderBMCLVersionList(this);
|
||||
this.optifine = new OptiFineBMCLVersionList(apiRoot);
|
||||
this.quilt = new QuiltVersionList(this);
|
||||
@ -78,6 +81,8 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
return fabricApi;
|
||||
case "forge":
|
||||
return forge;
|
||||
case "neoforge":
|
||||
return neoforge;
|
||||
case "liteloader":
|
||||
return liteLoader;
|
||||
case "optifine":
|
||||
@ -102,6 +107,7 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
.replace("https://libraries.minecraft.net", apiRoot + "/libraries")
|
||||
.replaceFirst("https?://files\\.minecraftforge\\.net/maven", apiRoot + "/maven")
|
||||
.replace("https://maven.minecraftforge.net", apiRoot + "/maven")
|
||||
.replace("https://maven.neoforged.net/releases/net/neoforged/forge", apiRoot + "/maven/net/neoforged/forge")
|
||||
.replace("http://dl.liteloader.com/versions/versions.json", apiRoot + "/maven/com/mumfrey/liteloader/versions.json")
|
||||
.replace("http://dl.liteloader.com/versions", apiRoot + "/maven")
|
||||
.replace("https://meta.fabricmc.net", apiRoot + "/fabric-meta")
|
||||
|
@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.neoforge.NeoForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.Artifact;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
@ -179,6 +180,11 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
||||
|
||||
return Task
|
||||
.composeAsync(() -> {
|
||||
try {
|
||||
return NeoForgeInstallTask.install(this, oldVersion, installer);
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
|
||||
try {
|
||||
return ForgeInstallTask.install(this, oldVersion, installer);
|
||||
} catch (IOException ignore) {
|
||||
|
@ -17,9 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.download;
|
||||
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.VersionProvider;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.mod.ModLoaderType;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -113,7 +111,8 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
|
||||
/**
|
||||
* Remove library by library id
|
||||
* @param libraryId patch id or "forge"/"optifine"/"liteloader"/"fabric"/"quilt"
|
||||
*
|
||||
* @param libraryId patch id or "forge"/"optifine"/"liteloader"/"fabric"/"quilt"/"neoforge"
|
||||
* @return this
|
||||
*/
|
||||
public LibraryAnalyzer removeLibrary(String libraryId) {
|
||||
@ -139,7 +138,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
for (Library library : version.resolve(null).getLibraries()) {
|
||||
for (LibraryType type : LibraryType.values()) {
|
||||
if (type.matchLibrary(library)) {
|
||||
libraries.put(type.getPatchId(), pair(library, type.patchVersion(library.getVersion())));
|
||||
libraries.put(type.getPatchId(), pair(library, type.patchVersion(version, library.getVersion())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -179,14 +178,56 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$");
|
||||
|
||||
@Override
|
||||
public String patchVersion(String libraryVersion) {
|
||||
public String patchVersion(Version gameVersion, String libraryVersion) {
|
||||
Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion);
|
||||
if (matcher.find()) {
|
||||
return matcher.group("forge");
|
||||
}
|
||||
return super.patchVersion(libraryVersion);
|
||||
return super.patchVersion(gameVersion, libraryVersion);
|
||||
}
|
||||
},
|
||||
NEO_FORGE(true, "neoforge", Pattern.compile("net\\.neoforged\\.fancymodloader"), Pattern.compile("(core|loader)"), ModLoaderType.NEO_FORGED) {
|
||||
@Override
|
||||
public String patchVersion(Version gameVersion, String libraryVersion) {
|
||||
String res = scanVersion(gameVersion);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (Version patch : gameVersion.getPatches()) {
|
||||
res = scanVersion(patch);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return super.patchVersion(gameVersion, libraryVersion);
|
||||
}
|
||||
|
||||
private String scanVersion(Version version) {
|
||||
Optional<Arguments> optArgument = version.getArguments();
|
||||
if (!optArgument.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
List<Argument> gameArguments = optArgument.get().getGame();
|
||||
if (gameArguments == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < gameArguments.size() - 1; i++) {
|
||||
Argument argument = gameArguments.get(i);
|
||||
if (argument instanceof StringArgument && "--fml.neoForgeVersion".equals(((StringArgument) argument).getArgument())) {
|
||||
Argument next = gameArguments.get(i + 1);
|
||||
if (next instanceof StringArgument) {
|
||||
return ((StringArgument) next).getArgument();
|
||||
}
|
||||
return null; // Normally, there should not be two --fml.neoForgeVersion argument.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
},
|
||||
LITELOADER(true, "liteloader", Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader"), ModLoaderType.LITE_LOADER),
|
||||
OPTIFINE(false, "optifine", Pattern.compile("(net\\.)?optifine"), Pattern.compile("^(?!.*launchwrapper).*$"), null),
|
||||
QUILT(true, "quilt", Pattern.compile("org\\.quiltmc"), Pattern.compile("quilt-loader"), ModLoaderType.QUILT),
|
||||
@ -229,7 +270,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches();
|
||||
}
|
||||
|
||||
public String patchVersion(String libraryVersion) {
|
||||
public String patchVersion(Version gameVersion, String libraryVersion) {
|
||||
return libraryVersion;
|
||||
}
|
||||
}
|
||||
@ -259,14 +300,14 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
|
||||
public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher";
|
||||
public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher";
|
||||
|
||||
public static final String[] FORGE_TWEAKERS = new String[] {
|
||||
"net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2
|
||||
"cpw.mods.fml.common.launcher.FMLTweaker", // 1.6.1 ~ 1.7.10
|
||||
"net.minecraftforge.fml.common.launcher.FMLTweaker" // 1.8 ~ 1.12.2
|
||||
public static final String[] FORGE_TWEAKERS = new String[]{
|
||||
"net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2
|
||||
"cpw.mods.fml.common.launcher.FMLTweaker", // 1.6.1 ~ 1.7.10
|
||||
"net.minecraftforge.fml.common.launcher.FMLTweaker" // 1.8 ~ 1.12.2
|
||||
};
|
||||
public static final String[] OPTIFINE_TWEAKERS = new String[] {
|
||||
"optifine.OptiFineTweaker",
|
||||
"optifine.OptiFineForgeTweaker"
|
||||
public static final String[] OPTIFINE_TWEAKERS = new String[]{
|
||||
"optifine.OptiFineTweaker",
|
||||
"optifine.OptiFineForgeTweaker"
|
||||
};
|
||||
public static final String LITELOADER_TWEAKER = "com.mumfrey.liteloader.launch.LiteLoaderTweaker";
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ public class MaintainTask extends Task<Version> {
|
||||
LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version);
|
||||
VersionLibraryBuilder builder = new VersionLibraryBuilder(version);
|
||||
|
||||
if (!libraryAnalyzer.has(FORGE)) return version;
|
||||
if (!libraryAnalyzer.has(FORGE) && !libraryAnalyzer.has(NEO_FORGE)) return version;
|
||||
|
||||
Optional<String> bslVersion = libraryAnalyzer.getVersion(BOOTSTRAP_LAUNCHER);
|
||||
|
||||
|
@ -22,19 +22,21 @@ import org.jackhuang.hmcl.download.fabric.FabricVersionList;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.game.GameVersionList;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
|
||||
import org.jackhuang.hmcl.download.neoforge.NeoForgeBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltAPIVersionList;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltVersionList;
|
||||
|
||||
/**
|
||||
* @see <a href="http://wiki.vg">http://wiki.vg</a>
|
||||
* @author huangyuhui
|
||||
* @see <a href="http://wiki.vg">http://wiki.vg</a>
|
||||
*/
|
||||
public class MojangDownloadProvider implements DownloadProvider {
|
||||
private final GameVersionList game;
|
||||
private final FabricVersionList fabric;
|
||||
private final FabricAPIVersionList fabricApi;
|
||||
private final ForgeBMCLVersionList forge;
|
||||
private final NeoForgeBMCLVersionList neoforge;
|
||||
private final LiteLoaderVersionList liteLoader;
|
||||
private final OptiFineBMCLVersionList optifine;
|
||||
private final QuiltVersionList quilt;
|
||||
@ -47,6 +49,7 @@ public class MojangDownloadProvider implements DownloadProvider {
|
||||
this.fabric = new FabricVersionList(this);
|
||||
this.fabricApi = new FabricAPIVersionList(this);
|
||||
this.forge = new ForgeBMCLVersionList(apiRoot);
|
||||
this.neoforge = new NeoForgeBMCLVersionList(apiRoot);
|
||||
this.liteLoader = new LiteLoaderVersionList(this);
|
||||
this.optifine = new OptiFineBMCLVersionList(apiRoot);
|
||||
this.quilt = new QuiltVersionList(this);
|
||||
@ -74,6 +77,8 @@ public class MojangDownloadProvider implements DownloadProvider {
|
||||
return fabricApi;
|
||||
case "forge":
|
||||
return forge;
|
||||
case "neoforge":
|
||||
return neoforge;
|
||||
case "liteloader":
|
||||
return liteLoader;
|
||||
case "optifine":
|
||||
|
@ -192,7 +192,7 @@ public class ForgeNewInstallTask extends Task<Version> {
|
||||
private Path tempDir;
|
||||
private AtomicInteger processorDoneCount = new AtomicInteger(0);
|
||||
|
||||
ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
|
||||
public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameRepository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.neoforge;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
|
||||
public final class NeoForgeBMCLVersionList extends VersionList<NeoForgeRemoteVersion> {
|
||||
private final String apiRoot;
|
||||
|
||||
/**
|
||||
* @param apiRoot API Root of BMCLAPI implementations
|
||||
*/
|
||||
public NeoForgeBMCLVersionList(String apiRoot) {
|
||||
this.apiRoot = apiRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> loadAsync() {
|
||||
throw new UnsupportedOperationException("NeoForgeBMCLVersionList does not support loading the entire NeoForge remote version list.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
throw new UnsupportedOperationException("NeoForgeBMCLVersionList does not support loading the entire NeoForge remote version list.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return CompletableFuture.completedFuture((Void) null)
|
||||
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/neoforge/list/" + gameVersion).<List<NeoForgeVersion>>getJson(new TypeToken<List<NeoForgeVersion>>() {
|
||||
}.getType())))
|
||||
.thenAcceptAsync(neoForgeVersions -> {
|
||||
lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
versions.clear(gameVersion);
|
||||
for (NeoForgeVersion neoForgeVersion : neoForgeVersions) {
|
||||
String nf = StringUtils.removePrefix(
|
||||
neoForgeVersion.version,
|
||||
"1.20.1".equals(gameVersion) ? "1.20.1-forge-" : "neoforge-" // Som of the version numbers for 1.20.1 are like forge.
|
||||
);
|
||||
versions.put(gameVersion, new NeoForgeRemoteVersion(
|
||||
neoForgeVersion.mcVersion,
|
||||
nf,
|
||||
Lang.immutableListOf(
|
||||
apiRoot + "/neoforge/version/" + neoForgeVersion.version + "/download/installer.jar"
|
||||
)
|
||||
));
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NeoForgeRemoteVersion> getVersion(String gameVersion, String remoteVersion) {
|
||||
remoteVersion = StringUtils.substringAfter(remoteVersion, "-", remoteVersion);
|
||||
return super.getVersion(gameVersion, remoteVersion);
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private static final class NeoForgeVersion implements Validation {
|
||||
private final String rawVersion;
|
||||
|
||||
private final String version;
|
||||
|
||||
@SerializedName("mcversion")
|
||||
private final String mcVersion;
|
||||
|
||||
public NeoForgeVersion(String rawVersion, String version, String mcVersion) {
|
||||
this.rawVersion = rawVersion;
|
||||
this.version = version;
|
||||
this.mcVersion = mcVersion;
|
||||
}
|
||||
|
||||
public String getRawVersion() {
|
||||
return this.rawVersion;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public String getMcVersion() {
|
||||
return this.mcVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws JsonParseException {
|
||||
if (this.rawVersion == null) {
|
||||
throw new JsonParseException("NeoForgeVersion rawVersion cannot be null.");
|
||||
}
|
||||
if (this.version == null) {
|
||||
throw new JsonParseException("NeoForgeVersion version cannot be null.");
|
||||
}
|
||||
if (this.mcVersion == null) {
|
||||
throw new JsonParseException("NeoForgeVersion mcversion cannot be null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package org.jackhuang.hmcl.download.neoforge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.VersionMismatchException;
|
||||
import org.jackhuang.hmcl.download.forge.*;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
|
||||
|
||||
public final class NeoForgeInstallTask extends Task<Version> {
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
|
||||
private final Version version;
|
||||
|
||||
private final NeoForgeRemoteVersion remoteVersion;
|
||||
|
||||
private Path installer = null;
|
||||
|
||||
private FileDownloadTask dependent;
|
||||
|
||||
private Task<Version> dependency;
|
||||
|
||||
public NeoForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, NeoForgeRemoteVersion remoteVersion) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
this.remoteVersion = remoteVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPreExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
installer = Files.createTempFile("neoforge-installer", ".jar");
|
||||
|
||||
dependent = new FileDownloadTask(
|
||||
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls()),
|
||||
installer.toFile(), null
|
||||
);
|
||||
dependent.setCacheRepository(dependencyManager.getCacheRepository());
|
||||
dependent.setCaching(true);
|
||||
dependent.addIntegrityCheckHandler(FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPostExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
Files.deleteIfExists(installer);
|
||||
this.setResult(dependency.getResult());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Task<?>> getDependents() {
|
||||
return Collections.singleton(dependent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Task<?>> getDependencies() {
|
||||
return Collections.singleton(dependency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
dependency = install(dependencyManager, version, installer);
|
||||
}
|
||||
|
||||
public static Task<Version> install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException {
|
||||
Optional<String> gameVersion = dependencyManager.getGameRepository().getGameVersion(version);
|
||||
if (!gameVersion.isPresent()) throw new IOException();
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||
String installProfileText = FileUtils.readText(fs.getPath("install_profile.json"));
|
||||
Map<?, ?> installProfile = JsonUtils.fromNonNullJson(installProfileText, Map.class);
|
||||
if (LibraryAnalyzer.LibraryType.FORGE.getPatchId().equals(installProfile.get("profile")) && Files.exists(fs.getPath("META-INF/NEOFORGE.RSA"))) {
|
||||
ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getMinecraft()))
|
||||
throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get());
|
||||
return new ForgeNewInstallTask(dependencyManager, version, modifyNeoForgeOldVersion(gameVersion.get(), profile.getVersion()), installer).thenApplyAsync(neoForgeVersion -> {
|
||||
if (!neoForgeVersion.getId().equals(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) || neoForgeVersion.getVersion() == null) {
|
||||
throw new IOException("Invalid neoforge version.");
|
||||
}
|
||||
return neoForgeVersion.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()).setVersion(neoForgeVersion.getVersion().replace(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()));
|
||||
});
|
||||
} else if (LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId().equals(installProfile.get("profile"))) {
|
||||
ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class);
|
||||
if (!gameVersion.get().equals(profile.getMinecraft()))
|
||||
throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get());
|
||||
return new NeoForgeOldInstallTask(dependencyManager, version, modifyNeoForgeNewVersion(profile.getVersion()), installer);
|
||||
} else {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String modifyNeoForgeOldVersion(String gameVersion, String version) {
|
||||
return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_");
|
||||
}
|
||||
|
||||
private static String modifyNeoForgeNewVersion(String version) {
|
||||
return removePrefix(version.replace("neoforge", ""), "-");
|
||||
}
|
||||
}
|
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.download.neoforge;
|
||||
|
||||
import org.jackhuang.hmcl.download.ArtifactMalformedException;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile.Processor;
|
||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.SystemUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.gson.JsonUtils.fromNonNullJson;
|
||||
|
||||
public class NeoForgeOldInstallTask extends Task<Version> {
|
||||
|
||||
private class ProcessorTask extends Task<Void> {
|
||||
|
||||
private final Processor processor;
|
||||
private final Map<String, String> vars;
|
||||
|
||||
public ProcessorTask(@NotNull Processor processor, @NotNull Map<String, String> vars) {
|
||||
this.processor = processor;
|
||||
this.vars = vars;
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
Map<String, String> outputs = new HashMap<>();
|
||||
boolean miss = false;
|
||||
|
||||
for (Map.Entry<String, String> entry : processor.getOutputs().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
key = parseLiteral(key, vars);
|
||||
value = parseLiteral(value, vars);
|
||||
|
||||
if (key == null || value == null) {
|
||||
throw new ArtifactMalformedException("Invalid forge installation configuration");
|
||||
}
|
||||
|
||||
outputs.put(key, value);
|
||||
|
||||
Path artifact = Paths.get(key);
|
||||
if (Files.exists(artifact)) {
|
||||
String code;
|
||||
try (InputStream stream = Files.newInputStream(artifact)) {
|
||||
code = (DigestUtils.digestToString("SHA-1", stream));
|
||||
}
|
||||
|
||||
if (!Objects.equals(code, value)) {
|
||||
Files.delete(artifact);
|
||||
LOG.info("Found existing file is not valid: " + artifact);
|
||||
|
||||
miss = true;
|
||||
}
|
||||
} else {
|
||||
miss = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!processor.getOutputs().isEmpty() && !miss) {
|
||||
return;
|
||||
}
|
||||
|
||||
Path jar = gameRepository.getArtifactFile(version, processor.getJar());
|
||||
if (!Files.isRegularFile(jar))
|
||||
throw new FileNotFoundException("Game processor file not found, should be downloaded in preprocess");
|
||||
|
||||
String mainClass;
|
||||
try (JarFile jarFile = new JarFile(jar.toFile())) {
|
||||
mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(mainClass))
|
||||
throw new Exception("Game processor jar does not have main class " + jar);
|
||||
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(JavaVersion.fromCurrentEnvironment().getBinary().toString());
|
||||
command.add("-cp");
|
||||
|
||||
List<String> classpath = new ArrayList<>(processor.getClasspath().size() + 1);
|
||||
for (Artifact artifact : processor.getClasspath()) {
|
||||
Path file = gameRepository.getArtifactFile(version, artifact);
|
||||
if (!Files.isRegularFile(file))
|
||||
throw new Exception("Game processor dependency missing");
|
||||
classpath.add(file.toString());
|
||||
}
|
||||
classpath.add(jar.toString());
|
||||
command.add(String.join(OperatingSystem.PATH_SEPARATOR, classpath));
|
||||
|
||||
command.add(mainClass);
|
||||
|
||||
List<String> args = new ArrayList<>(processor.getArgs().size());
|
||||
for (String arg : processor.getArgs()) {
|
||||
String parsed = parseLiteral(arg, vars);
|
||||
if (parsed == null)
|
||||
throw new ArtifactMalformedException("Invalid forge installation configuration");
|
||||
args.add(parsed);
|
||||
}
|
||||
|
||||
command.addAll(args);
|
||||
|
||||
LOG.info("Executing external processor " + processor.getJar().toString() + ", command line: " + new CommandBuilder().addAll(command).toString());
|
||||
int exitCode = SystemUtils.callExternalProcess(command);
|
||||
if (exitCode != 0)
|
||||
throw new IOException("Game processor exited abnormally with code " + exitCode);
|
||||
|
||||
for (Map.Entry<String, String> entry : outputs.entrySet()) {
|
||||
Path artifact = Paths.get(entry.getKey());
|
||||
if (!Files.isRegularFile(artifact))
|
||||
throw new FileNotFoundException("File missing: " + artifact);
|
||||
|
||||
String code;
|
||||
try (InputStream stream = Files.newInputStream(artifact)) {
|
||||
code = DigestUtils.digestToString("SHA-1", stream);
|
||||
}
|
||||
|
||||
if (!Objects.equals(code, entry.getValue())) {
|
||||
Files.delete(artifact);
|
||||
throw new ChecksumMismatchException("SHA-1", entry.getValue(), code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final DefaultDependencyManager dependencyManager;
|
||||
private final DefaultGameRepository gameRepository;
|
||||
private final Version version;
|
||||
private final Path installer;
|
||||
private final List<Task<?>> dependents = new ArrayList<>(1);
|
||||
private final List<Task<?>> dependencies = new ArrayList<>(1);
|
||||
|
||||
private ForgeNewInstallProfile profile;
|
||||
private List<Processor> processors;
|
||||
private Version neoForgeVersion;
|
||||
private final String selfVersion;
|
||||
|
||||
private Path tempDir;
|
||||
private AtomicInteger processorDoneCount = new AtomicInteger(0);
|
||||
|
||||
NeoForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.gameRepository = dependencyManager.getGameRepository();
|
||||
this.version = version;
|
||||
this.installer = installer;
|
||||
this.selfVersion = selfVersion;
|
||||
|
||||
setSignificance(TaskSignificance.MAJOR);
|
||||
}
|
||||
|
||||
private static String replaceTokens(Map<String, String> tokens, String value) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int x = 0; x < value.length(); x++) {
|
||||
char c = value.charAt(x);
|
||||
if (c == '\\') {
|
||||
if (x == value.length() - 1)
|
||||
throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value);
|
||||
buf.append(value.charAt(++x));
|
||||
} else if (c == '{' || c == '\'') {
|
||||
StringBuilder key = new StringBuilder();
|
||||
for (int y = x + 1; y <= value.length(); y++) {
|
||||
if (y == value.length())
|
||||
throw new IllegalArgumentException("Illegal pattern (Unclosed " + c + "): " + value);
|
||||
char d = value.charAt(y);
|
||||
if (d == '\\') {
|
||||
if (y == value.length() - 1)
|
||||
throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value);
|
||||
key.append(value.charAt(++y));
|
||||
} else {
|
||||
if (c == '{' && d == '}') {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
if (c == '\'' && d == '\'') {
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
key.append(d);
|
||||
}
|
||||
}
|
||||
if (c == '\'') {
|
||||
buf.append(key);
|
||||
} else {
|
||||
if (!tokens.containsKey(key.toString()))
|
||||
throw new IllegalArgumentException("Illegal pattern: " + value + " Missing Key: " + key);
|
||||
buf.append(tokens.get(key.toString()));
|
||||
}
|
||||
} else {
|
||||
buf.append(c);
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private <E extends Exception> String parseLiteral(String literal, Map<String, String> var, ExceptionalFunction<String, String, E> plainConverter) throws E {
|
||||
if (StringUtils.isSurrounded(literal, "{", "}"))
|
||||
return var.get(StringUtils.removeSurrounding(literal, "{", "}"));
|
||||
else if (StringUtils.isSurrounded(literal, "'", "'"))
|
||||
return StringUtils.removeSurrounding(literal, "'");
|
||||
else if (StringUtils.isSurrounded(literal, "[", "]"))
|
||||
return gameRepository.getArtifactFile(version, Artifact.fromDescriptor(StringUtils.removeSurrounding(literal, "[", "]"))).toString();
|
||||
else
|
||||
return plainConverter.apply(replaceTokens(var, literal));
|
||||
}
|
||||
|
||||
private String parseLiteral(String literal, Map<String, String> var) {
|
||||
return parseLiteral(literal, var, ExceptionalFunction.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependents() {
|
||||
return dependents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task<?>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPreExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preExecute() throws Exception {
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||
profile = JsonUtils.fromNonNullJson(FileUtils.readText(fs.getPath("install_profile.json")), ForgeNewInstallProfile.class);
|
||||
processors = profile.getProcessors();
|
||||
neoForgeVersion = JsonUtils.fromNonNullJson(FileUtils.readText(fs.getPath(profile.getJson())), Version.class);
|
||||
|
||||
for (Library library : profile.getLibraries()) {
|
||||
Path file = fs.getPath("maven").resolve(library.getPath());
|
||||
if (Files.exists(file)) {
|
||||
Path dest = gameRepository.getLibraryFile(version, library).toPath();
|
||||
FileUtils.copyFile(file, dest);
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.getPath().isPresent()) {
|
||||
Path mainJar = profile.getPath().get().getPath(fs.getPath("maven"));
|
||||
if (Files.exists(mainJar)) {
|
||||
Path dest = gameRepository.getArtifactFile(version, profile.getPath().get());
|
||||
FileUtils.copyFile(mainJar, dest);
|
||||
}
|
||||
}
|
||||
} catch (ZipException ex) {
|
||||
throw new ArtifactMalformedException("Malformed forge installer file", ex);
|
||||
}
|
||||
|
||||
dependents.add(new GameLibrariesTask(dependencyManager, version, true, profile.getLibraries()));
|
||||
}
|
||||
|
||||
private Map<String, String> parseOptions(List<String> args, Map<String, String> vars) {
|
||||
Map<String, String> options = new LinkedHashMap<>();
|
||||
String optionName = null;
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("--")) {
|
||||
if (optionName != null) {
|
||||
options.put(optionName, "");
|
||||
}
|
||||
optionName = arg.substring(2);
|
||||
} else {
|
||||
if (optionName == null) {
|
||||
// ignore
|
||||
} else {
|
||||
options.put(optionName, parseLiteral(arg, vars));
|
||||
optionName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (optionName != null) {
|
||||
options.put(optionName, "");
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
private Task<?> patchDownloadMojangMappingsTask(Processor processor, Map<String, String> vars) {
|
||||
Map<String, String> options = parseOptions(processor.getArgs(), vars);
|
||||
if (!"DOWNLOAD_MOJMAPS".equals(options.get("task")) || !"client".equals(options.get("side")))
|
||||
return null;
|
||||
String version = options.get("version");
|
||||
String output = options.get("output");
|
||||
if (version == null || output == null)
|
||||
return null;
|
||||
|
||||
LOG.info("Patching DOWNLOAD_MOJMAPS task");
|
||||
return new VersionJsonDownloadTask(version, dependencyManager)
|
||||
.thenComposeAsync(json -> {
|
||||
DownloadInfo mappings = fromNonNullJson(json, Version.class)
|
||||
.getDownloads().get(DownloadType.CLIENT_MAPPINGS);
|
||||
if (mappings == null) {
|
||||
throw new Exception("client_mappings download info not found");
|
||||
}
|
||||
|
||||
List<URL> mappingsUrl = dependencyManager.getDownloadProvider()
|
||||
.injectURLWithCandidates(mappings.getUrl());
|
||||
FileDownloadTask mappingsTask = new FileDownloadTask(
|
||||
mappingsUrl,
|
||||
new File(output),
|
||||
IntegrityCheck.of("SHA-1", mappings.getSha1()));
|
||||
mappingsTask.setCaching(true);
|
||||
mappingsTask.setCacheRepository(dependencyManager.getCacheRepository());
|
||||
return mappingsTask;
|
||||
});
|
||||
}
|
||||
|
||||
private Task<?> createProcessorTask(Processor processor, Map<String, String> vars) {
|
||||
Task<?> task = patchDownloadMojangMappingsTask(processor, vars);
|
||||
if (task == null) {
|
||||
task = new ProcessorTask(processor, vars);
|
||||
}
|
||||
task.onDone().register(
|
||||
() -> updateProgress(processorDoneCount.incrementAndGet(), processors.size()));
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
tempDir = Files.createTempDirectory("neoforge_installer");
|
||||
|
||||
Map<String, String> vars = new HashMap<>();
|
||||
|
||||
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
|
||||
for (Map.Entry<String, String> entry : profile.getData().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
vars.put(key, parseLiteral(value,
|
||||
Collections.emptyMap(),
|
||||
str -> {
|
||||
Path dest = Files.createTempFile(tempDir, null, null);
|
||||
FileUtils.copyFile(fs.getPath(str), dest);
|
||||
return dest.toString();
|
||||
}));
|
||||
}
|
||||
} catch (ZipException ex) {
|
||||
throw new ArtifactMalformedException("Malformed neoforge installer file", ex);
|
||||
}
|
||||
|
||||
vars.put("SIDE", "client");
|
||||
vars.put("MINECRAFT_JAR", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("MINECRAFT_VERSION", gameRepository.getVersionJar(version).getAbsolutePath());
|
||||
vars.put("ROOT", gameRepository.getBaseDirectory().getAbsolutePath());
|
||||
vars.put("INSTALLER", installer.toAbsolutePath().toString());
|
||||
vars.put("LIBRARY_DIR", gameRepository.getLibrariesDirectory(version).getAbsolutePath());
|
||||
|
||||
updateProgress(0, processors.size());
|
||||
|
||||
Task<?> processorsTask = Task.runSequentially(
|
||||
processors.stream()
|
||||
.map(processor -> createProcessorTask(processor, vars))
|
||||
.toArray(Task<?>[]::new));
|
||||
|
||||
dependencies.add(
|
||||
processorsTask.thenComposeAsync(
|
||||
dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true)));
|
||||
|
||||
setResult(neoForgeVersion
|
||||
.setPriority(30000)
|
||||
.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId())
|
||||
.setVersion(selfVersion));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPostExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecute() throws Exception {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.jackhuang.hmcl.download.neoforge;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NeoForgeRemoteVersion extends RemoteVersion {
|
||||
public NeoForgeRemoteVersion(String gameVersion, String selfVersion, List<String> urls) {
|
||||
super(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId(), gameVersion, selfVersion, null, urls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<Version> getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) {
|
||||
return new NeoForgeInstallTask(dependencyManager, baseVersion, this);
|
||||
}
|
||||
}
|
@ -145,7 +145,6 @@ public enum JavaVersionConstraint {
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null || javaVersion == null || analyzer == null) return false;
|
||||
VersionNumber forgePatchVersion = analyzer.getVersion(LibraryAnalyzer.LibraryType.FORGE)
|
||||
.map(LibraryAnalyzer.LibraryType.FORGE::patchVersion)
|
||||
.map(VersionNumber::asVersion)
|
||||
.orElse(null);
|
||||
if (forgePatchVersion == null) {
|
||||
|
@ -18,20 +18,11 @@
|
||||
package org.jackhuang.hmcl.mod;
|
||||
|
||||
public enum ModLoaderType {
|
||||
UNKNOWN("Unknown"),
|
||||
FORGE("Forge"),
|
||||
FABRIC("Fabric"),
|
||||
QUILT("Quilt"),
|
||||
LITE_LOADER("LiteLoader"),
|
||||
PACK("Pack");
|
||||
|
||||
private final String loaderName;
|
||||
|
||||
ModLoaderType(String loaderName) {
|
||||
this.loaderName = loaderName;
|
||||
}
|
||||
|
||||
public final String getLoaderName() {
|
||||
return loaderName;
|
||||
}
|
||||
UNKNOWN,
|
||||
FORGE,
|
||||
NEO_FORGED,
|
||||
FABRIC,
|
||||
QUILT,
|
||||
LITE_LOADER,
|
||||
PACK;
|
||||
}
|
||||
|
@ -588,6 +588,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
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_FORGED);
|
||||
else return Stream.empty();
|
||||
}).collect(Collectors.toList())
|
||||
);
|
||||
|
@ -57,9 +57,9 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
* Constructor.
|
||||
*
|
||||
* @param dependencyManager the dependency manager.
|
||||
* @param zipFile the CurseForge modpack file.
|
||||
* @param manifest The manifest content of given CurseForge modpack.
|
||||
* @param name the new version name
|
||||
* @param zipFile the CurseForge modpack file.
|
||||
* @param manifest The manifest content of given CurseForge modpack.
|
||||
* @param name the new version name
|
||||
* @see CurseManifest#readCurseForgeModpackManifest
|
||||
*/
|
||||
public CurseInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, CurseManifest manifest, String name) {
|
||||
@ -81,6 +81,8 @@ public final class CurseInstallTask extends Task<Void> {
|
||||
builder.version("forge", modLoader.getId().substring("forge-".length()));
|
||||
} else if (modLoader.getId().startsWith("fabric-")) {
|
||||
builder.version("fabric", modLoader.getId().substring("fabric-".length()));
|
||||
} else if (modLoader.getId().startsWith("neoforge-")) {
|
||||
builder.version("neoforge", modLoader.getId().substring("neoforge-".length()));
|
||||
}
|
||||
}
|
||||
dependents.add(builder.buildAsync());
|
||||
|
@ -513,7 +513,9 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
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_FORGED);
|
||||
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())
|
||||
));
|
||||
|
Loading…
x
Reference in New Issue
Block a user