Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b3312ebb | ||
|
|
f351d1d133 | ||
|
|
5ca1120223 | ||
|
|
6aa4019221 | ||
|
|
73fb8bda10 | ||
|
|
cc16f84992 | ||
|
|
b4945a150e | ||
|
|
8bcbe74d82 | ||
|
|
69c2429e92 | ||
|
|
88e83dc44a | ||
|
|
87c2126e5a | ||
|
|
abf1ec3eb8 | ||
|
|
139fb41149 | ||
|
|
6c986921d6 | ||
|
|
b8e6d6fc96 | ||
|
|
75e4aee8aa | ||
|
|
abda9f50e1 | ||
|
|
c33ef5170b | ||
|
|
a3cb871928 | ||
|
|
62e965c6fe | ||
|
|
99a031cea4 | ||
|
|
f97b9b9382 |
No files matched your search
+13
-3
@@ -190,9 +190,9 @@ tasks.build {
|
||||
dependsOn(makeExecutables)
|
||||
}
|
||||
|
||||
fun parseToolOptions(options: String?): List<String> {
|
||||
fun parseToolOptions(options: String?): MutableList<String> {
|
||||
if (options == null)
|
||||
return listOf()
|
||||
return mutableListOf()
|
||||
|
||||
val builder = StringBuilder()
|
||||
val result = mutableListOf<String>()
|
||||
@@ -249,10 +249,20 @@ tasks.create<JavaExec>("run") {
|
||||
workingDir = rootProject.rootDir
|
||||
|
||||
val vmOptions = parseToolOptions(System.getenv("HMCL_JAVA_OPTS"))
|
||||
if (vmOptions.none { it.startsWith("-Dhmcl.offline.auth.restricted=") })
|
||||
vmOptions += "-Dhmcl.offline.auth.restricted=false"
|
||||
|
||||
jvmArgs(vmOptions)
|
||||
|
||||
val hmclJavaHome = System.getenv("HMCL_JAVA_HOME")
|
||||
if (hmclJavaHome != null) {
|
||||
this.executable(file(hmclJavaHome).resolve("bin")
|
||||
.resolve(if (System.getProperty("os.name").lowercase().startsWith("windows")) "java.exe" else "java"))
|
||||
}
|
||||
|
||||
doFirst {
|
||||
logger.quiet("HMCL_JAVA_OPTS: $vmOptions")
|
||||
logger.quiet("HMCL_JAVA_OPTS: {}", vmOptions)
|
||||
logger.quiet("HMCL_JAVA_HOME: {}", hmclJavaHome ?: System.getProperty("java.home"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -509,16 +509,16 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
public static long getAllocatedMemory(long minimum, long available, boolean auto) {
|
||||
if (auto) {
|
||||
available -= 384 * 1024 * 1024; // Reserve 384MiB memory for off-heap memory and HMCL itself
|
||||
available -= 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself
|
||||
if (available <= 0) {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
final long threshold = 8L * 1024 * 1024 * 1024;
|
||||
final long threshold = 8L * 1024 * 1024 * 1024; // 8 GiB
|
||||
final long suggested = Math.min(available <= threshold
|
||||
? (long) (available * 0.8)
|
||||
: (long) (threshold * 0.8 + (available - threshold) * 0.2),
|
||||
16384L * 1024 * 1024);
|
||||
16L * 1024 * 1024 * 1024);
|
||||
return Math.max(minimum, suggested);
|
||||
} else {
|
||||
return minimum;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -64,7 +64,7 @@ public final class HMCLModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile file, Path path, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader file, Path path, Charset encoding) throws IOException, JsonParseException {
|
||||
String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json");
|
||||
Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, HMCLModpack.class).setEncoding(encoding);
|
||||
String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json");
|
||||
|
||||
@@ -894,9 +894,6 @@ public final class LauncherHelper {
|
||||
|
||||
}
|
||||
|
||||
private static final String ORACLEJDK_DOWNLOAD_LINK = "https://www.java.com/download/";
|
||||
private static final String OPENJDK_DOWNLOAD_LINK = "https://learn.microsoft.com/java/openjdk/download";
|
||||
|
||||
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public static void stopManagedProcesses() {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseModpackProvider;
|
||||
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||
@@ -83,7 +83,7 @@ public final class ModpackHelper {
|
||||
}
|
||||
|
||||
public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException, ManuallyCreatedModpackException {
|
||||
try (ZipFile zipFile = CompressingUtils.openZipFile(file, charset)) {
|
||||
try (ZipArchiveReader zipFile = CompressingUtils.openZipFile(file, charset)) {
|
||||
// Order for trying detecting manifest is necessary here.
|
||||
// Do not change to iterating providers.
|
||||
for (ModpackProvider provider : new ModpackProvider[]{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.java;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.Hex;
|
||||
|
||||
@@ -22,6 +22,7 @@ import javafx.beans.Observable;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
@@ -50,6 +51,7 @@ import java.util.*;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static javafx.collections.FXCollections.observableArrayList;
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
|
||||
import static org.jackhuang.hmcl.util.Lang.immutableListOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -277,6 +279,30 @@ public final class Accounts {
|
||||
selected = accounts.get(0);
|
||||
}
|
||||
|
||||
if (!globalConfig().isEnableOfflineAccount())
|
||||
for (Account account : accounts) {
|
||||
if (account instanceof MicrosoftAccount) {
|
||||
globalConfig().setEnableOfflineAccount(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!globalConfig().isEnableOfflineAccount())
|
||||
accounts.addListener(new ListChangeListener<Account>() {
|
||||
@Override
|
||||
public void onChanged(Change<? extends Account> change) {
|
||||
while (change.next()) {
|
||||
for (Account account : change.getAddedSubList()) {
|
||||
if (account instanceof MicrosoftAccount) {
|
||||
accounts.removeListener(this);
|
||||
globalConfig().setEnableOfflineAccount(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
selectedAccount.set(selected);
|
||||
|
||||
InvalidationListener listener = o -> {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.setting;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.ToNumberPolicy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
@@ -60,6 +61,7 @@ public final class Config implements Observable {
|
||||
.registerTypeAdapter(EnumBackgroundImage.class, new EnumOrdinalDeserializer<>(EnumBackgroundImage.class)) // backward compatibility for backgroundType
|
||||
.registerTypeAdapter(Proxy.Type.class, new EnumOrdinalDeserializer<>(Proxy.Type.class)) // backward compatibility for hasProxy
|
||||
.setPrettyPrinting()
|
||||
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
|
||||
.create();
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
@@ -53,6 +55,8 @@ public final class GlobalConfig implements Observable {
|
||||
|
||||
private final IntegerProperty logRetention = new SimpleIntegerProperty();
|
||||
|
||||
private final BooleanProperty enableOfflineAccount = new SimpleBooleanProperty(false);
|
||||
|
||||
private final ObservableSet<String> userJava = FXCollections.observableSet(new LinkedHashSet<>());
|
||||
|
||||
private final ObservableSet<String> disabledJava = FXCollections.observableSet(new LinkedHashSet<>());
|
||||
@@ -115,6 +119,18 @@ public final class GlobalConfig implements Observable {
|
||||
this.logRetention.set(logRetention);
|
||||
}
|
||||
|
||||
public boolean isEnableOfflineAccount() {
|
||||
return enableOfflineAccount.get();
|
||||
}
|
||||
|
||||
public BooleanProperty enableOfflineAccountProperty() {
|
||||
return enableOfflineAccount;
|
||||
}
|
||||
|
||||
public void setEnableOfflineAccount(boolean value) {
|
||||
enableOfflineAccount.set(value);
|
||||
}
|
||||
|
||||
public ObservableSet<String> getUserJava() {
|
||||
return userJava;
|
||||
}
|
||||
@@ -129,7 +145,8 @@ public final class GlobalConfig implements Observable {
|
||||
"platformPromptVersion",
|
||||
"logRetention",
|
||||
"userJava",
|
||||
"disabledJava"
|
||||
"disabledJava",
|
||||
"enableOfflineAccount"
|
||||
));
|
||||
|
||||
@Override
|
||||
@@ -142,6 +159,9 @@ public final class GlobalConfig implements Observable {
|
||||
jsonObject.add("agreementVersion", context.serialize(src.getAgreementVersion()));
|
||||
jsonObject.add("platformPromptVersion", context.serialize(src.getPlatformPromptVersion()));
|
||||
jsonObject.add("logRetention", context.serialize(src.getLogRetention()));
|
||||
if (src.enableOfflineAccount.get())
|
||||
jsonObject.addProperty("enableOfflineAccount", true);
|
||||
|
||||
if (!src.getUserJava().isEmpty())
|
||||
jsonObject.add("userJava", context.serialize(src.getUserJava()));
|
||||
|
||||
@@ -165,6 +185,7 @@ public final class GlobalConfig implements Observable {
|
||||
config.setAgreementVersion(Optional.ofNullable(obj.get("agreementVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setPlatformPromptVersion(Optional.ofNullable(obj.get("platformPromptVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setLogRetention(Optional.ofNullable(obj.get("logRetention")).map(JsonElement::getAsInt).orElse(20));
|
||||
config.setEnableOfflineAccount(Optional.ofNullable(obj.get("enableOfflineAccount")).map(JsonElement::getAsBoolean).orElse(false));
|
||||
|
||||
JsonElement userJava = obj.get("userJava");
|
||||
if (userJava != null && userJava.isJsonArray()) {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.account;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -44,14 +45,37 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.createSelectedItemPropertyFor;
|
||||
|
||||
public class AccountListPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
public final class AccountListPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
static final BooleanProperty RESTRICTED = new SimpleBooleanProperty(true);
|
||||
|
||||
static {
|
||||
String property = System.getProperty("hmcl.offline.auth.restricted", "auto");
|
||||
|
||||
if ("false".equals(property)
|
||||
|| "auto".equals(property) && "Asia/Shanghai".equals(ZoneId.systemDefault().getId())
|
||||
|| globalConfig().isEnableOfflineAccount())
|
||||
RESTRICTED.set(false);
|
||||
else
|
||||
globalConfig().enableOfflineAccountProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> o, Boolean oldValue, Boolean newValue) {
|
||||
if (newValue) {
|
||||
globalConfig().enableOfflineAccountProperty().removeListener(this);
|
||||
RESTRICTED.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final ObservableList<AccountListItem> items;
|
||||
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("account.manage")));
|
||||
private final ListProperty<Account> accounts = new SimpleListProperty<>(this, "accounts", FXCollections.observableArrayList());
|
||||
@@ -88,6 +112,7 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
private static class AccountListPageSkin extends DecoratorAnimatedPageSkin<AccountListPage> {
|
||||
|
||||
private final ObservableList<AdvancedListItem> authServerItems;
|
||||
private ChangeListener<Boolean> holder;
|
||||
|
||||
public AccountListPageSkin(AccountListPage skinnable) {
|
||||
super(skinnable);
|
||||
@@ -96,24 +121,21 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
VBox boxMethods = new VBox();
|
||||
{
|
||||
boxMethods.getStyleClass().add("advanced-list-box-content");
|
||||
boxMethods.getChildren().add(new ClassTitle(i18n("account.create").toUpperCase(Locale.ROOT)));
|
||||
FXUtils.setLimitWidth(boxMethods, 200);
|
||||
|
||||
AdvancedListItem offlineItem = new AdvancedListItem();
|
||||
offlineItem.getStyleClass().add("navigation-drawer-item");
|
||||
offlineItem.setActionButtonVisible(false);
|
||||
offlineItem.setTitle(i18n("account.methods.offline"));
|
||||
offlineItem.setLeftGraphic(wrap(SVG.PERSON));
|
||||
offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE)));
|
||||
boxMethods.getChildren().add(offlineItem);
|
||||
|
||||
AdvancedListItem microsoftItem = new AdvancedListItem();
|
||||
microsoftItem.getStyleClass().add("navigation-drawer-item");
|
||||
microsoftItem.setActionButtonVisible(false);
|
||||
microsoftItem.setTitle(i18n("account.methods.microsoft"));
|
||||
microsoftItem.setLeftGraphic(wrap(SVG.MICROSOFT));
|
||||
microsoftItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT)));
|
||||
boxMethods.getChildren().add(microsoftItem);
|
||||
|
||||
AdvancedListItem offlineItem = new AdvancedListItem();
|
||||
offlineItem.getStyleClass().add("navigation-drawer-item");
|
||||
offlineItem.setActionButtonVisible(false);
|
||||
offlineItem.setTitle(i18n("account.methods.offline"));
|
||||
offlineItem.setLeftGraphic(wrap(SVG.PERSON));
|
||||
offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE)));
|
||||
|
||||
VBox boxAuthServers = new VBox();
|
||||
authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> {
|
||||
@@ -149,7 +171,29 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
return item;
|
||||
});
|
||||
Bindings.bindContent(boxAuthServers.getChildren(), authServerItems);
|
||||
boxMethods.getChildren().add(boxAuthServers);
|
||||
|
||||
ClassTitle title = new ClassTitle(i18n("account.create").toUpperCase(Locale.ROOT));
|
||||
if (RESTRICTED.get()) {
|
||||
VBox wrapper = new VBox(offlineItem, boxAuthServers);
|
||||
wrapper.setPadding(Insets.EMPTY);
|
||||
FXUtils.installFastTooltip(wrapper, i18n("account.login.restricted"));
|
||||
|
||||
offlineItem.setDisable(true);
|
||||
boxAuthServers.setDisable(true);
|
||||
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, wrapper);
|
||||
|
||||
holder = FXUtils.onWeakChange(RESTRICTED, value -> {
|
||||
if (!value) {
|
||||
holder = null;
|
||||
offlineItem.setDisable(false);
|
||||
boxAuthServers.setDisable(false);
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, offlineItem, boxAuthServers);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, offlineItem, boxAuthServers);
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedListItem addAuthServerItem = new AdvancedListItem();
|
||||
|
||||
@@ -108,12 +108,17 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
|
||||
public CreateAccountPane(AccountFactory<?> factory) {
|
||||
if (factory == null) {
|
||||
showMethodSwitcher = true;
|
||||
String preferred = config().getPreferredLoginType();
|
||||
try {
|
||||
factory = Accounts.getAccountFactory(preferred);
|
||||
} catch (IllegalArgumentException e) {
|
||||
factory = Accounts.FACTORY_OFFLINE;
|
||||
if (AccountListPage.RESTRICTED.get()) {
|
||||
showMethodSwitcher = false;
|
||||
factory = Accounts.FACTORY_MICROSOFT;
|
||||
} else {
|
||||
showMethodSwitcher = true;
|
||||
String preferred = config().getPreferredLoginType();
|
||||
try {
|
||||
factory = Accounts.getAccountFactory(preferred);
|
||||
} catch (IllegalArgumentException e) {
|
||||
factory = Accounts.FACTORY_OFFLINE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showMethodSwitcher = false;
|
||||
|
||||
@@ -203,7 +203,11 @@ public class DecoratorController {
|
||||
case CUSTOM:
|
||||
String backgroundImage = config().getBackgroundImage();
|
||||
if (backgroundImage != null)
|
||||
image = tryLoadImage(Paths.get(backgroundImage));
|
||||
try {
|
||||
image = tryLoadImage(Paths.get(backgroundImage));
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Couldn't load background image", e);
|
||||
}
|
||||
break;
|
||||
case NETWORK:
|
||||
String backgroundImageUrl = config().getBackgroundImageUrl();
|
||||
|
||||
@@ -130,7 +130,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
};
|
||||
}
|
||||
|
||||
private static void download(Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) {
|
||||
public static void download(Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) {
|
||||
if (version == null) version = profile.getSelectedVersion();
|
||||
|
||||
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version).toPath() : profile.getRepository().getBaseDirectory().toPath();
|
||||
|
||||
@@ -43,12 +43,15 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
|
||||
|
||||
public ModpackTypeSelectionPage(WizardController controller) {
|
||||
this.controller = controller;
|
||||
this.setPadding(new Insets(10));
|
||||
|
||||
Label title = new Label(i18n("modpack.export.as"));
|
||||
title.setPadding(new Insets(8));
|
||||
VBox.setMargin(title, new Insets(8, 0, 8, 12));
|
||||
|
||||
this.getStyleClass().add("jfx-list-view");
|
||||
this.setMaxSize(300, 150);
|
||||
this.setMaxSize(400, 150);
|
||||
this.setSpacing(8);
|
||||
|
||||
this.getChildren().setAll(
|
||||
title,
|
||||
createButton(MODPACK_TYPE_MCBBS, McbbsModpackExportTask.OPTION),
|
||||
@@ -59,6 +62,8 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
|
||||
|
||||
private JFXButton createButton(String type, ModpackExportInfo.Options option) {
|
||||
JFXButton button = new JFXButton();
|
||||
|
||||
button.getStyleClass().add("card");
|
||||
button.setOnAction(e -> {
|
||||
controller.getSettings().put(MODPACK_TYPE, type);
|
||||
controller.getSettings().put(MODPACK_INFO_OPTION, option);
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.apache.commons.compress.utils.BoundedInputStream;
|
||||
import kala.compress.utils.BoundedInputStream;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -60,10 +60,7 @@ import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
|
||||
@@ -156,7 +153,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
FXUtils.runInFX(() -> selectedVersion.set(versionID));
|
||||
}
|
||||
|
||||
public void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) {
|
||||
private void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) {
|
||||
retrySearch = null;
|
||||
setLoading(true);
|
||||
setFailed(false);
|
||||
@@ -171,7 +168,9 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
? version.getProfile().getRepository().getGameVersion(version.getVersion()).orElse("")
|
||||
: "";
|
||||
}
|
||||
}).thenApplyAsync(gameVersion -> repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC)).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
}).thenApplyAsync(
|
||||
gameVersion -> repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC)
|
||||
).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (searchID != currentSearchID) {
|
||||
return;
|
||||
}
|
||||
@@ -312,7 +311,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
|
||||
StackPane categoryStackPane = new StackPane();
|
||||
JFXComboBox<CategoryIndented> categoryComboBox = new JFXComboBox<>();
|
||||
categoryComboBox.getItems().setAll(new CategoryIndented(0, null));
|
||||
categoryComboBox.getItems().setAll(CategoryIndented.ALL);
|
||||
categoryStackPane.getChildren().setAll(categoryComboBox);
|
||||
categoryComboBox.prefWidthProperty().bind(categoryStackPane.widthProperty());
|
||||
categoryComboBox.getStyleClass().add("fit-width");
|
||||
@@ -320,14 +319,22 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
categoryComboBox.setConverter(stringConverter(getSkinnable()::getLocalizedCategoryIndent));
|
||||
FXUtils.onChangeAndOperate(getSkinnable().downloadSource, downloadSource -> {
|
||||
categoryComboBox.getItems().setAll(CategoryIndented.ALL);
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
|
||||
Task.supplyAsync(() -> getSkinnable().repository.getCategories())
|
||||
.thenAcceptAsync(Schedulers.javafx(), categories -> {
|
||||
if (!Objects.equals(getSkinnable().downloadSource.get(), downloadSource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CategoryIndented> result = new ArrayList<>();
|
||||
result.add(new CategoryIndented(0, null));
|
||||
result.add(CategoryIndented.ALL);
|
||||
for (RemoteModRepository.Category category : Lang.toIterable(categories)) {
|
||||
resolveCategory(category, 0, result);
|
||||
}
|
||||
categoryComboBox.getItems().setAll(result);
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
}).start();
|
||||
});
|
||||
|
||||
@@ -344,7 +351,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
IntegerProperty filterID = new SimpleIntegerProperty(this, "Filter ID", 0);
|
||||
IntegerProperty currentFilterID = new SimpleIntegerProperty(this, "Current Filter ID", -1);
|
||||
EventHandler<ActionEvent> searchAction = e -> {
|
||||
if (currentFilterID.get() != filterID.get()) {
|
||||
if (currentFilterID.get() != -1 && currentFilterID.get() != filterID.get()) {
|
||||
control.pageOffset.set(0);
|
||||
}
|
||||
currentFilterID.set(filterID.get());
|
||||
@@ -379,8 +386,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
JFXButton firstPageButton = FXUtils.newBorderButton(i18n("search.first_page"));
|
||||
firstPageButton.setOnAction(event -> {
|
||||
control.pageOffset.set(0);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
});
|
||||
|
||||
JFXButton previousPageButton = FXUtils.newBorderButton(i18n("search.previous_page"));
|
||||
@@ -388,8 +395,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
int pageOffset = control.pageOffset.get();
|
||||
if (pageOffset > 0) {
|
||||
control.pageOffset.set(pageOffset - 1);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -404,16 +411,16 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
int nv = control.pageOffset.get() + 1;
|
||||
if (nv < control.pageCount.get()) {
|
||||
control.pageOffset.set(nv);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
}
|
||||
});
|
||||
|
||||
JFXButton lastPageButton = FXUtils.newBorderButton(i18n("search.last_page"));
|
||||
lastPageButton.setOnAction(event -> {
|
||||
control.pageOffset.set(control.pageCount.get() - 1);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
});
|
||||
|
||||
firstPageButton.setDisable(true);
|
||||
@@ -529,6 +536,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
}
|
||||
|
||||
private static class CategoryIndented {
|
||||
private static final CategoryIndented ALL = new CategoryIndented(0, null);
|
||||
|
||||
private final int indent;
|
||||
private final RemoteModRepository.Category category;
|
||||
|
||||
|
||||
@@ -479,14 +479,17 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
this.setBody(box);
|
||||
|
||||
JFXButton downloadButton = new JFXButton(type == RemoteModRepository.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) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
selfPage.download(version);
|
||||
});
|
||||
JFXButton downloadButton = null;
|
||||
if (selfPage.callback != null) {
|
||||
downloadButton = new JFXButton(type == RemoteModRepository.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) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
selfPage.download(version);
|
||||
});
|
||||
}
|
||||
|
||||
JFXButton saveAsButton = new JFXButton(i18n("mods.save_as"));
|
||||
saveAsButton.getStyleClass().add("dialog-accept");
|
||||
@@ -501,7 +504,11 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
cancelButton.getStyleClass().add("dialog-cancel");
|
||||
cancelButton.setOnAction(e -> fireEvent(new DialogCloseEvent()));
|
||||
|
||||
this.setActions(downloadButton, saveAsButton, cancelButton);
|
||||
if (downloadButton == null) {
|
||||
this.setActions(saveAsButton, cancelButton);
|
||||
} else {
|
||||
this.setActions(downloadButton, saveAsButton, cancelButton);
|
||||
}
|
||||
|
||||
this.prefWidthProperty().bind(BindingMapping.of(Controllers.getStage().widthProperty()).map(w -> w.doubleValue() * 0.7));
|
||||
this.prefHeightProperty().bind(BindingMapping.of(Controllers.getStage().heightProperty()).map(w -> w.doubleValue() * 0.7));
|
||||
|
||||
+5
-5
@@ -55,11 +55,11 @@ public final class HMCLLocalizedDownloadListPage extends DownloadListPage {
|
||||
repository = new Repository(type, curseForge, modrinth);
|
||||
|
||||
supportChinese.set(true);
|
||||
downloadSources.get().setAll("mods.curseforge", "mods.modrinth");
|
||||
if (curseForge != null) {
|
||||
downloadSource.set("mods.curseforge");
|
||||
} else if (modrinth != null) {
|
||||
downloadSources.get().setAll("mods.modrinth", "mods.curseforge");
|
||||
if (modrinth != null) {
|
||||
downloadSource.set("mods.modrinth");
|
||||
} else if (curseForge != null) {
|
||||
downloadSource.set("mods.curseforge");
|
||||
} else {
|
||||
throw new AssertionError("Should not be here.");
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public final class HMCLLocalizedDownloadListPage extends DownloadListPage {
|
||||
try {
|
||||
return I18n.getResourceBundle().getString(key);
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.warning("Cannot find key " + key + " in resource bundle", e);
|
||||
LOG.warning("Cannot find key " + key + " in resource bundle");
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
repository instanceof CurseForgeRemoteModRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false),
|
||||
remoteMod,
|
||||
new Profile.ProfileVersion(ModListPageSkin.this.getSkinnable().getProfile(), ModListPageSkin.this.getSkinnable().getVersionId()),
|
||||
null
|
||||
(profile, version, file) -> org.jackhuang.hmcl.ui.download.DownloadPage.download(profile, version, file, "mods")
|
||||
));
|
||||
});
|
||||
button.setDisable(false);
|
||||
|
||||
@@ -140,7 +140,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
}
|
||||
|
||||
void createBackup() {
|
||||
Controllers.taskDialog(new WorldBackupTask(world, backupsDir).setName(i18n("world.backup")).thenApplyAsync(path -> {
|
||||
Controllers.taskDialog(new WorldBackupTask(world, backupsDir).setName(i18n("world.backup.processing")).thenApplyAsync(path -> {
|
||||
Matcher matcher = backupFileNamePattern.matcher(path.getFileName().toString());
|
||||
if (!matcher.matches()) {
|
||||
throw new AssertionError("Wrong backup file name" + path);
|
||||
@@ -176,7 +176,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
|
||||
@Override
|
||||
protected List<Node> initializeToolbar(WorldBackupsPage skinnable) {
|
||||
return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("world.backup"), SVG.ARCHIVE, skinnable::createBackup));
|
||||
return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("world.backup.create.new_one"), SVG.ARCHIVE, skinnable::createBackup));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ account.login.skip=Log in offline
|
||||
account.login.retry=Retry
|
||||
account.login.refresh=Log in again
|
||||
account.login.refresh.microsoft.hint=You need to log in to your Microsoft account again because the account authorization is invalid.
|
||||
account.login.restricted=Sign in to your Microsoft account to enable this feature
|
||||
account.logout=Logout
|
||||
account.register=Register
|
||||
account.manage=Account List
|
||||
@@ -328,7 +329,7 @@ download=Download
|
||||
download.hint=Install games and modpacks or download mods, resource packs, and worlds.
|
||||
download.code.404=File "%s" not found on the remote server.
|
||||
download.content=Addons
|
||||
download.curseforge.unavailable=HMCL Nightly channel build does not support access to CurseForge. Please use Release or Beta channel builds to download.
|
||||
download.curseforge.unavailable=This HMCL build does not support access to CurseForge. Please use the official build to access CurseForge.
|
||||
download.existing=The file cannot be saved because it already exists. You can click "Save As" to save the file elsewhere.
|
||||
download.external_link=Visit Download Website
|
||||
download.failed=Failed to download "%1$s", response code: %2$d.
|
||||
@@ -1047,11 +1048,13 @@ web.view_in_browser=View in browser
|
||||
|
||||
world=Worlds
|
||||
world.add=Add World
|
||||
world.backup=Backup World
|
||||
world.backup=World Backup
|
||||
world.backup.create.new_one=Create New Backup
|
||||
world.backup.create.failed=Failed to create backup.\n%s
|
||||
world.backup.create.locked=The world is currently in use. Please close the game and try again.
|
||||
world.backup.create.success=Successfully created a new backup: %s
|
||||
world.backup.delete=Delete this backup
|
||||
world.backup.processing=Backing up ...
|
||||
world.backup.reveal=Show in folder
|
||||
world.backup.title=World [%s] - Backups
|
||||
world.datapack=Manage Datapacks
|
||||
|
||||
@@ -328,7 +328,7 @@ download=Descargar
|
||||
download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos y mundos.
|
||||
download.code.404=Archivo no encontrado en el servidor remoto: %s
|
||||
download.content=Complementos
|
||||
download.curseforge.unavailable=La versión Nightly de HMCL no admite el acceso a CurseForge. Utilice las versiones Release o Beta para descargarlas.
|
||||
download.curseforge.unavailable=Esta versión de HMCL no permite acceder a CurseForge. Utilice la versión oficial para acceder a CurseForge.
|
||||
download.existing=El archivo no se puede guardar porque ya existe. Puedes hacer clic en «Guardar como» para guardar el archivo en otro lugar.
|
||||
download.external_link=Abrir sitio web
|
||||
download.failed=Falló la descarga de %1$s, código de respuesta: %2$d.
|
||||
@@ -1052,10 +1052,12 @@ web.view_in_browser=Ver en navegador
|
||||
world=Mundos
|
||||
world.add=Añadir mundo
|
||||
world.backup=Copia de seguridad
|
||||
world.backup.create.new_one=Crear uno nuevo
|
||||
world.backup.create.failed=No se ha podido crear la copia de seguridad.\n%s
|
||||
world.backup.create.locked=El mundo está actualmente en uso. Por favor, cierra el juego e inténtalo de nuevo.
|
||||
world.backup.create.success=Creada con éxito una nueva copia de seguridad: %s
|
||||
world.backup.delete=Eliminar esta copia de seguridad
|
||||
world.backup.processing=Creando nueva copia de seguridad ...
|
||||
world.backup.reveal=Abrir el directorio
|
||||
world.backup.title=Mundo [%s] - Copias de seguridad
|
||||
world.datapack=Gestionar paquetes de datos
|
||||
|
||||
@@ -329,7 +329,7 @@ download=Скачать
|
||||
download.hint=Установить игры и модпаки или скачать моды, пакеты ресурсов и миры.
|
||||
download.code.404=Файл «%s» не найден на удаленном сервере.
|
||||
download.content=Аддоны
|
||||
download.curseforge.unavailable=Альфа-версия HMCL не поддерживает доступ к CurseForge. Для скачивания используйте версии релиза или бета.
|
||||
download.curseforge.unavailable=Эта сборка HMCL не поддерживает доступ к CurseForge. Пожалуйста, используйте официальную сборку для доступа к CurseForge.
|
||||
download.existing=Файл существует и по этому не может быть сохранён. Можно использовать «Сохранить как», чтобы сохранить файл в другом месте.
|
||||
download.external_link=Открыть сайт
|
||||
download.failed=Не удалось скачать «%1$s», код ответа: %2$d.
|
||||
@@ -1051,10 +1051,12 @@ web.view_in_browser=Смотреть в браузере
|
||||
world=Миры
|
||||
world.add=Добавить мир
|
||||
world.backup=Резервный мир
|
||||
world.backup.create.new_one=Создать новый
|
||||
world.backup.create.failed=Не удалось создать резервную копию.\n%s
|
||||
world.backup.create.locked=В настоящее время мир находится в эксплуатации. Закройте игру и попробуйте снова.
|
||||
world.backup.create.success=Успешно создано новое резервное копирование: %s
|
||||
world.backup.delete=Удалить эту резервную копию
|
||||
world.backup.processing=Создание новой резервной копии ...
|
||||
world.backup.reveal=Показать в диспетчере файлов
|
||||
world.backup.title=Мир [%s] - Резервные копии
|
||||
world.datapack=Управлять наборами данных
|
||||
|
||||
@@ -91,6 +91,7 @@ account.login.skip=跳過重新整理帳戶
|
||||
account.login.retry=再次重新整理帳戶
|
||||
account.login.refresh=重新登入
|
||||
account.login.refresh.microsoft.hint=由於帳戶授權失效,你需要重新加入 Microsoft 帳戶
|
||||
account.login.restricted=登入微軟帳戶以啟用此功能
|
||||
account.logout=登出
|
||||
account.register=註冊
|
||||
account.manage=帳戶清單
|
||||
@@ -330,7 +331,7 @@ download=下載
|
||||
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
|
||||
download.code.404=遠端伺服器沒有需要下載的檔案:%s
|
||||
download.content=遊戲內容
|
||||
download.curseforge.unavailable=HMCL 預覽版暫不支援訪問 CurseForge,請使用穩定版或開發版進行下載。
|
||||
download.curseforge.unavailable=這個 HMCL 版本不支援訪問 CurseForge。請使用官方版本進行下載。
|
||||
download.existing=檔案已存在,無法儲存。你可以將檔案儲存至其他地方。
|
||||
download.external_link=打開下載網站
|
||||
download.failed=下載失敗:%1$s\n錯誤碼:%2$d
|
||||
@@ -670,7 +671,7 @@ modpack.download=下載模組包
|
||||
modpack.download.title=模組包下載 - %1s
|
||||
modpack.enter_name=給遊戲取個你喜歡的名稱
|
||||
modpack.export=匯出模組包
|
||||
modpack.export.as=請選取模組包類型。若你無法決定,請選取 MCBBS 類型。
|
||||
modpack.export.as=請選取模組包類型
|
||||
modpack.file_api=模組包下載連結前綴
|
||||
modpack.files.blueprints=BuildCraft 藍圖
|
||||
modpack.files.config=模組設定檔案
|
||||
@@ -703,7 +704,7 @@ modpack.type.curse=Curse
|
||||
modpack.type.curse.error=無法完成該模組包所需的相依元件下載,請多次重試或設定代理……
|
||||
modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載,請嘗試該模組包的最新版本或者安裝其他模組包。
|
||||
modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能已經包含啟動器,建議嘗試解壓後使用其內建的啟動器執行遊戲。\nHMCL 可以嘗試匯入該模組包,但不保證可用性,是否繼續?
|
||||
modpack.type.mcbbs=MCBBS
|
||||
modpack.type.mcbbs=MCBBS 模組包
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 匯入
|
||||
modpack.type.modrinth=Modrinth
|
||||
modpack.type.multimc=MultiMC
|
||||
@@ -853,11 +854,13 @@ web.view_in_browser=在瀏覽器中查看
|
||||
|
||||
world=世界
|
||||
world.add=加入世界
|
||||
world.backup=備份世界
|
||||
world.backup=世界備份
|
||||
world.backup.create.new_one=建立新備份
|
||||
world.backup.create.failed=創建備份失敗。\n%s
|
||||
world.backup.create.locked=該世界正在使用中,請關閉遊戲後重試。
|
||||
world.backup.create.success=成功創建新備份:%s
|
||||
world.backup.delete=删除此備份
|
||||
world.backup.processing=正在備份中……
|
||||
world.backup.title=世界 [%s] - 備份
|
||||
world.datapack=管理資料包
|
||||
world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支援資料包
|
||||
|
||||
@@ -92,6 +92,7 @@ account.login.skip=跳过账户刷新
|
||||
account.login.retry=再次刷新账户
|
||||
account.login.refresh=重新登录
|
||||
account.login.refresh.microsoft.hint=由于账户授权失效,你需要重新添加微软账户。
|
||||
account.login.restricted=登录微软账户以启用此功能
|
||||
account.logout=登出
|
||||
account.register=注册
|
||||
account.manage=账户列表
|
||||
@@ -339,7 +340,7 @@ download=下载
|
||||
download.hint=安装游戏和整合包或下载模组、资源包和地图
|
||||
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
|
||||
download.content=游戏内容
|
||||
download.curseforge.unavailable=HMCL 预览版暂不支持访问 CurseForge,请使用稳定版或开发版进行下载。
|
||||
download.curseforge.unavailable=此 HMCL 版本不支持访问 CurseForge。请使用官方版本进行下载。
|
||||
download.existing=文件已存在,无法保存。你可以将文件保存至其他地方。
|
||||
download.external_link=打开下载网站
|
||||
download.failed=下载失败: %1$s,\n错误码:%2$d\n你可以点击右上角帮助按钮进行求助。
|
||||
@@ -681,7 +682,7 @@ modpack.download=下载整合包
|
||||
modpack.download.title=整合包下载 - %1s
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.export=导出整合包
|
||||
modpack.export.as=请选择整合包类型 (若无法决定,请选择“我的世界中文论坛整合包标准”)
|
||||
modpack.export.as=请选择整合包类型
|
||||
modpack.file_api=整合包下载链接前缀
|
||||
modpack.files.blueprints=BuildCraft 蓝图
|
||||
modpack.files.config=模组配置文件
|
||||
@@ -714,7 +715,7 @@ modpack.type.curse=Curse
|
||||
modpack.type.curse.error=未能完成该整合包所需的依赖下载,请再次尝试或设置代理。\n你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.manual.warning=该整合包由发布者手动打包,其中可能已经包含启动器,建议尝试解压后使用其自带的启动器运行游戏。\n如遇到问题,你可以点击右上角帮助按钮进行求助。\nHMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
|
||||
modpack.type.mcbbs=我的世界中文论坛整合包标准
|
||||
modpack.type.mcbbs=MCBBS 整合包 (推荐)
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 导入
|
||||
modpack.type.modrinth=Modrinth
|
||||
modpack.type.multimc=MultiMC
|
||||
@@ -864,11 +865,13 @@ web.view_in_browser=在浏览器中查看
|
||||
|
||||
world=世界
|
||||
world.add=添加世界
|
||||
world.backup=备份世界
|
||||
world.backup=世界备份
|
||||
world.backup.create.new_one=创建新备份
|
||||
world.backup.create.failed=创建备份失败。\n%s
|
||||
world.backup.create.locked=该世界正在使用中,请关闭游戏后重试。
|
||||
world.backup.create.success=成功创建新备份:%s
|
||||
world.backup.delete=删除此备份
|
||||
world.backup.processing=正在备份中……
|
||||
world.backup.title=世界 [%s] - 备份
|
||||
world.datapack=管理数据包
|
||||
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
|
||||
|
||||
@@ -3979,6 +3979,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.oshi:oshi-core:6.6.5": {
|
||||
"name": "com.github.oshi:oshi-core:6.8.0",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "com/github/oshi/oshi-core/6.8.0/oshi-core-6.8.0.jar",
|
||||
"url": "https://repo1.maven.org/maven2/com/github/oshi/oshi-core/6.8.0/oshi-core-6.8.0.jar",
|
||||
"sha1": "004d12fac84286063e6842688610b8b2d43924a4",
|
||||
"size": 1011309
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.java.jinput:jinput-platform:2.0.5:natives": null,
|
||||
"com.mojang:text2speech:1.10.3:natives": null,
|
||||
"com.mojang:text2speech:1.11.3:natives": null,
|
||||
@@ -3987,26 +3998,26 @@
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:x64",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:x64",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x64.jar",
|
||||
"sha1": "629fca32417d6ec489cef8b2cbd0827131ec6801",
|
||||
"size": 27174940
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar",
|
||||
"sha1": "b2552fcc8c98e95b4559c39f2ff87cdb3aaaa513",
|
||||
"size": 27971214
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"windows-x86": {
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:x86",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:x86",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x86.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x86.jar",
|
||||
"sha1": "d25e0cdf5c5eb182acc9b93f700e0e6d8de36283",
|
||||
"size": 22528549
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar",
|
||||
"sha1": "6fd0fd7da88ca6636f735e1e0f6feb7f62f59715",
|
||||
"size": 23311299
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4389,13 +4400,13 @@
|
||||
"com.mojang:text2speech:1.12.4:natives": null,
|
||||
"com.mojang:text2speech:1.13.9:natives-windows": null,
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:arm64",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:arm64",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-arm64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-arm64.jar",
|
||||
"sha1": "1986490c6fbe950e64018c2fb62c8ecf77a247ed",
|
||||
"size": 24082103
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar",
|
||||
"sha1": "40ca5de3351bf48b7b9efa5d2598d5b6b4d1ab8e",
|
||||
"size": 24905270
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val kalaCompressVersion = "1.27.1-1"
|
||||
|
||||
api("org.glavo.kala:kala-compress-archivers-zip:$kalaCompressVersion")
|
||||
api("org.glavo.kala:kala-compress-archivers-tar:$kalaCompressVersion")
|
||||
api("org.glavo:simple-png-javafx:0.3.0")
|
||||
api("com.google.code.gson:gson:2.12.1")
|
||||
api("com.google.code.gson:gson:2.13.0")
|
||||
api("com.moandjiezana.toml:toml4j:0.7.2")
|
||||
api("org.tukaani:xz:1.10")
|
||||
api("org.hildan.fxgson:fx-gson:5.0.0")
|
||||
api("org.jenkins-ci:constant-pool-scanner:1.2")
|
||||
api("com.github.steveice10:opennbt:1.5")
|
||||
api("org.nanohttpd:nanohttpd:2.3.1")
|
||||
api("org.apache.commons:commons-compress:1.25.0")
|
||||
api("org.jsoup:jsoup:1.18.3")
|
||||
api("org.jsoup:jsoup:1.19.1")
|
||||
compileOnlyApi("org.jetbrains:annotations:26.0.1")
|
||||
|
||||
if (JavaVersion.current().isJava8) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.download.UnsupportedInstallationException.FABRIC_NOT_COMPATIBLE_WITH_FORGE;
|
||||
@@ -83,8 +84,12 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion()));
|
||||
public void execute() throws IOException {
|
||||
FabricInfo fabricInfo = JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class);
|
||||
if (fabricInfo == null)
|
||||
throw new IOException("Fabric metadata is invalid");
|
||||
|
||||
setResult(getPatch(fabricInfo, remote.getGameVersion(), remote.getSelfVersion()));
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true));
|
||||
}
|
||||
|
||||
+27
-54
@@ -18,21 +18,16 @@
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
@@ -47,66 +42,44 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
private static final class LiteLoaderBMCLVersion {
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
private final LiteLoaderVersion build;
|
||||
private final String version;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = "https://bmclapi2.bangbang93.com/liteloader/download?version=" + version;
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
public LiteLoaderBMCLVersion(LiteLoaderVersion build, String version) {
|
||||
this.build = build;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return HttpRequest.GET(
|
||||
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/list"), Pair.pair("mcversion", gameVersion)
|
||||
)
|
||||
.getJsonAsync(LiteLoaderBMCLVersion.class)
|
||||
.thenAccept(v -> {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
||||
gameVersion, v.version, RemoteVersion.Type.UNCATEGORIZED,
|
||||
Collections.singletonList(NetworkUtils.withQuery(
|
||||
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/download"),
|
||||
Collections.singletonMap("version", v.version)
|
||||
)),
|
||||
v.build.getTweakClass(), v.build.getLibraries()
|
||||
));
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
private static String getLatestSnapshotVersion(String repo) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(repo + "maven-metadata.xml");
|
||||
Element r = doc.getDocumentElement();
|
||||
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
|
||||
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
|
||||
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
|
||||
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -30,6 +30,7 @@ import java.util.List;
|
||||
public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
private final String tweakClass;
|
||||
private final Collection<Library> libraries;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -37,8 +38,8 @@ public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, List<String> urls, String tweakClass, Collection<Library> libraries) {
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, urls);
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, Type type, List<String> urls, String tweakClass, Collection<Library> libraries) {
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, type, urls);
|
||||
|
||||
this.tweakClass = tweakClass;
|
||||
this.libraries = libraries;
|
||||
|
||||
+63
-53
@@ -18,21 +18,20 @@
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
@@ -45,52 +44,39 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile();
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
public static final String LITELOADER_LIST = "https://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
LiteLoaderGameVersions versions = root.getVersions().get(gameVersion);
|
||||
if (versions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LiteLoaderRemoteVersion snapshot = null;
|
||||
if (versions.getSnapshots() != null) {
|
||||
try {
|
||||
snapshot = loadSnapshotVersion(gameVersion, versions.getSnapshots().getLiteLoader().get("latest"));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
versions.clear();
|
||||
this.versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
if (versions.getRepoitory() != null && versions.getArtifacts() != null) {
|
||||
loadArtifactVersion(gameVersion, versions.getRepoitory(), versions.getArtifacts());
|
||||
}
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
if (snapshot != null) {
|
||||
this.versions.put(gameVersion, snapshot);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
@@ -98,16 +84,40 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static String getLatestSnapshotVersion(String repo) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(repo + "maven-metadata.xml");
|
||||
Element r = doc.getDocumentElement();
|
||||
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
|
||||
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
|
||||
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
|
||||
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
|
||||
private void loadArtifactVersion(String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch) {
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
||||
gameVersion, v.getVersion(), RemoteVersion.Type.RELEASE,
|
||||
Collections.singletonList(repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile()),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/HMCL-dev/HMCL/issues/3147: Some LiteLoader artifacts aren't published on http://dl.liteloader.com/repo
|
||||
private static final String SNAPSHOT_METADATA = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/maven-metadata.xml";
|
||||
private static final String SNAPSHOT_FILE = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/liteloader-%s-%s-%s-release.jar";
|
||||
|
||||
private LiteLoaderRemoteVersion loadSnapshotVersion(String gameVersion, LiteLoaderVersion v) throws IOException {
|
||||
String root = HttpRequest.GET(String.format(SNAPSHOT_METADATA, gameVersion)).getString();
|
||||
Document document = Jsoup.parseBodyFragment(root);
|
||||
String timestamp = Objects.requireNonNull(document.select("timestamp"), "timestamp").text();
|
||||
String buildNumber = Objects.requireNonNull(document.select("buildNumber"), "buildNumber").text();
|
||||
|
||||
return new LiteLoaderRemoteVersion(
|
||||
gameVersion, timestamp + "-" + buildNumber, RemoteVersion.Type.SNAPSHOT,
|
||||
Collections.singletonList(String.format(SNAPSHOT_FILE, gameVersion, gameVersion, timestamp, buildNumber)),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -10,6 +10,7 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemoteVersion> {
|
||||
private final DownloadProvider downloadProvider;
|
||||
@@ -56,8 +57,20 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
|
||||
}
|
||||
|
||||
for (String version : results[1].versions) {
|
||||
int si1 = version.indexOf('.'), si2 = version.indexOf('.', version.indexOf('.') + 1);
|
||||
String mcVersion = "1." + version.substring(0, Integer.parseInt(version.substring(si1 + 1, si2)) == 0 ? si1 : si2);
|
||||
String mcVersion;
|
||||
|
||||
try {
|
||||
int si1 = version.indexOf('.'), si2 = version.indexOf('.', version.indexOf('.') + 1);
|
||||
int majorVersion = Integer.parseInt(version.substring(0, si1));
|
||||
if (majorVersion == 0) { // Snapshot version.
|
||||
mcVersion = version.substring(si1 + 1, si2);
|
||||
} else {
|
||||
mcVersion = "1." + version.substring(0, Integer.parseInt(version.substring(si1 + 1, si2)) == 0 ? si1 : si2);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
LOG.warning(String.format("Cannot parse NeoForge version %s for cracking its mc version.", version), e);
|
||||
continue;
|
||||
}
|
||||
|
||||
versions.put(mcVersion, new NeoForgeRemoteVersion(
|
||||
mcVersion, NeoForgeRemoteVersion.normalize(version),
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.java;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import org.jackhuang.hmcl.util.KeyValuePairProperties;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
|
||||
@@ -189,30 +189,57 @@ public class DefaultLauncher extends Launcher {
|
||||
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS)
|
||||
res.addDefault("-Duser.home=", options.getGameDir().getParent());
|
||||
|
||||
final int javaVersion = options.getJava().getParsedVersion();
|
||||
final boolean is64bit = options.getJava().getBits() == Bits.BIT_64;
|
||||
|
||||
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
|
||||
res.addUnstableDefault("UnlockDiagnosticVMOptions", true);
|
||||
|
||||
// Using G1GC with its settings by default
|
||||
if (options.getJava().getParsedVersion() >= 8
|
||||
if (javaVersion >= 8
|
||||
&& res.noneMatch(arg -> "-XX:-UseG1GC".equals(arg) || (arg.startsWith("-XX:+Use") && arg.endsWith("GC")))) {
|
||||
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
|
||||
res.addUnstableDefault("UseG1GC", true);
|
||||
res.addUnstableDefault("G1MixedGCCountTarget", "5");
|
||||
res.addUnstableDefault("G1NewSizePercent", "20");
|
||||
res.addUnstableDefault("G1ReservePercent", "20");
|
||||
res.addUnstableDefault("MaxGCPauseMillis", "50");
|
||||
res.addUnstableDefault("G1HeapRegionSize", "32m");
|
||||
}
|
||||
|
||||
res.addUnstableDefault("UseAdaptiveSizePolicy", false);
|
||||
res.addUnstableDefault("OmitStackTraceInFastThrow", false);
|
||||
res.addUnstableDefault("DontCompileHugeMethods", false);
|
||||
|
||||
// JIT Options
|
||||
if (javaVersion <= 8) {
|
||||
res.addUnstableDefault("MaxInlineLevel", "15");
|
||||
}
|
||||
if (is64bit && OperatingSystem.TOTAL_MEMORY > 4 * 1024) {
|
||||
res.addUnstableDefault("DontCompileHugeMethods", false);
|
||||
res.addUnstableDefault("MaxNodeLimit", "240000");
|
||||
res.addUnstableDefault("NodeLimitFudgeFactor", "8000");
|
||||
res.addUnstableDefault("TieredCompileTaskTimeout", "10000");
|
||||
res.addUnstableDefault("ReservedCodeCacheSize", "400M");
|
||||
if (javaVersion >= 9) {
|
||||
res.addUnstableDefault("NonNMethodCodeHeapSize", "12M");
|
||||
res.addUnstableDefault("ProfiledCodeHeapSize", "194M");
|
||||
}
|
||||
|
||||
if (javaVersion >= 8) {
|
||||
res.addUnstableDefault("NmethodSweepActivity", "1");
|
||||
}
|
||||
}
|
||||
|
||||
// As 32-bit JVM allocate 320KB for stack by default rather than 64-bit version allocating 1MB,
|
||||
// causing Minecraft 1.13 crashed accounting for java.lang.StackOverflowError.
|
||||
if (options.getJava().getBits() == Bits.BIT_32) {
|
||||
if (!is64bit) {
|
||||
res.addDefault("-Xss", "1m");
|
||||
}
|
||||
|
||||
if (options.getJava().getParsedVersion() == 16)
|
||||
if (javaVersion == 16)
|
||||
res.addDefault("--illegal-access=", "permit");
|
||||
|
||||
if (javaVersion == 24 || javaVersion == 25)
|
||||
res.addDefault("--sun-misc-unsafe-memory-access=", "allow");
|
||||
|
||||
res.addDefault("-Dfml.ignoreInvalidMinecraftCertificates=", "true");
|
||||
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -44,7 +44,7 @@ public interface ModpackProvider {
|
||||
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||
* @return the manifest.
|
||||
*/
|
||||
Modpack readManifest(ZipFile zipFile, Path file, Charset encoding) throws IOException, JsonParseException;
|
||||
Modpack readManifest(ZipArchiveReader zipFile, Path file, Charset encoding) throws IOException, JsonParseException;
|
||||
|
||||
default void injectLaunchOptions(String modpackConfigurationJson, LaunchOptions.Builder builder) {
|
||||
}
|
||||
|
||||
+3
-1
@@ -102,7 +102,9 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
|
||||
@Override
|
||||
public SearchResult search(String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException {
|
||||
int categoryId = 0;
|
||||
if (category != null) categoryId = ((CurseAddon.Category) category.getSelf()).getId();
|
||||
if (category != null && category.getSelf() instanceof CurseAddon.Category) {
|
||||
categoryId = ((CurseAddon.Category) category.getSelf()).getId();
|
||||
}
|
||||
Response<List<CurseAddon>> response = HttpRequest.GET(PREFIX + "/v1/mods/search",
|
||||
pair("gameId", "432"),
|
||||
pair("classId", Integer.toString(section)),
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.curse;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
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;
|
||||
@@ -57,7 +57,7 @@ public final class CurseModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
CurseManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "manifest.json"), CurseManifest.class);
|
||||
String description = "No description";
|
||||
try {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.mcbbs;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
@@ -70,7 +70,7 @@ public final class McbbsModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
ZipArchiveEntry mcbbsPackMeta = zip.getEntry("mcbbs.packmeta");
|
||||
if (mcbbsPackMeta != null) {
|
||||
return fromManifestFile(zip.getInputStream(mcbbsPackMeta), encoding);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod.modrinth;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -55,7 +55,7 @@ public final class ModrinthModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
ModrinthManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "modrinth.index.json"), ModrinthManifest.class);
|
||||
return new Modpack(manifest.getName(), "", manifest.getVersionId(), manifest.getGameVersion(), manifest.getSummary(), encoding, manifest) {
|
||||
@Override
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class MultiMCManifest {
|
||||
* @throws IOException if zip file is malformed
|
||||
* @throws com.google.gson.JsonParseException if manifest is malformed.
|
||||
*/
|
||||
public static MultiMCManifest readMultiMCModpackManifest(ZipFile zipFile, String rootEntryName) throws IOException {
|
||||
public static MultiMCManifest readMultiMCModpackManifest(ZipArchiveReader zipFile, String rootEntryName) throws IOException {
|
||||
ZipArchiveEntry mmcPack = zipFile.getEntry(rootEntryName + "mmc-pack.json");
|
||||
if (mmcPack == null)
|
||||
return null;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
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;
|
||||
@@ -33,7 +33,6 @@ import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
@@ -71,14 +70,12 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRootEntryName(ZipFile file) throws IOException {
|
||||
private static String getRootEntryName(ZipArchiveReader file) throws IOException {
|
||||
final String instanceFileName = "instance.cfg";
|
||||
|
||||
if (file.getEntry(instanceFileName) != null) return "";
|
||||
|
||||
Enumeration<ZipArchiveEntry> entries = file.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipArchiveEntry entry = entries.nextElement();
|
||||
for (ZipArchiveEntry entry : file.getEntries()) {
|
||||
String entryName = entry.getName();
|
||||
|
||||
int idx = entryName.indexOf('/');
|
||||
@@ -92,7 +89,7 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile modpackFile, Path modpackPath, Charset encoding) throws IOException {
|
||||
public Modpack readManifest(ZipArchiveReader modpackFile, Path modpackPath, Charset encoding) throws IOException {
|
||||
String rootEntryName = getRootEntryName(modpackFile);
|
||||
MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile, rootEntryName);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod.server;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -55,7 +55,7 @@ public final class ServerModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json");
|
||||
ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class);
|
||||
return manifest.toModpack(encoding);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
@@ -54,19 +54,16 @@ public final class CompressingUtils {
|
||||
}
|
||||
|
||||
public static boolean testEncoding(Path zipFile, Charset encoding) throws IOException {
|
||||
try (ZipFile zf = openZipFile(zipFile, encoding)) {
|
||||
try (ZipArchiveReader zf = openZipFile(zipFile, encoding)) {
|
||||
return testEncoding(zf, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testEncoding(ZipFile zipFile, Charset encoding) throws IOException {
|
||||
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
|
||||
public static boolean testEncoding(ZipArchiveReader zipFile, Charset encoding) throws IOException {
|
||||
CharsetDecoder cd = newCharsetDecoder(encoding);
|
||||
CharBuffer cb = CharBuffer.allocate(32);
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipArchiveEntry entry = entries.nextElement();
|
||||
|
||||
for (ZipArchiveEntry entry : zipFile.getEntries()) {
|
||||
if (entry.getGeneralPurposeBit().usesUTF8ForNames()) continue;
|
||||
|
||||
cd.reset();
|
||||
@@ -88,12 +85,12 @@ public final class CompressingUtils {
|
||||
}
|
||||
|
||||
public static Charset findSuitableEncoding(Path zipFile) throws IOException {
|
||||
try (ZipFile zf = openZipFile(zipFile, StandardCharsets.UTF_8)) {
|
||||
try (ZipArchiveReader zf = openZipFile(zipFile, StandardCharsets.UTF_8)) {
|
||||
return findSuitableEncoding(zf);
|
||||
}
|
||||
}
|
||||
|
||||
public static Charset findSuitableEncoding(ZipFile zipFile) throws IOException {
|
||||
public static Charset findSuitableEncoding(ZipArchiveReader zipFile) throws IOException {
|
||||
if (testEncoding(zipFile, StandardCharsets.UTF_8)) return StandardCharsets.UTF_8;
|
||||
if (OperatingSystem.NATIVE_CHARSET != StandardCharsets.UTF_8 && testEncoding(zipFile, OperatingSystem.NATIVE_CHARSET))
|
||||
return OperatingSystem.NATIVE_CHARSET;
|
||||
@@ -133,12 +130,12 @@ public final class CompressingUtils {
|
||||
throw new IOException("Cannot find suitable encoding for the zip.");
|
||||
}
|
||||
|
||||
public static ZipFile openZipFile(Path zipFile) throws IOException {
|
||||
return new ZipFile(Files.newByteChannel(zipFile));
|
||||
public static ZipArchiveReader openZipFile(Path zipFile) throws IOException {
|
||||
return new ZipArchiveReader(Files.newByteChannel(zipFile));
|
||||
}
|
||||
|
||||
public static ZipFile openZipFile(Path zipFile, Charset charset) throws IOException {
|
||||
return new ZipFile(Files.newByteChannel(zipFile), charset.name());
|
||||
public static ZipArchiveReader openZipFile(Path zipFile, Charset charset) throws IOException {
|
||||
return new ZipArchiveReader(zipFile, charset);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
@@ -234,7 +231,7 @@ public final class CompressingUtils {
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(File zipFile, String name) throws IOException {
|
||||
try (ZipFile s = new ZipFile(zipFile)) {
|
||||
try (ZipArchiveReader s = new ZipArchiveReader(zipFile.toPath())) {
|
||||
return readTextZipEntry(s, name);
|
||||
}
|
||||
}
|
||||
@@ -247,7 +244,7 @@ public final class CompressingUtils {
|
||||
* @throws IOException if the file is not a valid zip file.
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(ZipFile zipFile, String name) throws IOException {
|
||||
public static String readTextZipEntry(ZipArchiveReader zipFile, String name) throws IOException {
|
||||
return IOUtils.readFullyAsString(zipFile.getInputStream(zipFile.getEntry(name)));
|
||||
}
|
||||
|
||||
@@ -260,7 +257,7 @@ public final class CompressingUtils {
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(Path zipFile, String name, Charset encoding) throws IOException {
|
||||
try (ZipFile s = openZipFile(zipFile, encoding)) {
|
||||
try (ZipArchiveReader s = openZipFile(zipFile, encoding)) {
|
||||
return IOUtils.readFullyAsString(s.getInputStream(s.getEntry(name)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -39,7 +39,7 @@ public abstract class ArchiveFileTree<F, E extends ArchiveEntry> implements Clos
|
||||
|
||||
String name = namePath.toString();
|
||||
if (name.endsWith(".jar") || name.endsWith(".zip")) {
|
||||
return new ZipFileTree(new ZipFile(file));
|
||||
return new ZipFileTree(new ZipArchiveReader(file));
|
||||
} else if (name.endsWith(".tar") || name.endsWith(".tar.gz") || name.endsWith(".tgz")) {
|
||||
return TarFileTree.open(file);
|
||||
} else {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarFile;
|
||||
import kala.compress.archivers.tar.TarArchiveEntry;
|
||||
import kala.compress.archivers.tar.TarArchiveReader;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
@@ -31,19 +31,19 @@ import java.util.zip.GZIPInputStream;
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class TarFileTree extends ArchiveFileTree<TarFile, TarArchiveEntry> {
|
||||
public final class TarFileTree extends ArchiveFileTree<TarArchiveReader, TarArchiveEntry> {
|
||||
|
||||
public static TarFileTree open(Path file) throws IOException {
|
||||
String fileName = file.getFileName().toString();
|
||||
|
||||
if (fileName.endsWith(".tar.gz") || fileName.endsWith(".tgz")) {
|
||||
Path tempFile = Files.createTempFile("hmcl-", ".tar");
|
||||
TarFile tarFile;
|
||||
TarArchiveReader tarFile;
|
||||
try (GZIPInputStream input = new GZIPInputStream(Files.newInputStream(file));
|
||||
OutputStream output = Files.newOutputStream(tempFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)
|
||||
) {
|
||||
IOUtils.copyTo(input, output);
|
||||
tarFile = new TarFile(tempFile.toFile());
|
||||
tarFile = new TarArchiveReader(tempFile);
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
Files.deleteIfExists(tempFile);
|
||||
@@ -55,14 +55,14 @@ public final class TarFileTree extends ArchiveFileTree<TarFile, TarArchiveEntry>
|
||||
|
||||
return new TarFileTree(tarFile, tempFile);
|
||||
} else {
|
||||
return new TarFileTree(new TarFile(file), null);
|
||||
return new TarFileTree(new TarArchiveReader(file), null);
|
||||
}
|
||||
}
|
||||
|
||||
private final Path tempFile;
|
||||
private final Thread shutdownHook;
|
||||
|
||||
public TarFileTree(TarFile file, Path tempFile) throws IOException {
|
||||
public TarFileTree(TarArchiveReader file, Path tempFile) throws IOException {
|
||||
super(file);
|
||||
this.tempFile = tempFile;
|
||||
try {
|
||||
|
||||
@@ -17,23 +17,21 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class ZipFileTree extends ArchiveFileTree<ZipFile, ZipArchiveEntry> {
|
||||
public ZipFileTree(ZipFile file) throws IOException {
|
||||
public final class ZipFileTree extends ArchiveFileTree<ZipArchiveReader, ZipArchiveEntry> {
|
||||
public ZipFileTree(ZipArchiveReader file) throws IOException {
|
||||
super(file);
|
||||
try {
|
||||
Enumeration<ZipArchiveEntry> entries = file.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
addEntry(entries.nextElement());
|
||||
for (ZipArchiveEntry zipArchiveEntry : file.getEntries()) {
|
||||
addEntry(zipArchiveEntry);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"id": "1_19_deep_dark_experimental_snapshot-1",
|
||||
"id": "1.19_deep_dark_experimental_snapshot-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_19_deep_dark_experimental_snapshot-1/1_19_deep_dark_experimental_snapshot-1.json",
|
||||
"time": "2022-02-17T13:55:59+00:00",
|
||||
"releaseTime": "2022-02-17T13:55:59+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-7",
|
||||
"id": "1.18_experimental-snapshot-7",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-7/1_18_experimental-snapshot-7.json",
|
||||
"time": "2021-09-08T12:33:23+00:00",
|
||||
"releaseTime": "2021-09-08T12:33:23+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-6",
|
||||
"id": "1.18_experimental-snapshot-6",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-6/1_18_experimental-snapshot-6.json",
|
||||
"time": "2021-09-01T12:53:14+00:00",
|
||||
"releaseTime": "2021-09-01T12:53:14+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-5",
|
||||
"id": "1.18_experimental-snapshot-5",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-5/1_18_experimental-snapshot-5.json",
|
||||
"time": "2021-08-25T14:41:57+00:00",
|
||||
"releaseTime": "2021-08-25T14:41:57+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-4",
|
||||
"id": "1.18_experimental-snapshot-4",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-4/1_18_experimental-snapshot-4.json",
|
||||
"time": "2021-08-17T16:10:25+00:00",
|
||||
"releaseTime": "2021-08-17T16:10:25+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-3",
|
||||
"id": "1.18_experimental-snapshot-3",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-3/1_18_experimental-snapshot-3.json",
|
||||
"time": "2021-08-10T12:42:45+00:00",
|
||||
"releaseTime": "2021-08-10T12:42:45+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-2",
|
||||
"id": "1.18_experimental-snapshot-2",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-2/1_18_experimental-snapshot-2.json",
|
||||
"time": "2021-07-20T13:35:08+00:00",
|
||||
"releaseTime": "2021-07-20T13:35:08+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-1",
|
||||
"id": "1.18_experimental-snapshot-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-1/1_18_experimental-snapshot-1.json",
|
||||
"time": "2021-07-13T12:54:19+00:00",
|
||||
@@ -120,43 +120,43 @@
|
||||
"releaseTime": "2019-11-29T15:41:39+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-3",
|
||||
"id": "1.14_combat-3",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-3/1_14_combat-3.json",
|
||||
"time": "2019-10-31T14:31:38+00:00",
|
||||
"releaseTime": "2019-10-31T14:31:38+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-0",
|
||||
"id": "1.14_combat-0",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-0/1_14_combat-0.json",
|
||||
"time": "2019-08-13T07:33:42+00:00",
|
||||
"releaseTime": "2019-08-13T07:33:42+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-212796",
|
||||
"id": "1.14_combat-212796",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-212796/1_14_combat-212796.json",
|
||||
"time": "2019-06-20T13:23:44+00:00",
|
||||
"releaseTime": "2019-06-20T13:23:44+00:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_blue",
|
||||
"type": "release",
|
||||
"id": "2.0_blue",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_blue/2point0_blue.json",
|
||||
"time": "2013-08-06T06:00:02-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:02-05:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_red",
|
||||
"type": "release",
|
||||
"id": "2.0_red",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_red/2point0_red.json",
|
||||
"time": "2013-08-06T06:00:01-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:01-05:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_purple",
|
||||
"type": "release",
|
||||
"id": "2.0_purple",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_purple/2point0_purple.json",
|
||||
"time": "2013-08-06T06:00:00-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:00-05:00"
|
||||
@@ -632,70 +632,70 @@
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-3",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-3/1.0.0-rc2-3.json",
|
||||
"time": "2013-08-06T07:33:30-07:00",
|
||||
"releaseTime": "2011-11-14T15:00:00-07:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-2",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-2/1.0.0-rc2-2.json",
|
||||
"time": "2019-04-21T21:04:24+00:00",
|
||||
"releaseTime": "2011-11-14T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-1/1.0.0-rc2-1.json",
|
||||
"time": "2019-11-21T22:37:45+00:00",
|
||||
"releaseTime": "2011-11-13T17:33:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc1/1.0.0-rc1.json",
|
||||
"time": "2013-08-06T07:33:30-07:00",
|
||||
"releaseTime": "2011-11-13T15:00:00-07:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre6",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre6/b1_9-pre6.json",
|
||||
"time": "2011-11-11T00:00:00+00:00",
|
||||
"releaseTime": "2011-11-11T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre5",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre5/b1_9-pre5.json",
|
||||
"time": "2011-10-27T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-27T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre4",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre4/b1_9-pre4.json",
|
||||
"time": "2011-10-13T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-13T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre3",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre3/b1_9-pre3.json",
|
||||
"time": "2011-10-06T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-06T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre2",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre2/b1_9-pre2.json",
|
||||
"time": "2011-09-29T00:00:00+00:00",
|
||||
"releaseTime": "2011-09-29T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre1/b1_9-pre1.json",
|
||||
"time": "2011-09-22T00:00:00+00:00",
|
||||
"releaseTime": "2011-09-22T00:00:00+00:00"
|
||||
@@ -1359,4 +1359,4 @@
|
||||
"releaseTime": "2009-05-20T00:00:00-00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user