Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63345f52ce | ||
|
|
0bff282d3d | ||
|
|
a81462425d | ||
|
|
e9ae43b1f1 | ||
|
|
dc31f0d5c9 | ||
|
|
76f11b6a6f | ||
|
|
d6ea2ef1f9 | ||
|
|
14db21211f | ||
|
|
f354f65adb | ||
|
|
83e8ea3735 | ||
|
|
0e9e9811eb | ||
|
|
420d77de9f | ||
|
|
9361719a67 | ||
|
|
b09a5868d4 | ||
|
|
56c48e5595 | ||
|
|
4d6cb547ea | ||
|
|
95ecd29018 | ||
|
|
5d26a106e9 | ||
|
|
95707560ef | ||
|
|
c54cb88db8 | ||
|
|
95afaa5112 | ||
|
|
fe608e32f1 | ||
|
|
82fc1c067d | ||
|
|
94890e2924 | ||
|
|
9a76b1ff27 | ||
|
|
8db8693132 | ||
|
|
59cc5b9114 | ||
|
|
c1acd0b0b5 | ||
|
|
fb6538f042 | ||
|
|
20fe5430dd | ||
|
|
7de07319d9 | ||
|
|
8e3c19651e | ||
|
|
2740bdeb77 | ||
|
|
59ab49e0c5 | ||
|
|
bc4aa43834 | ||
|
|
c7f3f2a229 |
No files matched your search
@@ -5,6 +5,9 @@ on:
|
||||
schedule:
|
||||
- cron: '30 * * * *'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dev-check-update:
|
||||
if: ${{ github.repository_owner == 'HMCL-dev' }}
|
||||
@@ -66,7 +69,7 @@ jobs:
|
||||
name: ${{ env.HMCL_TAG_NAME }}
|
||||
tag_name: ${{ env.HMCL_TAG_NAME }}
|
||||
stable-check-update:
|
||||
if: ${{ github.repository_owner == 'huanghongxun' }}
|
||||
if: ${{ github.repository_owner == 'HMCL-dev' }}
|
||||
needs: dev-check-update
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
@@ -10,16 +7,6 @@ import java.security.Signature
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("com.google.code.gson:gson:2.10.1")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||
}
|
||||
@@ -94,59 +81,6 @@ fun attachSignature(jar: File) {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.getByName<JavaCompile>("compileJava") {
|
||||
dependsOn(tasks.create("computeDynamicResources") {
|
||||
this@create.inputs.file(rootProject.rootDir.toPath().resolve("data-json/dynamic-remote-resources-raw.json"))
|
||||
this@create.outputs.file(rootProject.rootDir.toPath().resolve("data-json/dynamic-remote-resources.json"))
|
||||
|
||||
doLast {
|
||||
Gson().also { gsonInstance ->
|
||||
rootProject.rootDir.resolve("data-json/dynamic-remote-resources-raw.json").bufferedReader().use { br ->
|
||||
(gsonInstance.fromJson(br, JsonElement::class.java) as JsonObject)
|
||||
}.also { data ->
|
||||
data.asMap().forEach { (namespace, namespaceData) ->
|
||||
(namespaceData as JsonObject).asMap().forEach { (name, nameData) ->
|
||||
(nameData as JsonObject).asMap().forEach { (version, versionData) ->
|
||||
require(versionData is JsonObject)
|
||||
val localPath =
|
||||
(versionData.get("local_path") as com.google.gson.JsonPrimitive).asString
|
||||
val sha1 = (versionData.get("sha1") as com.google.gson.JsonPrimitive).asString
|
||||
|
||||
val currentSha1 = digest(
|
||||
"SHA-1",
|
||||
rootProject.rootDir.resolve(localPath).readBytes()
|
||||
).joinToString(separator = "") { "%02x".format(it) }
|
||||
|
||||
if (!sha1.equals(currentSha1, ignoreCase = true)) {
|
||||
throw IllegalStateException("Mismatched SHA-1 in $.${namespace}.${name}.${version} of dynamic remote resources detected. Require ${currentSha1}, but found $sha1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.rootDir.resolve("data-json/dynamic-remote-resources.json").also { zippedPath ->
|
||||
gsonInstance.toJson(data).also { expectedData ->
|
||||
if (zippedPath.exists()) {
|
||||
zippedPath.readText().also { rawData ->
|
||||
if (rawData != expectedData) {
|
||||
if (System.getenv("GITHUB_SHA") == null) {
|
||||
zippedPath.writeText(expectedData)
|
||||
} else {
|
||||
throw IllegalStateException("Mismatched zipped dynamic-remote-resources json file!")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zippedPath.writeText(expectedData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val java11 = sourceSets.create("java11") {
|
||||
java {
|
||||
srcDir("src/main/java11")
|
||||
|
||||
@@ -33,12 +33,10 @@ import org.jackhuang.hmcl.setting.SambaException;
|
||||
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
||||
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.CrashReporter;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
@@ -123,10 +121,6 @@ public final class Launcher extends Application {
|
||||
|
||||
UpdateChecker.init();
|
||||
|
||||
RemoteResourceManager.init();
|
||||
|
||||
RemoteResourceManager.register();
|
||||
|
||||
primaryStage.show();
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
@@ -293,10 +287,6 @@ public final class Launcher extends Application {
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD())
|
||||
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
|
||||
|
||||
if (System.getProperty("hmcl.update_source.override") != null) {
|
||||
Logging.LOG.log(Level.WARNING, "'hmcl.update_source.override' is deprecated! Please use 'hmcl.hmcl_update_source.override' instead");
|
||||
}
|
||||
|
||||
launch(Launcher.class, args);
|
||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
|
||||
|
||||
@@ -37,13 +37,11 @@ public final class Metadata {
|
||||
public static final String TITLE = NAME + " " + VERSION;
|
||||
public static final String FULL_TITLE = FULL_NAME + " v" + VERSION;
|
||||
|
||||
// hmcl.update_source.override is deprecated. If it is used, a warning message will be printed in org.jackhuang.hmcl.Launcher.main .
|
||||
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.hmcl_update_source.override", System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link"));
|
||||
public static final String RESOURCE_UPDATE_URL = System.getProperty("hmcl.resource_update_source.override", "https://hmcl.huangyuhui.net/api/dynamic_remote_resource/update_link");
|
||||
public static final String HMCL_UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link");
|
||||
public static final String CONTACT_URL = "https://docs.hmcl.net/help.html";
|
||||
public static final String HELP_URL = "https://docs.hmcl.net";
|
||||
public static final String CHANGELOG_URL = "https://docs.hmcl.net/changelog/";
|
||||
public static final String PUBLISH_URL = "https://www.mcbbs.net/thread-142335-1-1.html";
|
||||
public static final String PUBLISH_URL = "https://hmcl.huangyuhui.net";
|
||||
public static final String EULA_URL = "https://docs.hmcl.net/eula/hmcl.html";
|
||||
|
||||
public static final String BUILD_CHANNEL = JarUtils.getManifestAttribute("Build-Channel", "nightly");
|
||||
|
||||
@@ -20,10 +20,9 @@ package org.jackhuang.hmcl.countly;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
@@ -40,7 +39,7 @@ public class Countly {
|
||||
pair("metrics", metrics),
|
||||
pair("device_id", deviceId),
|
||||
pair("timestamp", Long.toString(System.currentTimeMillis())),
|
||||
pair("tz", Integer.toString(TimeZone.getDefault().getOffset(new Date().getTime()) / 60000)),
|
||||
pair("tz", Integer.toString(getTimezoneOffset())),
|
||||
pair("hour", Integer.toString(currentHour())),
|
||||
pair("dow", Integer.toString(currentDayOfWeek())),
|
||||
pair("app_key", APP_KEY),
|
||||
@@ -50,7 +49,7 @@ public class Countly {
|
||||
}
|
||||
|
||||
private static int getTimezoneOffset() {
|
||||
return TimeZone.getDefault().getOffset(new Date().getTime()) / 60000;
|
||||
return ZonedDateTime.now().getOffset().getTotalSeconds() / 60;
|
||||
}
|
||||
|
||||
private static String getLocale() {
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -68,7 +68,7 @@ public class CrashReport {
|
||||
public String getDisplayText() {
|
||||
return "---- Hello Minecraft! Crash Report ----\n" +
|
||||
" Version: " + Metadata.VERSION + "\n" +
|
||||
" Time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\n" +
|
||||
" Time: " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()) + "\n" +
|
||||
" Thread: " + thread + "\n" +
|
||||
"\n Content: \n " +
|
||||
stackTrace + "\n\n" +
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -84,7 +84,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
|
||||
1.11 ~ 12:zh_cn 时正常,zh_CN 时虽然显示了中文但语言设置会错误地显示选择英文
|
||||
1.13+ :zh_cn 时正常,zh_CN 时自动切换为英文
|
||||
*/
|
||||
VersionNumber gameVersion = VersionNumber.asVersion(repository.getGameVersion(version).orElse("0.0"));
|
||||
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version));
|
||||
if (gameVersion.compareTo("1.1") < 0) {
|
||||
lang = null;
|
||||
} else if (gameVersion.compareTo("1.11") < 0) {
|
||||
|
||||
@@ -48,6 +48,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -103,7 +104,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
public Stream<Version> getDisplayVersions() {
|
||||
return getVersions().stream()
|
||||
.filter(v -> !v.isHidden())
|
||||
.sorted(Comparator.comparing((Version v) -> v.getReleaseTime() == null ? new Date(0L) : v.getReleaseTime())
|
||||
.sorted(Comparator.comparing((Version v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH))
|
||||
.thenComparing(v -> VersionNumber.asVersion(v.getId())));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
@@ -332,7 +333,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
private static Task<JavaVersion> checkGameState(Profile profile, VersionSetting setting, Version version) {
|
||||
VersionNumber gameVersion = VersionNumber.asVersion(profile.getRepository().getGameVersion(version).orElse("Unknown"));
|
||||
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(profile.getRepository().getGameVersion(version));
|
||||
|
||||
if (setting.isNotCheckJVM()) {
|
||||
return Task.composeAsync(() -> setting.getJavaVersion(gameVersion, version))
|
||||
@@ -549,14 +550,14 @@ public final class LauncherHelper {
|
||||
// Forge 2760~2773 will crash game with LiteLoader.
|
||||
boolean hasForge2760 = forgeVersion != null && (forgeVersion.compareTo("1.12.2-14.23.5.2760") >= 0) && (forgeVersion.compareTo("1.12.2-14.23.5.2773") < 0);
|
||||
boolean hasLiteLoader = version.getLibraries().stream().anyMatch(it -> it.is("com.mumfrey", "liteloader"));
|
||||
if (hasForge2760 && hasLiteLoader && gameVersion.compareTo(VersionNumber.asVersion("1.12.2")) == 0) {
|
||||
if (hasForge2760 && hasLiteLoader && gameVersion.compareTo("1.12.2") == 0) {
|
||||
suggestions.add(i18n("launch.advice.forge2760_liteloader"));
|
||||
}
|
||||
|
||||
// OptiFine 1.14.4 is not compatible with Forge 28.2.2 and later versions.
|
||||
boolean hasForge28_2_2 = forgeVersion != null && (forgeVersion.compareTo("1.14.4-28.2.2") >= 0);
|
||||
boolean hasOptiFine = version.getLibraries().stream().anyMatch(it -> it.is("optifine", "OptiFine"));
|
||||
if (hasForge28_2_2 && hasOptiFine && gameVersion.compareTo(VersionNumber.asVersion("1.14.4")) == 0) {
|
||||
if (hasForge28_2_2 && hasOptiFine && gameVersion.compareTo("1.14.4") == 0) {
|
||||
suggestions.add(i18n("launch.advice.forge28_2_2_optifine"));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.WeakInvalidationListener;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
@@ -29,9 +32,13 @@ import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.ServerResponseMalformedException;
|
||||
import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.Skin;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.*;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
@@ -141,16 +148,7 @@ public final class TexturesLoader {
|
||||
}
|
||||
|
||||
private static void loadDefaultSkin(String path, TextureModel model) {
|
||||
Image skin;
|
||||
try {
|
||||
skin = new Image(path);
|
||||
if (skin.isError())
|
||||
throw skin.getException();
|
||||
} catch (Throwable e) {
|
||||
throw new ResourceNotFoundError("Cannot load default skin from " + path, e);
|
||||
}
|
||||
|
||||
DEFAULT_SKINS.put(model, new LoadedTexture(skin, singletonMap("model", model.modelName)));
|
||||
DEFAULT_SKINS.put(model, new LoadedTexture(FXUtils.newBuiltinImage(path), singletonMap("model", model.modelName)));
|
||||
}
|
||||
|
||||
public static LoadedTexture getDefaultSkin(TextureModel model) {
|
||||
@@ -188,27 +186,61 @@ public final class TexturesLoader {
|
||||
}, uuidFallback);
|
||||
}
|
||||
|
||||
public static ObjectBinding<LoadedTexture> skinBinding(Account account) {
|
||||
public static ObservableValue<LoadedTexture> skinBinding(Account account) {
|
||||
LoadedTexture uuidFallback = getDefaultSkin(TextureModel.detectUUID(account.getUUID()));
|
||||
return BindingMapping.of(account.getTextures())
|
||||
.map(textures -> textures
|
||||
.flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN)))
|
||||
.filter(it -> StringUtils.isNotBlank(it.getUrl())))
|
||||
.asyncMap(it -> {
|
||||
if (it.isPresent()) {
|
||||
Texture texture = it.get();
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return loadTexture(texture);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
return uuidFallback;
|
||||
if (account instanceof OfflineAccount) {
|
||||
OfflineAccount offlineAccount = (OfflineAccount) account;
|
||||
|
||||
SimpleObjectProperty<LoadedTexture> binding = new SimpleObjectProperty<>();
|
||||
InvalidationListener listener = o -> {
|
||||
Skin skin = offlineAccount.getSkin();
|
||||
String username = offlineAccount.getUsername();
|
||||
|
||||
binding.set(uuidFallback);
|
||||
if (skin != null) {
|
||||
skin.load(username).setExecutor(POOL).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (exception != null) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture", exception);
|
||||
} else if (result != null && result.getSkin() != null && result.getSkin().getImage() != null) {
|
||||
Map<String, String> metadata;
|
||||
if (result.getModel() != null) {
|
||||
metadata = singletonMap("model", result.getModel().modelName);
|
||||
} else {
|
||||
metadata = emptyMap();
|
||||
}
|
||||
}, POOL);
|
||||
} else {
|
||||
|
||||
binding.set(new LoadedTexture(result.getSkin().getImage(), metadata));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
listener.invalidated(offlineAccount);
|
||||
|
||||
binding.addListener(new Holder<>(listener));
|
||||
offlineAccount.addListener(new WeakInvalidationListener(listener));
|
||||
|
||||
return binding;
|
||||
} else {
|
||||
return BindingMapping.of(account.getTextures())
|
||||
.asyncMap(textures -> {
|
||||
if (textures.isPresent()) {
|
||||
Texture texture = textures.get().get(TextureType.SKIN);
|
||||
if (texture != null && StringUtils.isNotBlank(texture.getUrl())) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return loadTexture(texture);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
return uuidFallback;
|
||||
}
|
||||
}, POOL);
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(uuidFallback);
|
||||
}
|
||||
}, uuidFallback);
|
||||
}, uuidFallback);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
@@ -277,9 +309,9 @@ public final class TexturesLoader {
|
||||
static final WeakHashMap<Canvas, SkinBindingChangeListener> hole = new WeakHashMap<>();
|
||||
|
||||
final WeakReference<Canvas> canvasRef;
|
||||
final ObjectBinding<LoadedTexture> binding;
|
||||
final ObservableValue<LoadedTexture> binding;
|
||||
|
||||
SkinBindingChangeListener(Canvas canvas, ObjectBinding<LoadedTexture> binding) {
|
||||
SkinBindingChangeListener(Canvas canvas, ObservableValue<LoadedTexture> binding) {
|
||||
this.canvasRef = new WeakReference<>(canvas);
|
||||
this.binding = binding;
|
||||
}
|
||||
@@ -293,14 +325,14 @@ public final class TexturesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public static void fxAvatarBinding(Canvas canvas, ObjectBinding<LoadedTexture> skinBinding) {
|
||||
public static void fxAvatarBinding(Canvas canvas, ObservableValue<LoadedTexture> skinBinding) {
|
||||
synchronized (SkinBindingChangeListener.hole) {
|
||||
SkinBindingChangeListener oldListener = SkinBindingChangeListener.hole.remove(canvas);
|
||||
if (oldListener != null)
|
||||
oldListener.binding.removeListener(oldListener);
|
||||
|
||||
SkinBindingChangeListener listener = new SkinBindingChangeListener(canvas, skinBinding);
|
||||
listener.changed(skinBinding, null, skinBinding.get());
|
||||
listener.changed(skinBinding, null, skinBinding.getValue());
|
||||
skinBinding.addListener(listener);
|
||||
|
||||
SkinBindingChangeListener.hole.put(canvas, listener);
|
||||
@@ -312,7 +344,7 @@ public final class TexturesLoader {
|
||||
}
|
||||
|
||||
public static void bindAvatar(Canvas canvas, Account account) {
|
||||
if (account instanceof YggdrasilAccount || account instanceof MicrosoftAccount)
|
||||
if (account instanceof YggdrasilAccount || account instanceof MicrosoftAccount || account instanceof OfflineAccount)
|
||||
fxAvatarBinding(canvas, skinBinding(account));
|
||||
else {
|
||||
unbindAvatar(canvas);
|
||||
|
||||
@@ -131,7 +131,7 @@ public final class Config implements Cloneable, Observable {
|
||||
private ObjectProperty<SupportedLocale> localization = new SimpleObjectProperty<>(Locales.DEFAULT);
|
||||
|
||||
@SerializedName("autoDownloadThreads")
|
||||
private BooleanProperty autoDownloadThreads = new SimpleBooleanProperty(false);
|
||||
private BooleanProperty autoDownloadThreads = new SimpleBooleanProperty(true);
|
||||
|
||||
@SerializedName("downloadThreads")
|
||||
private IntegerProperty downloadThreads = new SimpleIntegerProperty(64);
|
||||
@@ -393,8 +393,8 @@ public final class Config implements Cloneable, Observable {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double height) {
|
||||
this.x.set(height);
|
||||
public void setX(double x) {
|
||||
this.x.set(x);
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
@@ -405,8 +405,8 @@ public final class Config implements Cloneable, Observable {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double height) {
|
||||
this.y.set(height);
|
||||
public void setY(double y) {
|
||||
this.y.set(y);
|
||||
}
|
||||
|
||||
public double getWidth() {
|
||||
|
||||
@@ -18,16 +18,11 @@
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
@@ -46,94 +41,66 @@ final class ConfigUpgrader {
|
||||
* @param rawContent raw json content of the config settings without modification
|
||||
*/
|
||||
static void upgradeConfig(Config deserialized, String rawContent) {
|
||||
if (deserialized.getConfigVersion() == CURRENT_VERSION) {
|
||||
int configVersion = deserialized.getConfigVersion();
|
||||
|
||||
if (configVersion == CURRENT_VERSION) {
|
||||
return;
|
||||
}
|
||||
|
||||
int configVersion = deserialized.getConfigVersion();
|
||||
if (configVersion > CURRENT_VERSION) {
|
||||
LOG.log(Level.WARNING, String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", CURRENT_VERSION, configVersion));
|
||||
deserialized.setConfigVersion(CURRENT_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, String.format("Updating configuration from %d to %d.", configVersion, CURRENT_VERSION));
|
||||
Map<?, ?> unmodifiableRawJson = Collections.unmodifiableMap(new Gson().<Map<?, ?>>fromJson(rawContent, Map.class));
|
||||
for (Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>> dfu : collectDFU()) {
|
||||
if (configVersion < dfu.getKey()) {
|
||||
dfu.getValue().accept(deserialized, unmodifiableRawJson);
|
||||
configVersion = dfu.getKey();
|
||||
Map<?, ?> rawJson = Collections.unmodifiableMap(new Gson().<Map<?, ?>>fromJson(rawContent, Map.class));
|
||||
|
||||
if (configVersion < 1) {
|
||||
// Upgrade configuration of HMCL 2.x: Convert OfflineAccounts whose stored uuid is important.
|
||||
tryCast(rawJson.get("auth"), Map.class).ifPresent(auth -> {
|
||||
tryCast(auth.get("offline"), Map.class).ifPresent(offline -> {
|
||||
String selected = rawJson.containsKey("selectedAccount") ? null
|
||||
: tryCast(offline.get("IAuthenticator_UserName"), String.class).orElse(null);
|
||||
|
||||
tryCast(offline.get("uuidMap"), Map.class).ifPresent(uuidMap -> {
|
||||
((Map<?, ?>) uuidMap).forEach((key, value) -> {
|
||||
Map<Object, Object> storage = new HashMap<>();
|
||||
storage.put("type", "offline");
|
||||
storage.put("username", key);
|
||||
storage.put("uuid", value);
|
||||
if (key.equals(selected)) {
|
||||
storage.put("selected", true);
|
||||
}
|
||||
deserialized.getAccountStorages().add(storage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upgrade configuration of HMCL earlier than 3.1.70
|
||||
if (!rawJson.containsKey("commonDirType"))
|
||||
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
|
||||
if (!rawJson.containsKey("backgroundType"))
|
||||
deserialized.setBackgroundImageType(StringUtils.isNotBlank(deserialized.getBackgroundImage()) ? EnumBackgroundImage.CUSTOM : EnumBackgroundImage.DEFAULT);
|
||||
if (!rawJson.containsKey("hasProxy"))
|
||||
deserialized.setHasProxy(StringUtils.isNotBlank(deserialized.getProxyHost()));
|
||||
if (!rawJson.containsKey("hasProxyAuth"))
|
||||
deserialized.setHasProxyAuth(StringUtils.isNotBlank(deserialized.getProxyUser()));
|
||||
|
||||
if (!rawJson.containsKey("downloadType")) {
|
||||
tryCast(rawJson.get("downloadtype"), Number.class)
|
||||
.map(Number::intValue)
|
||||
.ifPresent(id -> {
|
||||
if (id == 0) {
|
||||
deserialized.setDownloadType("mojang");
|
||||
} else if (id == 1) {
|
||||
deserialized.setDownloadType("bmclapi");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
deserialized.setConfigVersion(CURRENT_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Initialize the dfu of HMCL. Feel free to use lambda as all the lambda here would not be initialized unless HMCL needs to update the configuration.
|
||||
* For each item in this list, it should be a Map.Entry.</p>
|
||||
*
|
||||
* <p>The key should be a version number. All the configuration with a version number which is less than the specific one will be applied to this upgrader.</p>
|
||||
* <p>The value should be the upgrader. The configuration which is waited to being processed, and the raw unmodifiable value of the json from the configuration file.</p>
|
||||
* <p>The return value should a target version number of this item.</p>
|
||||
*
|
||||
* <p>The last item must return CURRENT_VERSION, as the config file should always being updated to the latest version.</p>
|
||||
*/
|
||||
private static List<Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>>> collectDFU() {
|
||||
List<Map.Entry<Integer, BiConsumer<Config, Map<?, ?>>>> dfu = Lang.immutableListOf(
|
||||
Pair.pair(1, (deserialized, rawJson) -> {
|
||||
// Upgrade configuration of HMCL 2.x: Convert OfflineAccounts whose stored uuid is important.
|
||||
tryCast(rawJson.get("auth"), Map.class).ifPresent(auth -> {
|
||||
tryCast(auth.get("offline"), Map.class).ifPresent(offline -> {
|
||||
String selected = rawJson.containsKey("selectedAccount") ? null
|
||||
: tryCast(offline.get("IAuthenticator_UserName"), String.class).orElse(null);
|
||||
|
||||
tryCast(offline.get("uuidMap"), Map.class).ifPresent(uuidMap -> {
|
||||
((Map<?, ?>) uuidMap).forEach((key, value) -> {
|
||||
Map<Object, Object> storage = new HashMap<>();
|
||||
storage.put("type", "offline");
|
||||
storage.put("username", key);
|
||||
storage.put("uuid", value);
|
||||
if (key.equals(selected)) {
|
||||
storage.put("selected", true);
|
||||
}
|
||||
deserialized.getAccountStorages().add(storage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Upgrade configuration of HMCL earlier than 3.1.70
|
||||
if (!rawJson.containsKey("commonDirType"))
|
||||
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
|
||||
if (!rawJson.containsKey("backgroundType"))
|
||||
deserialized.setBackgroundImageType(StringUtils.isNotBlank(deserialized.getBackgroundImage()) ? EnumBackgroundImage.CUSTOM : EnumBackgroundImage.DEFAULT);
|
||||
if (!rawJson.containsKey("hasProxy"))
|
||||
deserialized.setHasProxy(StringUtils.isNotBlank(deserialized.getProxyHost()));
|
||||
if (!rawJson.containsKey("hasProxyAuth"))
|
||||
deserialized.setHasProxyAuth(StringUtils.isNotBlank(deserialized.getProxyUser()));
|
||||
|
||||
if (!rawJson.containsKey("downloadType")) {
|
||||
tryCast(rawJson.get("downloadtype"), Number.class)
|
||||
.map(Number::intValue)
|
||||
.ifPresent(id -> {
|
||||
if (id == 0) {
|
||||
deserialized.setDownloadType("mojang");
|
||||
} else if (id == 1) {
|
||||
deserialized.setDownloadType("bmclapi");
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
Pair.pair(2, (deserialized, rawJson) -> {
|
||||
deserialized.setX(0.5D - deserialized.getWidth() / Controllers.SCREEN.getBounds().getWidth() / 2);
|
||||
deserialized.setY(0.5D - deserialized.getHeight() / Controllers.SCREEN.getBounds().getHeight() / 2);
|
||||
})
|
||||
);
|
||||
|
||||
if (dfu.get(dfu.size() - 1).getKey() != CURRENT_VERSION) {
|
||||
throw new IllegalStateException("The last dfu must adapt all the config version below CURRENT_VERSION");
|
||||
}
|
||||
|
||||
return dfu;
|
||||
}
|
||||
}
|
||||
@@ -55,10 +55,10 @@ public final class DownloadProviders {
|
||||
|
||||
private static final MojangDownloadProvider MOJANG;
|
||||
private static final BMCLAPIDownloadProvider BMCLAPI;
|
||||
private static final BMCLAPIDownloadProvider MCBBS;
|
||||
// private static final BMCLAPIDownloadProvider MCBBS;
|
||||
|
||||
public static final String DEFAULT_PROVIDER_ID = "balanced";
|
||||
public static final String DEFAULT_RAW_PROVIDER_ID = "mcbbs";
|
||||
public static final String DEFAULT_RAW_PROVIDER_ID = "bmclapi";
|
||||
|
||||
private static final InvalidationListener observer;
|
||||
|
||||
@@ -69,21 +69,27 @@ public final class DownloadProviders {
|
||||
|
||||
MOJANG = new MojangDownloadProvider();
|
||||
BMCLAPI = new BMCLAPIDownloadProvider(bmclapiRoot);
|
||||
MCBBS = new BMCLAPIDownloadProvider("https://download.mcbbs.net");
|
||||
// MCBBS = new BMCLAPIDownloadProvider("https://download.mcbbs.net");
|
||||
rawProviders = mapOf(
|
||||
pair("mojang", MOJANG),
|
||||
pair("bmclapi", BMCLAPI),
|
||||
pair("mcbbs", MCBBS)
|
||||
pair("bmclapi", BMCLAPI)
|
||||
// pair("mcbbs", MCBBS)
|
||||
);
|
||||
|
||||
AdaptedDownloadProvider fileProvider = new AdaptedDownloadProvider();
|
||||
fileProvider.setDownloadProviderCandidates(Arrays.asList(MCBBS, BMCLAPI, MOJANG));
|
||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(MOJANG, MCBBS, BMCLAPI);
|
||||
fileProvider.setDownloadProviderCandidates(Arrays.asList(
|
||||
// MCBBS,
|
||||
BMCLAPI,
|
||||
MOJANG));
|
||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(
|
||||
MOJANG,
|
||||
// MCBBS,
|
||||
BMCLAPI);
|
||||
|
||||
providersById = mapOf(
|
||||
pair("official", new AutoDownloadProvider(MOJANG, fileProvider)),
|
||||
pair("balanced", new AutoDownloadProvider(balanced, fileProvider)),
|
||||
pair("mirror", new AutoDownloadProvider(MCBBS, fileProvider)));
|
||||
pair("mirror", new AutoDownloadProvider(BMCLAPI /* MCBBS */, fileProvider)));
|
||||
|
||||
observer = FXUtils.observeWeak(() -> {
|
||||
FetchTask.setDownloadExecutorConcurrency(
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -644,11 +644,11 @@ public final class VersionSetting implements Cloneable {
|
||||
launcherVisibilityProperty.set(launcherVisibility);
|
||||
}
|
||||
|
||||
public Task<JavaVersion> getJavaVersion(VersionNumber gameVersion, Version version) {
|
||||
public Task<JavaVersion> getJavaVersion(GameVersionNumber gameVersion, Version version) {
|
||||
return getJavaVersion(gameVersion, version, true);
|
||||
}
|
||||
|
||||
public Task<JavaVersion> getJavaVersion(VersionNumber gameVersion, Version version, boolean checkJava) {
|
||||
public Task<JavaVersion> getJavaVersion(GameVersionNumber gameVersion, Version version, boolean checkJava) {
|
||||
return Task.runAsync(Schedulers.javafx(), () -> {
|
||||
if (StringUtils.isBlank(getJava())) {
|
||||
setJava(StringUtils.isBlank(getJavaDir()) ? "Auto" : "Custom");
|
||||
|
||||
@@ -37,10 +37,7 @@ import javafx.stage.StageStyle;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.EnumCommonDirectory;
|
||||
import org.jackhuang.hmcl.setting.Profiles;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.setting.*;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.account.AccountListPage;
|
||||
@@ -68,6 +65,8 @@ import static org.jackhuang.hmcl.ui.FXUtils.newBuiltinImage;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Controllers {
|
||||
public static final int MIN_WIDTH = 800 + 2 + 16; // bg width + border width*2 + shadow width*2
|
||||
public static final int MIN_HEIGHT = 450 + 2 + 40 + 16; // bg height + border width*2 + toolbar height + shadow width*2
|
||||
public static final Screen SCREEN = Screen.getPrimary();
|
||||
private static InvalidationListener stageSizeChangeListener;
|
||||
private static DoubleProperty stageX = new SimpleDoubleProperty();
|
||||
@@ -205,35 +204,37 @@ public final class Controllers {
|
||||
|
||||
WeakInvalidationListener weakListener = new WeakInvalidationListener(stageSizeChangeListener);
|
||||
|
||||
double initHeight = config().getHeight();
|
||||
double initWidth = config().getWidth();
|
||||
double initX = config().getX() * SCREEN.getBounds().getWidth();
|
||||
double initY = config().getY() * SCREEN.getBounds().getHeight();
|
||||
double initWidth = Math.max(MIN_WIDTH, config().getWidth());
|
||||
double initHeight = Math.max(MIN_HEIGHT, config().getHeight());
|
||||
|
||||
{
|
||||
double initX = config().getX() * SCREEN.getBounds().getWidth();
|
||||
double initY = config().getY() * SCREEN.getBounds().getHeight();
|
||||
|
||||
boolean invalid = true;
|
||||
double border = 20D;
|
||||
for (Screen screen : Screen.getScreens()) {
|
||||
Rectangle2D bound = screen.getBounds();
|
||||
|
||||
if (bound.getMinX() + border <= initX + initWidth && initX <= bound.getMaxX() - border && bound.getMinY() + border <= initY + initHeight && initY <= bound.getMaxY() - border) {
|
||||
if (bound.getMinX() + border <= initX + initWidth && initX <= bound.getMaxX() - border && bound.getMinY() + border <= initY && initY <= bound.getMaxY() - border) {
|
||||
invalid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
initX = (0.5D - initWidth / Controllers.SCREEN.getBounds().getWidth() / 2) * SCREEN.getBounds().getWidth();
|
||||
initY = (0.5D - initHeight / Controllers.SCREEN.getBounds().getHeight() / 2) * SCREEN.getBounds().getHeight();
|
||||
initX = (0.5D - initWidth / SCREEN.getBounds().getWidth() / 2) * SCREEN.getBounds().getWidth();
|
||||
initY = (0.5D - initHeight / SCREEN.getBounds().getHeight() / 2) * SCREEN.getBounds().getHeight();
|
||||
}
|
||||
|
||||
stage.setX(initX);
|
||||
stage.setY(initY);
|
||||
stageX.set(initX);
|
||||
stageY.set(initY);
|
||||
}
|
||||
|
||||
stage.setX(initX);
|
||||
stage.setY(initY);
|
||||
stage.setHeight(initHeight);
|
||||
stage.setWidth(initWidth);
|
||||
stageX.set(initX);
|
||||
stageY.set(initY);
|
||||
stageHeight.set(initHeight);
|
||||
stageWidth.set(initWidth);
|
||||
stage.xProperty().addListener(weakListener);
|
||||
@@ -255,8 +256,8 @@ public final class Controllers {
|
||||
|
||||
scene = new Scene(decorator.getDecorator());
|
||||
scene.setFill(Color.TRANSPARENT);
|
||||
stage.setMinHeight(450 + 2 + 40 + 16); // bg height + border width*2 + toolbar height + shadow width*2
|
||||
stage.setMinWidth(800 + 2 + 16); // bg width + border width*2 + shadow width*2
|
||||
stage.setMinWidth(MIN_WIDTH);
|
||||
stage.setMinHeight(MIN_HEIGHT);
|
||||
decorator.getDecorator().prefWidthProperty().bind(scene.widthProperty());
|
||||
decorator.getDecorator().prefHeightProperty().bind(scene.heightProperty());
|
||||
scene.getStylesheets().setAll(Theme.getTheme().getStylesheets(config().getLauncherFontFamily()));
|
||||
|
||||
@@ -28,7 +28,7 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.countly.CrashReport;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.newBuiltinImage;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -907,7 +907,9 @@ public final class FXUtils {
|
||||
content.putString(text);
|
||||
Clipboard.getSystemClipboard().setContent(content);
|
||||
|
||||
Controllers.showToast(i18n("message.copied"));
|
||||
if (!Controllers.isStopped()) {
|
||||
Controllers.showToast(i18n("message.copied"));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Node> parseSegment(String segment, Consumer<String> hyperlinkAction) {
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.setting.VersionIconType;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -170,7 +170,7 @@ public class InstallerItem extends Control {
|
||||
public InstallerItemGroup(String gameVersion) {
|
||||
mutualIncompatible(forge, fabric, quilt, neoForge, liteLoader);
|
||||
addIncompatibles(optiFine, fabric, quilt, neoForge);
|
||||
addIncompatibles(fabricApi, forge, quilt, quiltApi, neoForge, liteLoader, optiFine);
|
||||
addIncompatibles(fabricApi, forge, quiltApi, neoForge, liteLoader, optiFine);
|
||||
addIncompatibles(quiltApi, forge, fabric, fabricApi, neoForge, liteLoader, optiFine);
|
||||
|
||||
InvalidationListener listener = o -> {
|
||||
@@ -204,7 +204,7 @@ public class InstallerItem extends Control {
|
||||
|
||||
if (gameVersion == null) {
|
||||
this.libraries = new InstallerItem[]{game, forge, neoForge, liteLoader, optiFine, fabric, fabricApi, quilt, quiltApi};
|
||||
} else if (VersionNumber.compare(gameVersion, "1.13") < 0) {
|
||||
} else if (GameVersionNumber.compare(gameVersion, "1.13") < 0) {
|
||||
this.libraries = new InstallerItem[]{game, forge, liteLoader, optiFine};
|
||||
} else {
|
||||
this.libraries = new InstallerItem[]{game, forge, neoForge, optiFine, fabric, fabricApi, quilt, quiltApi};
|
||||
|
||||
@@ -25,7 +25,7 @@ import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.WeakListenerHolder;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
@@ -40,7 +40,7 @@ import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MultiFileItem<T> extends VBox {
|
||||
public final class MultiFileItem<T> extends VBox {
|
||||
private final ObjectProperty<T> selectedData = new SimpleObjectProperty<>(this, "selectedData");
|
||||
private final ObjectProperty<T> fallbackData = new SimpleObjectProperty<>(this, "fallbackData");
|
||||
|
||||
@@ -176,8 +176,8 @@ public class MultiFileItem<T> extends VBox {
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringOption<T> extends Option<T> {
|
||||
private JFXTextField customField = new JFXTextField();
|
||||
public static final class StringOption<T> extends Option<T> {
|
||||
private final JFXTextField customField = new JFXTextField();
|
||||
|
||||
public StringOption(String title, T data) {
|
||||
super(title, data);
|
||||
@@ -196,7 +196,7 @@ public class MultiFileItem<T> extends VBox {
|
||||
}
|
||||
|
||||
public StringOption<T> bindBidirectional(Property<String> property) {
|
||||
customField.textProperty().bindBidirectional(property);
|
||||
FXUtils.bindString(customField, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -230,8 +230,8 @@ public class MultiFileItem<T> extends VBox {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileOption<T> extends Option<T> {
|
||||
private FileSelector selector = new FileSelector();
|
||||
public static final class FileOption<T> extends Option<T> {
|
||||
private final FileSelector selector = new FileSelector();
|
||||
|
||||
public FileOption(String title, T data) {
|
||||
super(title, data);
|
||||
|
||||
@@ -298,7 +298,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
|
||||
content.setTitle(remoteVersion.getSelfVersion());
|
||||
if (remoteVersion.getReleaseDate() != null) {
|
||||
content.setSubtitle(formatDateTime(remoteVersion.getReleaseDate().toInstant()));
|
||||
content.setSubtitle(formatDateTime(remoteVersion.getReleaseDate()));
|
||||
} else {
|
||||
content.setSubtitle(null);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class AboutPage extends StackPane {
|
||||
ComponentList about = new ComponentList();
|
||||
{
|
||||
IconedTwoLineListItem launcher = new IconedTwoLineListItem();
|
||||
launcher.setImage(FXUtils.newBuiltinImage("/assets/img/craft_table.png"));
|
||||
launcher.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
|
||||
launcher.setTitle("Hello Minecraft! Launcher");
|
||||
launcher.setSubtitle(Metadata.VERSION);
|
||||
launcher.setExternalLink("https://hmcl.huangyuhui.net");
|
||||
@@ -85,11 +85,11 @@ public class AboutPage extends StackPane {
|
||||
redLnn.setImage(FXUtils.newBuiltinImage("/assets/img/red_lnn.png"));
|
||||
redLnn.setSubtitle(i18n("about.thanks_to.red_lnn.statement"));
|
||||
|
||||
IconedTwoLineListItem mcbbs = new IconedTwoLineListItem();
|
||||
mcbbs.setImage(FXUtils.newBuiltinImage("/assets/img/chest.png"));
|
||||
mcbbs.setTitle(i18n("about.thanks_to.mcbbs"));
|
||||
mcbbs.setSubtitle(i18n("about.thanks_to.mcbbs.statement"));
|
||||
mcbbs.setExternalLink("https://www.mcbbs.net/");
|
||||
// IconedTwoLineListItem mcbbs = new IconedTwoLineListItem();
|
||||
// mcbbs.setImage(FXUtils.newBuiltinImage("/assets/img/chest.png"));
|
||||
// mcbbs.setTitle(i18n("about.thanks_to.mcbbs"));
|
||||
// mcbbs.setSubtitle(i18n("about.thanks_to.mcbbs.statement"));
|
||||
// mcbbs.setExternalLink("https://www.mcbbs.net/");
|
||||
|
||||
IconedTwoLineListItem mcmod = new IconedTwoLineListItem();
|
||||
mcmod.setImage(FXUtils.newBuiltinImage("/assets/img/mcmod.png"));
|
||||
@@ -104,12 +104,12 @@ public class AboutPage extends StackPane {
|
||||
contributors.setExternalLink("https://github.com/HMCL-dev/HMCL/graphs/contributors");
|
||||
|
||||
IconedTwoLineListItem users = new IconedTwoLineListItem();
|
||||
users.setImage(FXUtils.newBuiltinImage("/assets/img/craft_table.png"));
|
||||
users.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
|
||||
users.setTitle(i18n("about.thanks_to.users"));
|
||||
users.setSubtitle(i18n("about.thanks_to.users.statement"));
|
||||
users.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
|
||||
|
||||
thanks.getContent().setAll(yushijinhun, bangbang93, glavo, zkitefly ,mcbbs, mcmod, gamerteam, redLnn, contributors, users);
|
||||
thanks.getContent().setAll(yushijinhun, bangbang93, glavo, zkitefly, /* mcbbs, */ mcmod, gamerteam, redLnn, contributors, users);
|
||||
}
|
||||
|
||||
ComponentList dep = new ComponentList();
|
||||
|
||||
@@ -53,9 +53,9 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.ui.versions.GameItem;
|
||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
||||
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
|
||||
@@ -43,15 +43,16 @@ import org.jackhuang.hmcl.ui.nbt.NBTEditorPage;
|
||||
import org.jackhuang.hmcl.ui.nbt.NBTHelper;
|
||||
import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.versions.Versions;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Level;
|
||||
@@ -118,9 +119,8 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
List<Version> children = repository.getVersions().parallelStream()
|
||||
.filter(version -> !version.isHidden())
|
||||
.sorted(Comparator
|
||||
.comparing((Version version) -> version.getReleaseTime() == null ? new Date(0L)
|
||||
: version.getReleaseTime())
|
||||
.thenComparing(a -> VersionNumber.asVersion(a.getId())))
|
||||
.comparing((Version version) -> Lang.requireNonNullElse(version.getReleaseTime(), Instant.EPOCH))
|
||||
.thenComparing(version -> VersionNumber.asVersion(repository.getGameVersion(version).orElse(version.getId()))))
|
||||
.collect(Collectors.toList());
|
||||
runInFX(() -> {
|
||||
if (profile == Profiles.getSelectedProfile())
|
||||
|
||||
@@ -27,10 +27,10 @@ import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChannel;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateHandler;
|
||||
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChannel;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -128,7 +128,7 @@ public class SponsorPage extends StackPane {
|
||||
private final String name;
|
||||
|
||||
@SerializedName("create_time")
|
||||
private final Date createTime;
|
||||
private final Instant createTime;
|
||||
|
||||
@SerializedName("money")
|
||||
private final BigDecimal money;
|
||||
@@ -140,10 +140,10 @@ public class SponsorPage extends StackPane {
|
||||
private final String afdianId;
|
||||
|
||||
public Sponsor() {
|
||||
this("", new Date(), BigDecimal.ZERO, "", "");
|
||||
this("", Instant.now(), BigDecimal.ZERO, "", "");
|
||||
}
|
||||
|
||||
public Sponsor(String name, Date createTime, BigDecimal money, String contact, String afdianId) {
|
||||
public Sponsor(String name, Instant createTime, BigDecimal money, String contact, String afdianId) {
|
||||
this.name = name;
|
||||
this.createTime = createTime;
|
||||
this.money = money;
|
||||
@@ -155,7 +155,7 @@ public class SponsorPage extends StackPane {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
public Instant getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||
@@ -57,8 +56,8 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -163,24 +162,23 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
retrySearch = null;
|
||||
setLoading(true);
|
||||
setFailed(false);
|
||||
File versionJar = StringUtils.isNotBlank(version.get().getVersion())
|
||||
? version.get().getProfile().getRepository().getVersionJar(version.get().getVersion())
|
||||
: null;
|
||||
|
||||
if (executor != null && !executor.isCancelled()) {
|
||||
executor.cancel();
|
||||
}
|
||||
|
||||
executor = Task.supplyAsync(() -> {
|
||||
String gameVersion;
|
||||
if (StringUtils.isBlank(version.get().getVersion())) {
|
||||
gameVersion = userGameVersion;
|
||||
Profile.ProfileVersion version = this.version.get();
|
||||
if (StringUtils.isBlank(version.getVersion())) {
|
||||
return userGameVersion;
|
||||
} else {
|
||||
gameVersion = GameVersion.minecraftVersion(versionJar).orElse("");
|
||||
return StringUtils.isNotBlank(version.getVersion())
|
||||
? version.getProfile().getRepository().getGameVersion(version.getVersion()).orElse("")
|
||||
: "";
|
||||
}
|
||||
return gameVersion;
|
||||
}).thenApplyAsync(gameVersion -> {
|
||||
return 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) -> {
|
||||
setLoading(false);
|
||||
if (exception == null) {
|
||||
items.setAll(result.getResults().collect(Collectors.toList()));
|
||||
@@ -295,7 +293,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
JFXComboBox<String> gameVersionField = new JFXComboBox<>();
|
||||
gameVersionField.setMaxWidth(Double.MAX_VALUE);
|
||||
gameVersionField.setEditable(true);
|
||||
gameVersionField.getItems().setAll(RemoteModRepository.DEFAULT_GAME_VERSIONS);
|
||||
gameVersionField.getItems().setAll(GameVersionNumber.getDefaultGameVersions());
|
||||
Label lblGameVersion = new Label(i18n("world.game_version"));
|
||||
searchPane.addRow(rowIndex++, new Label(i18n("mods.name")), nameField, lblGameVersion, gameVersionField);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
@@ -309,7 +309,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
}
|
||||
|
||||
for (String gameVersion : control.versions.keys().stream()
|
||||
.sorted(Collections.reverseOrder(VersionNumber::compare))
|
||||
.sorted(Collections.reverseOrder(GameVersionNumber::compare))
|
||||
.collect(Collectors.toList())) {
|
||||
ComponentList sublist = new ComponentList(() ->
|
||||
control.versions.get(gameVersion).stream()
|
||||
@@ -382,7 +382,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
HBox.setHgrow(content, Priority.ALWAYS);
|
||||
content.setTitle(dataItem.getName());
|
||||
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished().toInstant()));
|
||||
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished()));
|
||||
|
||||
switch (dataItem.getVersionType()) {
|
||||
case Alpha:
|
||||
|
||||
@@ -223,7 +223,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
}
|
||||
} else {
|
||||
String lowerQueryString = queryString.toLowerCase(Locale.ROOT);
|
||||
predicate = s -> s.contains(lowerQueryString);
|
||||
predicate = s -> s.toLowerCase(Locale.ROOT).contains(lowerQueryString);
|
||||
}
|
||||
|
||||
// Do we need to search in the background thread?
|
||||
|
||||
@@ -18,13 +18,11 @@
|
||||
package org.jackhuang.hmcl.ui.versions;
|
||||
|
||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||
import org.jackhuang.hmcl.upgrade.resource.RemoteResourceManager;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -38,19 +36,19 @@ import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
* @see <a href="https://www.mcmod.cn">mcmod.cn</a>
|
||||
*/
|
||||
public enum ModTranslations {
|
||||
MOD("/assets/mod_data.txt", "translation", "mod_data", "1") {
|
||||
MOD("/assets/mod_data.txt") {
|
||||
@Override
|
||||
public String getMcmodUrl(Mod mod) {
|
||||
return String.format("https://www.mcmod.cn/class/%s.html", mod.getMcmod());
|
||||
}
|
||||
},
|
||||
MODPACK("/assets/modpack_data.txt", "translation", "modpack_data", "1") {
|
||||
MODPACK("/assets/modpack_data.txt") {
|
||||
@Override
|
||||
public String getMcmodUrl(Mod mod) {
|
||||
return String.format("https://www.mcmod.cn/modpack/%s.html", mod.getMcmod());
|
||||
}
|
||||
},
|
||||
EMPTY("", "", "", "") {
|
||||
EMPTY("") {
|
||||
@Override
|
||||
public String getMcmodUrl(Mod mod) {
|
||||
return "";
|
||||
@@ -68,18 +66,15 @@ public enum ModTranslations {
|
||||
}
|
||||
}
|
||||
|
||||
private final String defaultResourceName;
|
||||
private final RemoteResourceManager.RemoteResourceKey remoteResourceKey;
|
||||
private final String resourceName;
|
||||
private List<Mod> mods;
|
||||
private Map<String, Mod> modIdMap; // mod id -> mod
|
||||
private Map<String, Mod> curseForgeMap; // curseforge id -> mod
|
||||
private List<Pair<String, Mod>> keywords;
|
||||
private int maxKeywordLength = -1;
|
||||
|
||||
ModTranslations(String defaultResourceName, String namespace, String name, String version) {
|
||||
this.defaultResourceName = defaultResourceName;
|
||||
|
||||
remoteResourceKey = RemoteResourceManager.get(namespace, name, version, () -> ModTranslations.class.getResourceAsStream(defaultResourceName));
|
||||
ModTranslations(String resourceName) {
|
||||
this.resourceName = resourceName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -120,24 +115,19 @@ public enum ModTranslations {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean loaded() {
|
||||
private boolean loadFromResource() {
|
||||
if (mods != null) return true;
|
||||
if (StringUtils.isBlank(defaultResourceName)) {
|
||||
if (StringUtils.isBlank(resourceName)) {
|
||||
mods = Collections.emptyList();
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = remoteResourceKey.getResource();
|
||||
if (inputStream == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String modData = IOUtils.readFullyAsString(inputStream);
|
||||
String modData = IOUtils.readFullyAsString(ModTranslations.class.getResourceAsStream(resourceName));
|
||||
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Failed to load " + defaultResourceName, e);
|
||||
LOG.log(Level.WARNING, "Failed to load " + resourceName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +138,7 @@ public enum ModTranslations {
|
||||
}
|
||||
|
||||
if (mods == null) {
|
||||
if (!loaded()) return false;
|
||||
if (!loadFromResource()) return false;
|
||||
}
|
||||
|
||||
curseForgeMap = new HashMap<>();
|
||||
@@ -166,7 +156,7 @@ public enum ModTranslations {
|
||||
}
|
||||
|
||||
if (mods == null) {
|
||||
if (!loaded()) return false;
|
||||
if (!loadFromResource()) return false;
|
||||
}
|
||||
|
||||
modIdMap = new HashMap<>();
|
||||
@@ -186,7 +176,7 @@ public enum ModTranslations {
|
||||
}
|
||||
|
||||
if (mods == null) {
|
||||
if (!loaded()) return false;
|
||||
if (!loadFromResource()) return false;
|
||||
}
|
||||
|
||||
keywords = new ArrayList<>();
|
||||
|
||||
@@ -46,7 +46,7 @@ import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
@@ -611,10 +611,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
|
||||
Task.composeAsync(Schedulers.javafx(), () -> {
|
||||
if (versionId == null) {
|
||||
return versionSetting.getJavaVersion(VersionNumber.asVersion("Unknown"), null);
|
||||
return versionSetting.getJavaVersion(GameVersionNumber.unknown(), null);
|
||||
} else {
|
||||
return versionSetting.getJavaVersion(
|
||||
VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(versionId)).orElse("Unknown")),
|
||||
GameVersionNumber.asGameVersion(profile.getRepository().getGameVersion(versionId)),
|
||||
profile.getRepository().getVersion(versionId));
|
||||
}
|
||||
}).thenAcceptAsync(Schedulers.javafx(), javaVersion -> javaSublist.setSubtitle(Optional.ofNullable(javaVersion)
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.game.World;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
@@ -94,8 +94,7 @@ public class WorldListItem extends Control {
|
||||
|
||||
public void manageDatapacks() {
|
||||
if (world.getGameVersion() == null || // old game will not write game version to level.dat
|
||||
(VersionNumber.isIntVersionNumber(world.getGameVersion()) // we don't parse snapshot version
|
||||
&& VersionNumber.asVersion(world.getGameVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0)) {
|
||||
GameVersionNumber.compare(world.getGameVersion(), "1.13") < 0) {
|
||||
Controllers.dialog(i18n("world.datapack.1_13"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class WorldListPage extends ListPageBase<WorldListItem> implements Versio
|
||||
return Arrays.asList(chkShowAll,
|
||||
createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh),
|
||||
createToolbarButton2(i18n("world.add"), SVG.PLUS, skinnable::add),
|
||||
createToolbarButton2(i18n("world.download"), SVG.PLUS, skinnable::download));
|
||||
createToolbarButton2(i18n("world.download"), SVG.DOWNLOAD_OUTLINE, skinnable::download));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.util.Pack200Utils;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
|
||||
+5
-6
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -32,13 +32,12 @@ import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.versioning.VersionNumber.asVersion;
|
||||
|
||||
public final class UpdateChecker {
|
||||
private UpdateChecker() {}
|
||||
|
||||
private static ObjectProperty<RemoteVersion> latestVersion = new SimpleObjectProperty<>();
|
||||
private static BooleanBinding outdated = Bindings.createBooleanBinding(
|
||||
private static final ObjectProperty<RemoteVersion> latestVersion = new SimpleObjectProperty<>();
|
||||
private static final BooleanBinding outdated = Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
RemoteVersion latest = latestVersion.get();
|
||||
if (latest == null || isDevelopmentVersion(Metadata.VERSION)) {
|
||||
@@ -46,11 +45,11 @@ public final class UpdateChecker {
|
||||
} else {
|
||||
// We can update from development version to stable version,
|
||||
// which can be downgrading.
|
||||
return asVersion(latest.getVersion()).compareTo(asVersion(Metadata.VERSION)) != 0;
|
||||
return !latest.getVersion().equals(Metadata.VERSION);
|
||||
}
|
||||
},
|
||||
latestVersion);
|
||||
private static ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false);
|
||||
private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false);
|
||||
|
||||
public static void init() {
|
||||
requestCheckUpdate(UpdateChannel.getChannel());
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 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.upgrade.hmcl;
|
||||
package org.jackhuang.hmcl.upgrade;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParseException;
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 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.upgrade.resource;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.versions.ModTranslations;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class RemoteResourceManager {
|
||||
private RemoteResourceManager() {
|
||||
}
|
||||
|
||||
private static final class RemoteResource {
|
||||
@SerializedName("sha1")
|
||||
private final String sha1;
|
||||
|
||||
@SerializedName("urls")
|
||||
private final String[] urls;
|
||||
|
||||
private transient byte[] data = null;
|
||||
|
||||
private RemoteResource(String sha1, String[] urls) {
|
||||
this.sha1 = sha1;
|
||||
this.urls = urls;
|
||||
}
|
||||
|
||||
public void download(Path path, Runnable callback) {
|
||||
if (data != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
new FileDownloadTask(Arrays.stream(urls).map(NetworkUtils::toURL).collect(Collectors.toList()), path.toFile(), new FileDownloadTask.IntegrityCheck("SHA-1", sha1))
|
||||
.whenComplete(Schedulers.defaultScheduler(), (result, exception) -> {
|
||||
if (exception != null) {
|
||||
data = Files.readAllBytes(path);
|
||||
callback.run();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RemoteResourceKey {
|
||||
private final String namespace;
|
||||
private final String name;
|
||||
private final String version;
|
||||
private final Path cachePath;
|
||||
private final ExceptionalSupplier<InputStream, IOException> localResourceSupplier;
|
||||
private String localResourceSha1 = null;
|
||||
|
||||
public RemoteResourceKey(String namespace, String name, String version, ExceptionalSupplier<InputStream, IOException> localResourceSupplier) {
|
||||
this.namespace = namespace;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
this.localResourceSupplier = localResourceSupplier;
|
||||
|
||||
this.cachePath = Metadata.HMCL_DIRECTORY.resolve("remoteResources").resolve(namespace).resolve(name).resolve(version).resolve(String.format("%s-%s-%s.resource", namespace, name, version));
|
||||
}
|
||||
|
||||
private InputStream getLocalResource() throws IOException {
|
||||
if (Files.isReadable(cachePath)) {
|
||||
return Files.newInputStream(cachePath);
|
||||
}
|
||||
return localResourceSupplier.get();
|
||||
}
|
||||
|
||||
private String getLocalResourceSha1() throws IOException {
|
||||
if (localResourceSha1 == null) {
|
||||
localResourceSha1 = DigestUtils.digestToString("SHA-1", IOUtils.readFullyAsByteArray(getLocalResource()));
|
||||
}
|
||||
|
||||
return localResourceSha1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RemoteResource getRemoteResource() {
|
||||
return Optional.ofNullable(remoteResources.get(namespace)).map(map -> map.get(name)).map(map -> map.get(version)).orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InputStream getResource() throws IOException {
|
||||
RemoteResource remoteResource = getRemoteResource();
|
||||
|
||||
if (remoteResource == null) {
|
||||
return getLocalResource();
|
||||
}
|
||||
|
||||
if (remoteResource.sha1.equals(getLocalResourceSha1())) {
|
||||
return getLocalResource();
|
||||
}
|
||||
|
||||
if (remoteResource.data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream(remoteResource.data);
|
||||
}
|
||||
|
||||
public void downloadRemoteResourceIfNecessary() throws IOException {
|
||||
RemoteResource remoteResource = getRemoteResource();
|
||||
|
||||
if (remoteResource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteResource.sha1.equals(getLocalResourceSha1())) {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteResource.download(cachePath, () -> localResourceSha1 = null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
RemoteResourceKey that = (RemoteResourceKey) o;
|
||||
|
||||
if (!namespace.equals(that.namespace)) return false;
|
||||
if (!name.equals(that.name)) return false;
|
||||
return version.equals(that.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = namespace.hashCode();
|
||||
result = 31 * result + name.hashCode();
|
||||
result = 31 * result + version.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, Map<String, Map<String, RemoteResource>>> remoteResources = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<String, RemoteResourceKey> keys = new ConcurrentHashMap<>();
|
||||
|
||||
public static void init() {
|
||||
Task.<Map<String, Map<String, Map<String, RemoteResource>>>>supplyAsync(() ->
|
||||
IntegrityChecker.isSelfVerified() ? HttpRequest.GET(Metadata.RESOURCE_UPDATE_URL).getJson(
|
||||
new TypeToken<Map<String, Map<String, Map<String, RemoteResource>>>>() {
|
||||
}.getType()
|
||||
) : null
|
||||
).whenComplete(Schedulers.defaultScheduler(), (result, exception) -> {
|
||||
if (exception == null && result != null) {
|
||||
remoteResources.clear();
|
||||
remoteResources.putAll(result);
|
||||
|
||||
for (RemoteResourceKey key : keys.values()) {
|
||||
key.downloadRemoteResourceIfNecessary();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
ModTranslations.values();
|
||||
}
|
||||
|
||||
public static RemoteResourceKey get(@NotNull String namespace, @NotNull String name, @NotNull String version, ExceptionalSupplier<InputStream, IOException> defaultSupplier) {
|
||||
String stringKey = String.format("%s:%s:%s", namespace, name, version);
|
||||
RemoteResourceKey key = keys.containsKey(stringKey) ? keys.get(stringKey) : new RemoteResourceKey(namespace, name, version, defaultSupplier);
|
||||
Task.runAsync(key::downloadRemoteResourceIfNecessary).start();
|
||||
keys.put(stringKey, key);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ import javafx.scene.control.Alert.AlertType;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.countly.CrashReport;
|
||||
import org.jackhuang.hmcl.ui.CrashWindow;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.upgrade.hmcl.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -45,7 +45,7 @@ public final class NativePatcher {
|
||||
|
||||
public static Version patchNative(Version version, String gameVersion, JavaVersion javaVersion, VersionSetting settings) {
|
||||
if (settings.getNativesDirType() == NativesDirectoryType.CUSTOM) {
|
||||
if (gameVersion != null && VersionNumber.compare(gameVersion, "1.19") < 0)
|
||||
if (gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") < 0)
|
||||
return version;
|
||||
|
||||
ArrayList<Library> newLibraries = new ArrayList<>();
|
||||
@@ -66,7 +66,7 @@ public final class NativePatcher {
|
||||
final boolean useNativeOpenAL = settings.isUseNativeOpenAL();
|
||||
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && (useNativeGLFW || useNativeOpenAL)
|
||||
&& VersionNumber.compare(gameVersion, "1.19") >= 0) {
|
||||
&& GameVersionNumber.compare(gameVersion, "1.19") >= 0) {
|
||||
|
||||
version = version.setLibraries(version.getLibraries().stream()
|
||||
.filter(library -> {
|
||||
@@ -88,7 +88,7 @@ public final class NativePatcher {
|
||||
|
||||
OperatingSystem os = javaVersion.getPlatform().getOperatingSystem();
|
||||
Architecture arch = javaVersion.getArchitecture();
|
||||
VersionNumber gameVersionNumber = gameVersion != null ? VersionNumber.asVersion(gameVersion) : null;
|
||||
GameVersionNumber gameVersionNumber = gameVersion != null ? GameVersionNumber.asGameVersion(gameVersion) : null;
|
||||
|
||||
if (settings.isNotPatchNatives())
|
||||
return version;
|
||||
|
||||
@@ -204,7 +204,7 @@ curse.category.4477=Mini Game
|
||||
curse.category.4478=Quests
|
||||
curse.category.4484=Multiplayer
|
||||
curse.category.4476=Exploration
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.4736=Skyblock
|
||||
curse.category.4475=Adventure and RPG
|
||||
curse.category.4487=FTB
|
||||
curse.category.4480=Map Based
|
||||
@@ -219,6 +219,11 @@ curse.category.5299=Education
|
||||
curse.category.5232=Galacticraft
|
||||
curse.category.5129=Vanilla+
|
||||
curse.category.5189=Utility & QOL
|
||||
curse.category.6814=Performance
|
||||
curse.category.6954=Integrated Dynamics
|
||||
curse.category.6484=Create
|
||||
curse.category.6821=Bug Fixes
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.5190=QoL
|
||||
curse.category.5191=Utility & QoL
|
||||
curse.category.5192=FancyMenu
|
||||
@@ -356,9 +361,7 @@ HMCL cannot automatically install JavaFX under Java versions below 11.\n\
|
||||
Please update your Java to version 11 or higher.
|
||||
fatal.javafx.incomplete=The JavaFX environment is incomplete.\n\
|
||||
Please try replacing your Java or reinstalling OpenJFX.
|
||||
fatal.javafx.missing=Missing JavaFX environment.\n\
|
||||
If you are using Java 11 or higher, please downgrade it to Oracle JRE 8 (java.com), or install BellSoft Liberica Full JRE (bell-sw.com/pages/downloads/?package\=jre-full).\n\
|
||||
Or, if you are using OpenJDK distributions, please make sure it has OpenJFX included.
|
||||
fatal.javafx.missing=Missing JavaFX environment. Please launch Hello Minecraft! Launcher with a Java which includes OpenJFX.
|
||||
fatal.config_change_owner_root=You are using the root account to start Hello Minecraft! Launcher, this may cause you to fail to start Hello Minecraft! Launcher with other account in the future.\n\
|
||||
Do you still want to continue?
|
||||
fatal.config_in_temp_dir=You are start Hello Minecraft! Launcher in a temporary directory, your settings and game data may be lost.\n\
|
||||
@@ -522,6 +525,7 @@ game.crash.reason.forgemod_resolution=The game crashed due to mod resolution fai
|
||||
\n\
|
||||
Forge provided the following details:\n\
|
||||
%1$s
|
||||
game.crash.reason.forge_found_duplicate_mods=The game cannot continue due to a duplicate mods issue. Forge provides the following information: \n%1$s
|
||||
game.crash.reason.mod_resolution_collection=The game crashed because the mod version is not compatible.\n\
|
||||
\n\
|
||||
%1$s requires %2$s.\n\
|
||||
|
||||
@@ -187,7 +187,7 @@ curse.category.4477=Mini Juego
|
||||
curse.category.4478=Quests
|
||||
curse.category.4484=Multijugador
|
||||
curse.category.4476=Exploración
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.4736=Skyblock
|
||||
curse.category.4475=Aventura y RPG
|
||||
curse.category.4487=FTB
|
||||
curse.category.4480=Basado en mapa
|
||||
@@ -203,6 +203,11 @@ curse.category.5232=Galacticraft
|
||||
curse.category.5129=Vanilla+
|
||||
curse.category.5189=Utilidad y calidad de vida
|
||||
curse.category.5190=Calidad de vida
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.6814=Performance
|
||||
curse.category.6954=Integrated Dynamics
|
||||
curse.category.6484=Create
|
||||
curse.category.6821=Bug Fixes
|
||||
curse.category.5191=Utilidad y calidad de vida
|
||||
curse.category.5192=Menú elegante
|
||||
curse.category.423=Mapa e información
|
||||
@@ -451,6 +456,7 @@ game.crash.reason.forgemod_resolution=El juego se ha bloqueado debido a un fallo
|
||||
\n\
|
||||
Forge proporcionó los siguientes detalles\:\n\
|
||||
%1$s
|
||||
game.crash.reason.forge_found_duplicate_mods=El juego no puede continuar debido a un problema de duplicación de mods. Forge proporciona la siguiente información: \n%1$s
|
||||
game.crash.reason.night_config_fixes=El juego actual no puede seguir ejecutándose debido a algunos problemas con Night Config. \nPuedes intentar instalar el mod <a href="https://www.curseforge.com/minecraft/mc-mods/night-config-fixes">Night Config Fixes</a>, que puede ayudarte con este problema. \nPara obtener más información, visite el <a href="https://www.github.com/Fuzss/nightconfigfixes">repositorio de GitHub</a> del mod.
|
||||
game.crash.reason.mod_resolution_collection=El juego se ha bloqueado porque la versión del mod no es compatible.\n\
|
||||
\n\
|
||||
|
||||
@@ -171,7 +171,7 @@ curse.category.4477=ミニゲーム
|
||||
curse.category.4478=クエスト
|
||||
curse.category.4484=マルチプレイヤー
|
||||
curse.category.4476=探索
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.4736=Skyblock
|
||||
curse.category.4475=アドベンチャーとRPG
|
||||
curse.category.4487=FTB
|
||||
curse.category.4480=マップベース
|
||||
@@ -187,6 +187,11 @@ curse.category.5190=QoL
|
||||
curse.category.5191=Utility & QoL
|
||||
curse.category.5192=FancyMenu
|
||||
curse.category.423=地図と情報
|
||||
curse.category.6814=パフォーマンス
|
||||
curse.category.6484=Create
|
||||
curse.category.6954=Integrated Dynamics
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.6821=バグ修正
|
||||
curse.category.426=Addons
|
||||
curse.category.434=武器、道具、武器
|
||||
curse.category.409=構造
|
||||
@@ -378,6 +383,7 @@ game.crash.reason.memory_exceeded=JVMが割り当てるのに十分なメモリ
|
||||
game.crash.reason.mod=modが原因でゲームがクラッシュしました:%1$s。\nmodを更新または削除して、再試行できます。
|
||||
game.crash.reason.mod_resolution=modの解決に失敗したため、ゲームがクラッシュしました。\nFabricは次の情報を提供します:\n%s
|
||||
game.crash.reason.forgemod_resolution=modの解決に失敗したため、ゲームがクラッシュしました。\nForgeは次の情報を提供します:\n%s
|
||||
game.crash.reason.forge_found_duplicate_mods=現在のゲームは、モッズの重複の問題により、続行できません。Forge が次の情報を提供しました:\n%1$s
|
||||
game.crash.reason.mod_resolution_collection=改造前のバージョンと一致しないため、現在のゲームを続行できない。\n%1$s Mod: %2$s が必要です。をクリックすると実行を継続します。\nつまり、フロントエンドをアップデートするか、ダウングレードする必要があるのです。ダウンロードページからMODをダウンロードするか、ウェブから %3$s をダウンロードすることができます。
|
||||
game.crash.reason.mod_resolution_conflict=modが競合しているため、ゲームがクラッシュしました。\n%1$s が %2$s と競合しています。
|
||||
game.crash.reason.mod_resolution_missing=Modプレフィックスがないため、現在のゲームを続けることができません。\続行するにはMod: %2$s が必要です。\これはMODがインストールされていないか、そのMODのバージョンが足りないことを意味します。ダウンロードページからMODをダウンロードするか、ウェブから %3$s をダウンロードすることができます。
|
||||
|
||||
@@ -185,7 +185,7 @@ curse.category.4477=Mini Game
|
||||
curse.category.4478=Quests
|
||||
curse.category.4484=Multiplayer
|
||||
curse.category.4476=Exploration
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.4736=Skyblock
|
||||
curse.category.4475=Adventure and RPG
|
||||
curse.category.4487=FTB
|
||||
curse.category.4480=Map Based
|
||||
@@ -203,6 +203,11 @@ curse.category.5189=Utility & QOL
|
||||
curse.category.5190=QoL
|
||||
curse.category.5191=Utility & QoL
|
||||
curse.category.5192=FancyMenu
|
||||
curse.category.6814=Performance
|
||||
curse.category.6954=Integrated Dynamics
|
||||
curse.category.6484=Create
|
||||
curse.category.6821=Bug Fixes
|
||||
curse.category.6145=Skyblock
|
||||
curse.category.423=Map and Information
|
||||
curse.category.426=Addons
|
||||
curse.category.434=Armor, Tools, and Weapons
|
||||
@@ -401,6 +406,7 @@ game.crash.reason.memory_exceeded=Игра вылетела, потому что
|
||||
game.crash.reason.mod=Игра вылетела из-за мода: %1$s.\nВы можете обновить или удалить мод и повторить попытку.
|
||||
game.crash.reason.mod_resolution=Игра вылетела из-за сбоя разрешения мода.\nFabric даёт некоторую информацию:\n%s
|
||||
game.crash.reason.forgemod_resolution=Игра вылетела из-за сбоя разрешения мода.\nForge даёт некоторую информацию:\n%s
|
||||
game.crash.reason.forge_found_duplicate_mods=В настоящее время игра не может продолжаться из-за проблемы с повторяющимися модами. Forge предоставляет следующую информацию: \n%1$s
|
||||
game.crash.reason.mod_resolution_collection=Игра вылетела из-за несовместимости требуемой версии мода.\n%1$s требует %2$s.\nЭто означает, что вам необходимо обновить или понизить версию %2$s. Вам необходимо скачать %3$s.
|
||||
game.crash.reason.mod_resolution_conflict=Игра вылетела из-за конфликтующих модов.\n%1$s конфликтуют с %2$s.
|
||||
game.crash.reason.mod_resolution_missing=Игра вылетела, потому что мод, от которого зависит другой мод, не установлен.\n%1$s требует установки мода: %2$s.\nЭто означает, что вам необходимо скачать %2$s со страницы скачивания или через сайт.
|
||||
|
||||
@@ -212,7 +212,7 @@ curse.category.4477=小遊戲
|
||||
curse.category.4478=任務
|
||||
curse.category.4484=多人
|
||||
curse.category.4476=探索
|
||||
curse.category.6145=空島
|
||||
curse.category.4736=空島
|
||||
curse.category.4475=冒險 RPG
|
||||
curse.category.4487=FTB 模組包
|
||||
curse.category.4480=有特定地圖
|
||||
@@ -227,6 +227,11 @@ curse.category.5299=教育
|
||||
curse.category.5232=額外行星
|
||||
curse.category.5129=原版增強
|
||||
curse.category.5189=實用與QOL
|
||||
curse.category.6814=性能
|
||||
curse.category.6954=動態聯合/集成動力 (Integrated Dynamics)
|
||||
curse.category.6484=機械動力 (Create)
|
||||
curse.category.6821=錯誤修復
|
||||
curse.category.6145=空島
|
||||
curse.category.5190=QoL
|
||||
curse.category.5191=實用與QoL
|
||||
curse.category.5192=夢幻菜單
|
||||
@@ -441,6 +446,7 @@ game.crash.reason.memory_exceeded=當前遊戲因為分配的記憶體過大,
|
||||
game.crash.reason.mod=當前遊戲因為 %1$s 的問題,無法繼續運行。\n你可以更新或刪除已經安裝的 %1$s 再試。
|
||||
game.crash.reason.mod_resolution=當前遊戲因為 Mod 依賴問題,無法繼續運行。Fabric 提供了如下訊息:\n%1$s
|
||||
game.crash.reason.forgemod_resolution=當前遊戲因為 Mod 依賴問題,無法繼續運行。Forge 提供了如下訊息:\n%1$s
|
||||
game.crash.reason.forge_found_duplicate_mods=遊戲崩潰原因模組重複的問題,無法繼續運行。Forge 提供了以下信息:\n%1$s
|
||||
game.crash.reason.mod_resolution_collection=當前遊戲因為前置 Mod 版本不匹配,無法繼續運行。\n%1$s 需要前置 Mod:%2$s 才能繼續運行。\n這表示你需要更新或降級前置。你可以到下載頁的模組下載,或到網路上下載 %3$s。
|
||||
game.crash.reason.mod_resolution_conflict=當前遊戲因為 Mod 衝突,無法繼續運行。\n%1$s 與 %2$s 不能相容。
|
||||
game.crash.reason.mod_resolution_missing=當前遊戲因為缺少 Mod 前置,無法繼續運行。\n%1$s 需要前置 Mod:%2$s 才能繼續運行。\n這表示你少安裝了 Mod,或該 Mod 版本不夠。你可以到下載頁的模組下載,或到網路上下載 %2$s。
|
||||
|
||||
@@ -213,7 +213,7 @@ curse.category.4477=小游戏
|
||||
curse.category.4478=任务
|
||||
curse.category.4484=多人
|
||||
curse.category.4476=探索
|
||||
curse.category.6145=空岛
|
||||
curse.category.4736=空岛
|
||||
curse.category.4475=冒险 RPG
|
||||
curse.category.4487=FTB 整合包
|
||||
curse.category.4480=有特定地图
|
||||
@@ -231,6 +231,11 @@ curse.category.5189=实用与QOL
|
||||
curse.category.5190=QoL
|
||||
curse.category.5191=实用与QoL
|
||||
curse.category.5192=梦幻菜单
|
||||
curse.category.6145=空岛
|
||||
curse.category.6814=性能
|
||||
curse.category.6954=动态联合/集成动力 (Integrated Dynamics)
|
||||
curse.category.6484=机械动力 (Create)
|
||||
curse.category.6821=错误修复
|
||||
curse.category.423=信息展示
|
||||
curse.category.426=模组扩展
|
||||
curse.category.434=装备武器
|
||||
@@ -434,7 +439,6 @@ game.crash.reason.memory_exceeded=当前游戏因为分配的内存过大,无
|
||||
game.crash.reason.mac_jdk_8u261=当前游戏因为你所使用的 Forge 或 OptiFine 与 Java 冲突崩溃。\n请尝试更新 Forge 和 OptiFine,或使用 Java 8u251 及更早版本启动。
|
||||
game.crash.reason.mod=当前游戏因为 %1$s 的问题,无法继续运行。\n你可以更新或删除已经安装的 %1$s 再试。
|
||||
game.crash.reason.mod_resolution=当前游戏因为 Mod 依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s
|
||||
game.crash.reason.forgemod_resolution=当前游戏因为 Mod 依赖问题,无法继续运行。Forge 提供了如下信息:\n%1$s
|
||||
game.crash.reason.mod_resolution_collection=当前游戏因为前置 Mod 版本不匹配,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你需要更新或降级前置。你可以到下载页的模组下载,或到网上下载 %3$s。
|
||||
game.crash.reason.mod_resolution_conflict=当前游戏因为 Mod 冲突,无法继续运行。\n%1$s 与 %2$s 不能兼容。
|
||||
game.crash.reason.mod_resolution_missing=当前游戏因为缺少 Mod 前置,无法继续运行。\n%1$s 需要前置 Mod:%2$s 才能继续运行。\n这表示你少安装了 Mod,或该 Mod 版本不够。你可以到下载页的模组下载,或到网上下载 %3$s。
|
||||
@@ -445,6 +449,7 @@ game.crash.reason.forge_repeat_installation=当前游戏因为 Forge 重复安
|
||||
game.crash.reason.optifine_repeat_installation=当前游戏因为 Optifine 重复安装,无法继续运行。\n请删除 Mod 文件夹下的 Optifine 或前往 游戏管理-自动安装 卸载自动安装的 Optifine。
|
||||
game.crash.reason.mod_resolution=当前游戏因为模组依赖问题,无法继续运行。Fabric 提供了如下信息:\n%1$s
|
||||
game.crash.reason.forgemod_resolution=当前游戏因为模组依赖问题,无法继续运行。Forge 提供了如下信息:\n%1$s
|
||||
game.crash.reason.forge_found_duplicate_mods=当前游戏因为模组重复问题,无法继续运行。Forge 提供了如下信息:\n%1$s
|
||||
game.crash.reason.mod_resolution_collection=当前游戏因为前置模组版本不匹配,无法继续运行。\n%1$s 需要前置模组:%2$s 才能继续运行。\n这表示你需要更新或降级前置。你可以到下载页的模组下载,或到网上下载 %3$s。
|
||||
game.crash.reason.mod_resolution_conflict=当前游戏因为模组冲突,无法继续运行。\n%1$s 与 %2$s 不能兼容。
|
||||
game.crash.reason.mod_resolution_missing=当前游戏因为缺少模组前置,无法继续运行。\n%1$s 需要前置模组:%2$s 才能继续运行。\n这表示你少安装了模组,或该模组版本不够。你可以到下载页的模组下载,或到网上下载 %3$s。
|
||||
|
||||
@@ -2346,6 +2346,160 @@
|
||||
"org.lwjgl:lwjgl-glfw:3.3.1:natives-linux": null,
|
||||
"org.lwjgl:lwjgl-stb:3.3.1:natives-linux": null,
|
||||
"org.lwjgl:lwjgl-tinyfd:3.3.1:natives-linux": null,
|
||||
"org.lwjgl:lwjgl:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl/3.3.4-SNAPSHOT/lwjgl-3.3.4-20231218.151521-4.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl/3.3.4-SNAPSHOT/lwjgl-3.3.4-20231218.151521-4.jar",
|
||||
"sha1": "7202012cf0cadb9ffad4874494920fd8bbd93413",
|
||||
"size": 792204
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl/3.3.4-SNAPSHOT/lwjgl-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl/3.3.4-SNAPSHOT/lwjgl-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"sha1": "2d38355b453edfe2daee1a567bcdb82c0485edcf",
|
||||
"size": 95872
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-jemalloc:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-jemalloc/3.3.4-SNAPSHOT/lwjgl-jemalloc-3.3.4-20231218.151521-4.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-jemalloc/3.3.4-SNAPSHOT/lwjgl-jemalloc-3.3.4-20231218.151521-4.jar",
|
||||
"sha1": "41256f2c098806304fd224613d3d01b02725470e",
|
||||
"size": 46421
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-jemalloc:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-jemalloc/3.3.4-SNAPSHOT/lwjgl-jemalloc-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-jemalloc/3.3.4-SNAPSHOT/lwjgl-jemalloc-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"sha1": "bdba1662b621228679c7aed87945d1f28c590d5b",
|
||||
"size": 155867
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-openal:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-openal:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-openal/3.3.4-SNAPSHOT/lwjgl-openal-3.3.4-20231218.151521-4.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-openal/3.3.4-SNAPSHOT/lwjgl-openal-3.3.4-20231218.151521-4.jar",
|
||||
"sha1": "89d8868c2d688b55e3e923345e4a146c6d034229",
|
||||
"size": 113094
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-openal:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-openal:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-openal/3.3.4-SNAPSHOT/lwjgl-openal-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-openal/3.3.4-SNAPSHOT/lwjgl-openal-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"sha1": "0fc6495e6752727b629cf03a105c9d56087d4edd",
|
||||
"size": 597512
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-opengl:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-opengl:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-opengl/3.3.4-SNAPSHOT/lwjgl-opengl-3.3.4-20231218.151521-4.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-opengl/3.3.4-SNAPSHOT/lwjgl-opengl-3.3.4-20231218.151521-4.jar",
|
||||
"sha1": "81d0a7fd96bf5eb6257fddf6b77e338d8918bf32",
|
||||
"size": 931744
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-opengl:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-opengl:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-opengl/3.3.4-SNAPSHOT/lwjgl-opengl-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-opengl/3.3.4-SNAPSHOT/lwjgl-opengl-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"sha1": "d3e8ec997cef8bc66819c7e0ad7a54182f7aff2a",
|
||||
"size": 81034
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-glfw:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-glfw:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-glfw/3.3.4-SNAPSHOT/lwjgl-glfw-3.3.4-20231218.151521-4.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-glfw/3.3.4-SNAPSHOT/lwjgl-glfw-3.3.4-20231218.151521-4.jar",
|
||||
"sha1": "e6dba9ab8532cb6aac273adf26496ce689999943",
|
||||
"size": 146829
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-glfw:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-glfw:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-glfw/3.3.4-SNAPSHOT/lwjgl-glfw-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-glfw/3.3.4-SNAPSHOT/lwjgl-glfw-3.3.4-20231218.151521-4-natives-freebsd.jar",
|
||||
"sha1": "4881a965c7679b4984c0d8a5890f07ea85c653c4",
|
||||
"size": 101847
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-stb:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-stb:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-stb/3.3.4-SNAPSHOT/lwjgl-stb-3.3.4-20231218.151521-3.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-stb/3.3.4-SNAPSHOT/lwjgl-stb-3.3.4-20231218.151521-3.jar",
|
||||
"sha1": "033fe42d1b37e35afd8b6e2653abc77deadb0730",
|
||||
"size": 143099
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-stb:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-stb:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-stb/3.3.4-SNAPSHOT/lwjgl-stb-3.3.4-20231218.151521-3-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-stb/3.3.4-SNAPSHOT/lwjgl-stb-3.3.4-20231218.151521-3-natives-freebsd.jar",
|
||||
"sha1": "0aad82a857ebb9a3a212dc2c386761d57e11a8f2",
|
||||
"size": 225735
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-tinyfd:3.3.2": {
|
||||
"name": "org.lwjgl:lwjgl-tinyfd:3.3.4-SNAPSHOT",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-tinyfd/3.3.4-SNAPSHOT/lwjgl-tinyfd-3.3.4-20231218.151521-3.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-tinyfd/3.3.4-SNAPSHOT/lwjgl-tinyfd-3.3.4-20231218.151521-3.jar",
|
||||
"sha1": "8b7c94a57f56a5b38b23c02c1cada77dccba9930",
|
||||
"size": 15917
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-tinyfd:3.3.2:natives-linux": {
|
||||
"name": "org.lwjgl:lwjgl-tinyfd:3.3.4-SNAPSHOT:natives-freebsd",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-tinyfd/3.3.4-SNAPSHOT/lwjgl-tinyfd-3.3.4-20231218.151521-3-natives-freebsd.jar",
|
||||
"url": "https://oss.sonatype.org/content/repositories/snapshots/org/lwjgl/lwjgl-tinyfd/3.3.4-SNAPSHOT/lwjgl-tinyfd-3.3.4-20231218.151521-3-natives-freebsd.jar",
|
||||
"sha1": "124d0e48ae1584f09e5701588ae8ad139be26003",
|
||||
"size": 39077
|
||||
}
|
||||
}
|
||||
},
|
||||
"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,
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide
|
||||
}
|
||||
|
||||
try {
|
||||
new FileDownloadTask(new URL(downloadProvider.get().injectURL(latest.downloadUrl)), artifactLocation.toFile(),
|
||||
new FileDownloadTask(downloadProvider.get().injectURLWithCandidates(latest.downloadUrl), artifactLocation.toFile(),
|
||||
Optional.ofNullable(latest.checksums.get("sha256"))
|
||||
.map(checksum -> new IntegrityCheck("SHA-256", checksum))
|
||||
.orElse(null))
|
||||
|
||||
@@ -155,7 +155,8 @@ public class Skin {
|
||||
if (defaultSkinLoader == null) {
|
||||
return Task.supplyAsync(() -> null);
|
||||
}
|
||||
return Task.supplyAsync(() -> new LoadedSkin(TextureModel.STEVE, Texture.loadTexture(defaultSkinLoader.apply(type)), null));
|
||||
TextureModel model = type == Type.ALEX ? TextureModel.ALEX : TextureModel.STEVE;
|
||||
return Task.supplyAsync(() -> new LoadedSkin(model, Texture.loadTexture(defaultSkinLoader.apply(type)), null));
|
||||
case LOCAL_FILE:
|
||||
return Task.supplyAsync(() -> {
|
||||
Texture skin = null, cape = null;
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.ToStringBuilder;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -36,7 +36,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
private final String libraryId;
|
||||
private final String gameVersion;
|
||||
private final String selfVersion;
|
||||
private final Date releaseDate;
|
||||
private final Instant releaseDate;
|
||||
private final List<String> urls;
|
||||
private final Type type;
|
||||
|
||||
@@ -47,7 +47,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Date releaseDate, List<String> urls) {
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Instant releaseDate, List<String> urls) {
|
||||
this(libraryId, gameVersion, selfVersion, releaseDate, Type.UNCATEGORIZED, urls);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar URL.
|
||||
*/
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Date releaseDate, Type type, List<String> urls) {
|
||||
public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Instant releaseDate, Type type, List<String> urls) {
|
||||
this.libraryId = Objects.requireNonNull(libraryId);
|
||||
this.gameVersion = Objects.requireNonNull(gameVersion);
|
||||
this.selfVersion = Objects.requireNonNull(selfVersion);
|
||||
@@ -83,7 +83,7 @@ public class RemoteVersion implements Comparable<RemoteVersion> {
|
||||
return getSelfVersion();
|
||||
}
|
||||
|
||||
public Date getReleaseDate() {
|
||||
public Instant getReleaseDate() {
|
||||
return releaseDate;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class FabricAPIRemoteVersion extends RemoteVersion {
|
||||
@@ -38,7 +38,7 @@ public class FabricAPIRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
FabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Date datePublished, RemoteMod.Version version, List<String> urls) {
|
||||
FabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteMod.Version version, List<String> urls) {
|
||||
super(LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId(), gameVersion, selfVersion, datePublished, urls);
|
||||
|
||||
this.fullVersion = fullVersion;
|
||||
|
||||
@@ -109,7 +109,7 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||
}
|
||||
|
||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||
version.getGameVersion(), version.getVersion(), releaseDate == null ? null : Date.from(releaseDate), urls));
|
||||
version.getGameVersion(), version.getVersion(), releaseDate, urls));
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
|
||||
@@ -24,7 +24,7 @@ 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 org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
@@ -100,7 +100,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
||||
@Override
|
||||
public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException {
|
||||
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
|
||||
if (VersionNumber.compare("1.13", remote.getGameVersion()) <= 0) {
|
||||
if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) {
|
||||
// Forge 1.13 is not compatible with fabric.
|
||||
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass)
|
||||
&& !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass)
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class ForgeRemoteVersion extends RemoteVersion {
|
||||
@@ -34,7 +34,7 @@ public class ForgeRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param url the installer or universal jar original URL.
|
||||
*/
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, Date releaseDate, List<String> url) {
|
||||
public ForgeRemoteVersion(String gameVersion, String selfVersion, Instant releaseDate, List<String> url) {
|
||||
super(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), gameVersion, selfVersion, releaseDate, url);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ public final class GameRemoteVersion extends RemoteVersion {
|
||||
|
||||
private final ReleaseType type;
|
||||
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, List<String> url, ReleaseType type, Date releaseDate) {
|
||||
public GameRemoteVersion(String gameVersion, String selfVersion, List<String> url, ReleaseType type, Instant releaseDate) {
|
||||
super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, releaseDate, getReleaseType(type), url);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -36,10 +36,10 @@ public final class GameRemoteVersionInfo implements Validation {
|
||||
private final String gameVersion;
|
||||
|
||||
@SerializedName("time")
|
||||
private final Date time;
|
||||
private final Instant time;
|
||||
|
||||
@SerializedName("releaseTime")
|
||||
private final Date releaseTime;
|
||||
private final Instant releaseTime;
|
||||
|
||||
@SerializedName("type")
|
||||
private final ReleaseType type;
|
||||
@@ -48,14 +48,14 @@ public final class GameRemoteVersionInfo implements Validation {
|
||||
private final String url;
|
||||
|
||||
public GameRemoteVersionInfo() {
|
||||
this("", new Date(), new Date(), ReleaseType.UNKNOWN);
|
||||
this("", Instant.now(), Instant.now(), ReleaseType.UNKNOWN);
|
||||
}
|
||||
|
||||
public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type) {
|
||||
public GameRemoteVersionInfo(String gameVersion, Instant time, Instant releaseTime, ReleaseType type) {
|
||||
this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json");
|
||||
}
|
||||
|
||||
public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) {
|
||||
public GameRemoteVersionInfo(String gameVersion, Instant time, Instant releaseTime, ReleaseType type, String url) {
|
||||
this.gameVersion = gameVersion;
|
||||
this.time = time;
|
||||
this.releaseTime = releaseTime;
|
||||
@@ -67,11 +67,11 @@ public final class GameRemoteVersionInfo implements Validation {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
public Instant getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public Date getReleaseTime() {
|
||||
public Instant getReleaseTime() {
|
||||
return releaseTime;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -65,7 +65,7 @@ public final class GameVerificationFixTask extends Task<Void> {
|
||||
File jar = dependencyManager.getGameRepository().getVersionJar(version);
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
|
||||
|
||||
if (jar.exists() && VersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) {
|
||||
if (jar.exists() && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) {
|
||||
try (FileSystem fs = CompressingUtils.createWritableZipFileSystem(jar.toPath(), StandardCharsets.UTF_8)) {
|
||||
Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.DSA"));
|
||||
Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.SF"));
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ public final class OptiFineInstallTask extends Task<Version> {
|
||||
|
||||
if (LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) {
|
||||
// OptiFine H1 Pre2+ is compatible with Forge 1.17
|
||||
if (buildofVer.compareTo(VersionNumber.asVersion("20210924-190833")) < 0) {
|
||||
if (buildofVer.compareTo("20210924-190833") < 0) {
|
||||
throw new UnsupportedInstallationException(UnsupportedInstallationException.FORGE_1_17_OPTIFINE_H1_PRE2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class QuiltAPIRemoteVersion extends RemoteVersion {
|
||||
@@ -38,7 +38,7 @@ public class QuiltAPIRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
QuiltAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Date datePublished, RemoteMod.Version version, List<String> urls) {
|
||||
QuiltAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Instant datePublished, RemoteMod.Version version, List<String> urls) {
|
||||
super(LibraryAnalyzer.LibraryType.QUILT_API.getPatchId(), gameVersion, selfVersion, datePublished, urls);
|
||||
|
||||
this.fullVersion = fullVersion;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* The Minecraft version for 1.5.x and earlier.
|
||||
@@ -32,7 +32,7 @@ public class ClassicVersion extends Version {
|
||||
super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}",
|
||||
null, "net.minecraft.client.Minecraft", null, null, null, null, null, null,
|
||||
Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")),
|
||||
null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false, false, null);
|
||||
null, null, null, ReleaseType.UNKNOWN, Instant.now(), Instant.now(), 0, false, false, null);
|
||||
}
|
||||
|
||||
private static class ClassicLibrary extends Library {
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class CrashReportAnalyzer {
|
||||
OPENGL_NOT_SUPPORTED(Pattern.compile("The driver does not appear to support OpenGL")),
|
||||
GRAPHICS_DRIVER(Pattern.compile("(Pixel format not accelerated|GLX: Failed to create context: GLXBadFBConfig|Couldn't set pixel format|net\\.minecraftforge\\.fml.client\\.SplashProgress|org\\.lwjgl\\.LWJGLException|EXCEPTION_ACCESS_VIOLATION(.|\\n|\\r)+# C {2}\\[(ig|atio|nvoglv))")),
|
||||
// Out of memory
|
||||
OUT_OF_MEMORY(Pattern.compile("(java\\.lang\\.OutOfMemoryError|The system is out of physical RAM or swap space|Out of Memory Error)")),
|
||||
OUT_OF_MEMORY(Pattern.compile("(java\\.lang\\.OutOfMemoryError|The system is out of physical RAM or swap space|Out of Memory Error|Error occurred during initialization of VM\\RToo small maximum heap)")),
|
||||
// Memory exceeded
|
||||
MEMORY_EXCEEDED(Pattern.compile("There is insufficient memory for the Java Runtime Environment to continue")),
|
||||
// Too high resolution
|
||||
@@ -71,6 +71,7 @@ public final class CrashReportAnalyzer {
|
||||
// Fabric mod resolution
|
||||
MOD_RESOLUTION(Pattern.compile("ModResolutionException: (?<reason>(.*)[\\n\\r]*( - (.*)[\\n\\r]*)+)"), "reason"),
|
||||
FORGEMOD_RESOLUTION(Pattern.compile("Missing or unsupported mandatory dependencies:(?<reason>(.*)[\\n\\r]*(\t(.*)[\\n\\r]*)+)"), "reason"),
|
||||
FORGE_FOUND_DUPLICATE_MODS(Pattern.compile("Found duplicate mods:(?<reason>(.*)\\R*(\t(.*)\\R*)+)"), "reason"),
|
||||
MOD_RESOLUTION_CONFLICT(Pattern.compile("ModResolutionException: Found conflicting mods: (?<sourcemod>.*) conflicts with (?<destmod>.*)"), "sourcemod", "destmod"),
|
||||
MOD_RESOLUTION_MISSING(Pattern.compile("ModResolutionException: Could not find required mod: (?<sourcemod>.*) requires (?<destmod>.*)"), "sourcemod", "destmod"),
|
||||
MOD_RESOLUTION_MISSING_MINECRAFT(Pattern.compile("ModResolutionException: Could not find required mod: (?<mod>.*) requires \\{minecraft @ (?<version>.*)}"), "mod", "version"),
|
||||
@@ -92,7 +93,7 @@ public final class CrashReportAnalyzer {
|
||||
DEBUG_CRASH(Pattern.compile("Manually triggered debug crash")),
|
||||
CONFIG(Pattern.compile("Failed loading config file (?<file>.*?) of type (.*?) for modid (?<id>.*)"), "id", "file"),
|
||||
// Fabric gives some warnings
|
||||
FABRIC_WARNINGS(Pattern.compile("(Warnings were found!|Incompatible mod set!)(.*?)[\\n\\r]+(?<reason>[^\\[]+)\\["), "reason"),
|
||||
FABRIC_WARNINGS(Pattern.compile("(Warnings were found!|Incompatible mod set!|Incompatible mods found!)(.*?)[\\n\\r]+(?<reason>[^\\[]+)\\["), "reason"),
|
||||
// Game crashed when ticking entity
|
||||
ENTITY(Pattern.compile("Entity Type: (?<type>.*)[\\w\\W\\n\\r]*?Entity's Exact location: (?<location>.*)"), "type", "location"),
|
||||
// Game crashed when tesselating block model
|
||||
@@ -114,7 +115,7 @@ public final class CrashReportAnalyzer {
|
||||
MOD_RESOLUTION0(Pattern.compile("(\tMod File:|-- MOD |\tFailure message:)")),
|
||||
FORGE_REPEAT_INSTALLATION(Pattern.compile("MultipleArgumentsForOptionException: Found multiple arguments for option (.*?), but you asked for only one")),//https://github.com/HMCL-dev/HMCL/issues/1880
|
||||
OPTIFINE_REPEAT_INSTALLATION(Pattern.compile("ResolutionException: Module optifine reads another module named optifine")),//Optifine 重复安装(及Mod文件夹有,自动安装也有)
|
||||
JAVA_VERSION_IS_TOO_HIGH(Pattern.compile("(Unable to make protected final java\\.lang\\.Class java\\.lang\\.ClassLoader\\.defineClass|java\\.lang\\.NoSuchFieldException: ucp|Unsupported class file major version|because module java\\.base does not export|java\\.lang\\.ClassNotFoundException: jdk\\.nashorn\\.api\\.scripting\\.NashornScriptEngineFactory|java\\.lang\\.ClassNotFoundException: java\\.lang\\.invoke\\.LambdaMetafactory)")),//Java版本过高
|
||||
JAVA_VERSION_IS_TOO_HIGH(Pattern.compile("(Unable to make protected final java\\.lang\\.Class java\\.lang\\.ClassLoader\\.defineClass|java\\.lang\\.NoSuchFieldException: ucp|Unsupported class file major version|because module java\\.base does not export|java\\.lang\\.ClassNotFoundException: jdk\\.nashorn\\.api\\.scripting\\.NashornScriptEngineFactory|java\\.lang\\.ClassNotFoundException: java\\.lang\\.invoke\\.LambdaMetafactory|Exception in thread \"main\" java\\.lang\\.NullPointerException: Cannot read the array length because \"urls\" is null)")),//Java版本过高
|
||||
INSTALL_MIXINBOOTSTRAP(Pattern.compile("java\\.lang\\.ClassNotFoundException: org\\.spongepowered\\.asm\\.launch\\.MixinTweaker")),
|
||||
|
||||
//Forge 默认会把每一个 mod jar 都当做一个 JPMS 的模块(Module)加载。在这个 jar 没有给出 module-info 声明的情况下,JPMS 会采用这样的顺序决定 module 名字:
|
||||
@@ -124,7 +125,7 @@ public final class CrashReportAnalyzer {
|
||||
MOD_NAME(Pattern.compile("Invalid module name: '' is not a Java identifier")),
|
||||
|
||||
//Forge 安装不完整
|
||||
INCOMPLETE_FORGE_INSTALLATION(Pattern.compile("(java\\.io\\.UncheckedIOException: java\\.io\\.IOException: Invalid paths argument, contained no existing paths: \\[(.*?)forge-(.*?)-client\\.jar\\]|Failed to find Minecraft resource version (.*?) at (.*?)forge-(.*?)-client\\.jar|Cannot find launch target fmlclient, unable to launch|java\\.lang\\.IllegalStateException: Could not find net/minecraft/client/Minecraft\\.class in classloader SecureModuleClassLoader)")),
|
||||
INCOMPLETE_FORGE_INSTALLATION(Pattern.compile("(java\\.io\\.UncheckedIOException: java\\.io\\.IOException: Invalid paths argument, contained no existing paths: \\[(.*?)(forge-(.*?)-client\\.jar|fmlcore-(.*?)\\.jar)\\]|Failed to find Minecraft resource version (.*?) at (.*?)forge-(.*?)-client\\.jar|Cannot find launch target fmlclient, unable to launch|java\\.lang\\.IllegalStateException: Could not find net/minecraft/client/Minecraft\\.class in classloader SecureModuleClassLoader)")),
|
||||
|
||||
NIGHT_CONFIG_FIXES(Pattern.compile("com\\.electronwill\\.nightconfig\\.core\\.io\\.ParsingException: Not enough data available")),//https://github.com/Fuzss/nightconfigfixes
|
||||
//Shaders Mod detected. Please remove it, OptiFine has built-in support for shaders.
|
||||
|
||||
@@ -42,7 +42,7 @@ import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class GameVersion {
|
||||
final class GameVersion {
|
||||
private GameVersion() {
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionRange;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -30,48 +31,47 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LAUNCH_WRAPPER_MAIN;
|
||||
import static org.jackhuang.hmcl.util.versioning.VersionRange.*;
|
||||
|
||||
public enum JavaVersionConstraint {
|
||||
// Minecraft>=1.13 requires Java 8
|
||||
VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.13"), atLeast("1.8")),
|
||||
VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, GameVersionNumber.atLeast("1.13"), VersionNumber.atLeast("1.8")),
|
||||
// Minecraft 1.17 requires Java 16
|
||||
VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.17"), atLeast("16")),
|
||||
VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, GameVersionNumber.atLeast("1.17"), VersionNumber.atLeast("16")),
|
||||
// Minecraft>=1.18 requires Java 17
|
||||
VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, atLeast("1.18"), atLeast("17")),
|
||||
VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, GameVersionNumber.atLeast("1.18"), VersionNumber.atLeast("17")),
|
||||
// Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7.
|
||||
MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, atMost("1.7.2"), atMost("1.7.999")) {
|
||||
MODDED_JAVA_7(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.atMost("1.7.2"), VersionNumber.atMost("1.7.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return version != null && analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.7.10", "1.16.999"), between("1.8", "1.8.999")) {
|
||||
MODDED_JAVA_8(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.between("1.7.10", "1.16.999"), VersionNumber.between("1.8", "1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, between("1.17", "1.17.999"), between("16", "16.999")) {
|
||||
MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.between("1.17", "1.17.999"), VersionNumber.between("16", "16.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.18"), between("17", "17.999")) {
|
||||
MODDED_JAVA_17(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.atLeast("1.18"), VersionNumber.between("17", "17.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE);
|
||||
}
|
||||
},
|
||||
// LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8)
|
||||
LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) {
|
||||
LAUNCH_WRAPPER(JavaVersionConstraint.RULE_MANDATORY, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null) return false;
|
||||
return LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) &&
|
||||
@@ -81,33 +81,33 @@ public enum JavaVersionConstraint {
|
||||
}
|
||||
},
|
||||
// Minecraft>=1.13 may crash when generating world on Java [1.8,1.8.0_51)
|
||||
VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, atLeast("1.13"), atLeast("1.8.0_51")),
|
||||
VANILLA_JAVA_8_51(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.atLeast("1.13"), VersionNumber.atLeast("1.8.0_51")),
|
||||
// Minecraft with suggested java version recorded in game json is restrictedly constrained.
|
||||
GAME_JSON(JavaVersionConstraint.RULE_MANDATORY, VersionRange.all(), VersionRange.all()) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null) return false;
|
||||
// We only checks for 1.7.10 and above, since 1.7.2 with Forge can only run on Java 7, but it is recorded Java 8 in game json, which is not correct.
|
||||
return gameVersionNumber.compareTo(VersionNumber.asVersion("1.7.10")) >= 0 && version.getJavaVersion() != null;
|
||||
return gameVersionNumber.compareTo("1.7.10") >= 0 && version.getJavaVersion() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VersionRange getJavaVersionRange(Version version) {
|
||||
public VersionRange<VersionNumber> getJavaVersionRange(Version version) {
|
||||
String javaVersion;
|
||||
if (Objects.requireNonNull(version.getJavaVersion()).getMajorVersion() >= 9) {
|
||||
javaVersion = "" + version.getJavaVersion().getMajorVersion();
|
||||
} else {
|
||||
javaVersion = "1." + version.getJavaVersion().getMajorVersion();
|
||||
}
|
||||
return atLeast(javaVersion);
|
||||
return VersionNumber.atLeast(javaVersion);
|
||||
}
|
||||
},
|
||||
// On Linux, JDK 9+ cannot launch Minecraft<=1.12.2, since JDK 9+ does not accept loading native library built in different arch.
|
||||
// For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library.
|
||||
VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, atMost("1.12.999"), atMost("1.8.999")) {
|
||||
VANILLA_LINUX_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return OperatingSystem.CURRENT_OS == OperatingSystem.LINUX
|
||||
&& Architecture.SYSTEM_ARCH == Architecture.X86_64
|
||||
@@ -115,33 +115,33 @@ public enum JavaVersionConstraint {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
return javaVersion.getArchitecture() != Architecture.X86_64 || super.checkJava(gameVersionNumber, version, javaVersion);
|
||||
}
|
||||
},
|
||||
// Minecraft currently does not provide official support for architectures other than x86 and x86-64.
|
||||
VANILLA_X86(JavaVersionConstraint.RULE_SUGGESTED, VersionRange.all(), VersionRange.all()) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (javaVersion == null || javaVersion.getArchitecture() != Architecture.ARM64)
|
||||
return false;
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS || OperatingSystem.CURRENT_OS == OperatingSystem.OSX)
|
||||
return gameVersionNumber.compareTo(VersionNumber.asVersion("1.6")) < 0;
|
||||
return gameVersionNumber.compareTo("1.6") < 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
return javaVersion.getArchitecture().isX86();
|
||||
}
|
||||
},
|
||||
// Minecraft 1.16+Forge with crash because JDK-8273826
|
||||
MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, between("1.16.3", "1.17.1"), VersionRange.all()) {
|
||||
MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, GameVersionNumber.between("1.16.3", "1.17.1"), VersionRange.all()) {
|
||||
@Override
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
if (version == null || javaVersion == null || analyzer == null) return false;
|
||||
VersionNumber forgePatchVersion = analyzer.getVersion(LibraryAnalyzer.LibraryType.FORGE)
|
||||
@@ -158,14 +158,14 @@ public enum JavaVersionConstraint {
|
||||
case "1.16.5":
|
||||
return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0;
|
||||
case "1.17.1":
|
||||
return between("37.0.60", "37.0.75").contains(forgePatchVersion);
|
||||
return VersionNumber.between("37.0.60", "37.0.75").contains(forgePatchVersion);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
int parsedJavaVersion = javaVersion.getParsedVersion();
|
||||
if (parsedJavaVersion > 17) {
|
||||
return false;
|
||||
@@ -181,13 +181,13 @@ public enum JavaVersionConstraint {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};;
|
||||
};
|
||||
|
||||
private final int type;
|
||||
private final VersionRange gameVersionRange;
|
||||
private final VersionRange javaVersionRange;
|
||||
private final VersionRange<GameVersionNumber> gameVersionRange;
|
||||
private final VersionRange<VersionNumber> javaVersionRange;
|
||||
|
||||
JavaVersionConstraint(int type, VersionRange gameVersionRange, VersionRange javaVersionRange) {
|
||||
JavaVersionConstraint(int type, VersionRange<GameVersionNumber> gameVersionRange, VersionRange<VersionNumber> javaVersionRange) {
|
||||
this.type = type;
|
||||
this.gameVersionRange = gameVersionRange;
|
||||
this.javaVersionRange = javaVersionRange;
|
||||
@@ -197,39 +197,39 @@ public enum JavaVersionConstraint {
|
||||
return type;
|
||||
}
|
||||
|
||||
public VersionRange getGameVersionRange() {
|
||||
public VersionRange<GameVersionNumber> getGameVersionRange() {
|
||||
return gameVersionRange;
|
||||
}
|
||||
|
||||
public VersionRange getJavaVersionRange(Version version) {
|
||||
public VersionRange<VersionNumber> getJavaVersionRange(Version version) {
|
||||
return javaVersionRange;
|
||||
}
|
||||
|
||||
public final boolean appliesToVersion(@Nullable VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
public final boolean appliesToVersion(@Nullable GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, LibraryAnalyzer analyzer) {
|
||||
return gameVersionRange.contains(gameVersionNumber)
|
||||
&& appliesToVersionImpl(gameVersionNumber, version, javaVersion, analyzer);
|
||||
}
|
||||
|
||||
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
|
||||
protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version,
|
||||
@Nullable JavaVersion javaVersion, @Nullable LibraryAnalyzer analyzer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
|
||||
return getJavaVersionRange(version).contains(javaVersion.getVersionNumber());
|
||||
}
|
||||
|
||||
public static final List<JavaVersionConstraint> ALL = Lang.immutableListOf(values());
|
||||
|
||||
public static VersionRanges findSuitableJavaVersionRange(VersionNumber gameVersion, Version version) {
|
||||
VersionRange mandatoryJavaRange = VersionRange.all();
|
||||
VersionRange suggestedJavaRange = VersionRange.all();
|
||||
public static VersionRanges findSuitableJavaVersionRange(GameVersionNumber gameVersion, Version version) {
|
||||
VersionRange<VersionNumber> mandatoryJavaRange = VersionRange.all();
|
||||
VersionRange<VersionNumber> suggestedJavaRange = VersionRange.all();
|
||||
LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version) : null;
|
||||
for (JavaVersionConstraint java : ALL) {
|
||||
if (java.appliesToVersion(gameVersion, version, null, analyzer)) {
|
||||
VersionRange javaVersionRange = java.getJavaVersionRange(version);
|
||||
VersionRange<VersionNumber> javaVersionRange = java.getJavaVersionRange(version);
|
||||
if (java.type == RULE_MANDATORY) {
|
||||
mandatoryJavaRange = mandatoryJavaRange.intersectionWith(javaVersionRange);
|
||||
suggestedJavaRange = suggestedJavaRange.intersectionWith(javaVersionRange);
|
||||
@@ -242,12 +242,12 @@ public enum JavaVersionConstraint {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JavaVersion findSuitableJavaVersion(VersionNumber gameVersion, Version version) throws InterruptedException {
|
||||
public static JavaVersion findSuitableJavaVersion(GameVersionNumber gameVersion, Version version) throws InterruptedException {
|
||||
VersionRanges range = findSuitableJavaVersionRange(gameVersion, version);
|
||||
|
||||
boolean forceX86 = Architecture.SYSTEM_ARCH == Architecture.ARM64
|
||||
&& (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS || OperatingSystem.CURRENT_OS == OperatingSystem.OSX)
|
||||
&& gameVersion.compareTo(VersionNumber.asVersion("1.6")) < 0;
|
||||
&& gameVersion.compareTo("1.6") < 0;
|
||||
|
||||
JavaVersion mandatory = null;
|
||||
JavaVersion suggested = null;
|
||||
@@ -298,19 +298,19 @@ public enum JavaVersionConstraint {
|
||||
public static final int RULE_SUGGESTED = 2;
|
||||
|
||||
public static final class VersionRanges {
|
||||
private final VersionRange mandatory;
|
||||
private final VersionRange suggested;
|
||||
private final VersionRange<VersionNumber> mandatory;
|
||||
private final VersionRange<VersionNumber> suggested;
|
||||
|
||||
public VersionRanges(VersionRange mandatory, VersionRange suggested) {
|
||||
public VersionRanges(VersionRange<VersionNumber> mandatory, VersionRange<VersionNumber> suggested) {
|
||||
this.mandatory = mandatory;
|
||||
this.suggested = suggested;
|
||||
}
|
||||
|
||||
public VersionRange getMandatory() {
|
||||
public VersionRange<VersionNumber> getMandatory() {
|
||||
return mandatory;
|
||||
}
|
||||
|
||||
public VersionRange getSuggested() {
|
||||
public VersionRange<VersionNumber> getSuggested() {
|
||||
return suggested;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ import org.jackhuang.hmcl.util.gson.JsonMap;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -66,8 +66,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
private final JsonMap<DownloadType, DownloadInfo> downloads;
|
||||
private final JsonMap<DownloadType, LoggingInfo> logging;
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
private final Date releaseTime;
|
||||
private final Instant time;
|
||||
private final Instant releaseTime;
|
||||
private final Integer minimumLauncherVersion;
|
||||
private final Boolean root;
|
||||
private final Boolean hidden;
|
||||
@@ -93,7 +93,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, null, null, libraries, null, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, GameJavaVersion javaVersion, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Boolean hidden, Boolean root, List<Version> patches) {
|
||||
public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, GameJavaVersion javaVersion, List<Library> libraries, List<CompatibilityRule> compatibilityRules, Map<DownloadType, DownloadInfo> downloads, Map<DownloadType, LoggingInfo> logging, ReleaseType type, Instant time, Instant releaseTime, Integer minimumLauncherVersion, Boolean hidden, Boolean root, List<Version> patches) {
|
||||
this.resolved = resolved;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
@@ -112,8 +112,8 @@ public class Version implements Comparable<Version>, Validation {
|
||||
this.downloads = downloads == null ? null : new JsonMap<>(downloads);
|
||||
this.logging = logging == null ? null : new JsonMap<>(logging);
|
||||
this.type = type;
|
||||
this.time = time == null ? null : (Date) time.clone();
|
||||
this.releaseTime = releaseTime == null ? null : (Date) releaseTime.clone();
|
||||
this.time = time;
|
||||
this.releaseTime = releaseTime;
|
||||
this.minimumLauncherVersion = minimumLauncherVersion;
|
||||
this.hidden = hidden;
|
||||
this.root = root;
|
||||
@@ -132,7 +132,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
public Instant getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ public class Version implements Comparable<Version>, Validation {
|
||||
return type == null ? ReleaseType.UNKNOWN : type;
|
||||
}
|
||||
|
||||
public Date getReleaseTime() {
|
||||
public Instant getReleaseTime() {
|
||||
return releaseTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
||||
import org.jackhuang.hmcl.util.gson.Validation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -46,12 +46,12 @@ public class TLauncherVersion implements Validation {
|
||||
private final JsonMap<DownloadType, DownloadInfo> downloads;
|
||||
private final JsonMap<DownloadType, LoggingInfo> logging;
|
||||
private final ReleaseType type;
|
||||
private final Date time;
|
||||
private final Date releaseTime;
|
||||
private final Instant time;
|
||||
private final Instant releaseTime;
|
||||
private final Integer minimumLauncherVersion;
|
||||
private final Integer tlauncherVersion;
|
||||
|
||||
public TLauncherVersion(String id, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, @Nullable GameJavaVersion javaVersion, List<TLauncherLibrary> libraries, List<CompatibilityRule> compatibilityRules, JsonMap<DownloadType, DownloadInfo> downloads, JsonMap<DownloadType, LoggingInfo> logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Integer tlauncherVersion) {
|
||||
public TLauncherVersion(String id, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, @Nullable GameJavaVersion javaVersion, List<TLauncherLibrary> libraries, List<CompatibilityRule> compatibilityRules, JsonMap<DownloadType, DownloadInfo> downloads, JsonMap<DownloadType, LoggingInfo> logging, ReleaseType type, Instant time, Instant releaseTime, Integer minimumLauncherVersion, Integer tlauncherVersion) {
|
||||
this.id = id;
|
||||
this.minecraftArguments = minecraftArguments;
|
||||
this.arguments = arguments;
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.io.Unzipper;
|
||||
import org.jackhuang.hmcl.util.platform.Bits;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -230,7 +230,7 @@ public class DefaultLauncher extends Launcher {
|
||||
Path tempNativeFolder = null;
|
||||
if ((OperatingSystem.CURRENT_OS == OperatingSystem.LINUX || OperatingSystem.CURRENT_OS == OperatingSystem.OSX)
|
||||
&& !StringUtils.isASCII(nativeFolderPath)
|
||||
&& gameVersion.isPresent() && VersionNumber.compare(gameVersion.get(), "1.19") < 0) {
|
||||
&& gameVersion.isPresent() && GameVersionNumber.compare(gameVersion.get(), "1.19") < 0) {
|
||||
tempNativeFolder = Paths.get("/", "tmp", "hmcl-natives-" + UUID.randomUUID());
|
||||
nativeFolderPath = tempNativeFolder + File.pathSeparator + nativeFolderPath;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
if (StringUtils.isNotBlank(options.getServerIp())) {
|
||||
String[] args = options.getServerIp().split(":");
|
||||
if (VersionNumber.compare(gameVersion.orElse("0.0"), "1.20") < 0) {
|
||||
if (GameVersionNumber.asGameVersion(gameVersion).compareTo("1.20") < 0) {
|
||||
res.add("--server");
|
||||
res.add(args[0]);
|
||||
res.add("--port");
|
||||
@@ -357,7 +357,7 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
|
||||
private boolean isUsingLog4j() {
|
||||
return VersionNumber.compare(repository.getGameVersion(version).orElse("1.7"), "1.7") >= 0;
|
||||
return GameVersionNumber.compare(repository.getGameVersion(version).orElse("1.7"), "1.7") >= 0;
|
||||
}
|
||||
|
||||
public File getLog4jConfigurationFile() {
|
||||
@@ -367,7 +367,7 @@ public class DefaultLauncher extends Launcher {
|
||||
public void extractLog4jConfigurationFile() throws IOException {
|
||||
File targetFile = getLog4jConfigurationFile();
|
||||
InputStream source;
|
||||
if (VersionNumber.compare(repository.getGameVersion(version).orElse("0.0"), "1.12") < 0) {
|
||||
if (GameVersionNumber.asGameVersion(repository.getGameVersion(version)).compareTo("1.12") < 0) {
|
||||
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.7.xml");
|
||||
} else {
|
||||
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.12.xml");
|
||||
|
||||
@@ -69,7 +69,10 @@ final class ExitWaiter implements Runnable {
|
||||
"A fatal exception has occurred. Program will exit.")) {
|
||||
EventBus.EVENT_BUS.fireEvent(new JVMLaunchFailedEvent(this, process));
|
||||
exitType = ProcessListener.ExitType.JVM_ERROR;
|
||||
} else if (exitCode != 0 || StringUtils.containsOne(errorLines, "Unable to launch")) {
|
||||
} else if (exitCode != 0 || StringUtils.containsOne(errorLines,
|
||||
"Someone is closing me!",
|
||||
"Crash report saved to", "Could not save crash report to", "This crash report has been saved to:",
|
||||
"Unable to launch", "An exception was thrown, the game will display an error screen and halt.")) {
|
||||
EventBus.EVENT_BUS.fireEvent(new ProcessExitedAbnormallyEvent(this, process));
|
||||
|
||||
if (exitCode == 137 && OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
@@ -218,14 +218,14 @@ public class RemoteMod {
|
||||
private final String name;
|
||||
private final String version;
|
||||
private final String changelog;
|
||||
private final Date datePublished;
|
||||
private final Instant datePublished;
|
||||
private final VersionType versionType;
|
||||
private final File file;
|
||||
private final List<Dependency> dependencies;
|
||||
private final List<String> gameVersions;
|
||||
private final List<ModLoaderType> loaders;
|
||||
|
||||
public Version(IVersion self, String modid, String name, String version, String changelog, Date datePublished, VersionType versionType, File file, List<Dependency> dependencies, List<String> gameVersions, List<ModLoaderType> loaders) {
|
||||
public Version(IVersion self, String modid, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List<Dependency> dependencies, List<String> gameVersions, List<ModLoaderType> loaders) {
|
||||
this.self = self;
|
||||
this.modid = modid;
|
||||
this.name = name;
|
||||
@@ -259,7 +259,7 @@ public class RemoteMod {
|
||||
return changelog;
|
||||
}
|
||||
|
||||
public Date getDatePublished() {
|
||||
public Instant getDatePublished() {
|
||||
return datePublished;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,28 +119,4 @@ public interface RemoteModRepository {
|
||||
return subcategories;
|
||||
}
|
||||
}
|
||||
|
||||
String[] DEFAULT_GAME_VERSIONS = new String[]{
|
||||
"1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20",
|
||||
"1.19.4", "1.19.3", "1.19.2", "1.19.1", "1.19",
|
||||
"1.18.2", "1.18.1", "1.18",
|
||||
"1.17.1", "1.17",
|
||||
"1.16.5", "1.16.4", "1.16.3", "1.16.2", "1.16.1", "1.16",
|
||||
"1.15.2", "1.15.1", "1.15",
|
||||
"1.14.4", "1.14.3", "1.14.2", "1.14.1", "1.14",
|
||||
"1.13.2", "1.13.1", "1.13",
|
||||
"1.12.2", "1.12.1", "1.12",
|
||||
"1.11.2", "1.11.1", "1.11",
|
||||
"1.10.2", "1.10.1", "1.10",
|
||||
"1.9.4", "1.9.3", "1.9.2", "1.9.1", "1.9",
|
||||
"1.8.9", "1.8.8", "1.8.7", "1.8.6", "1.8.5", "1.8.4", "1.8.3", "1.8.2", "1.8.1", "1.8",
|
||||
"1.7.10", "1.7.9", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2",
|
||||
"1.6.4", "1.6.2", "1.6.1",
|
||||
"1.5.2", "1.5.1",
|
||||
"1.4.7", "1.4.6", "1.4.5", "1.4.4", "1.4.2",
|
||||
"1.3.2", "1.3.1",
|
||||
"1.2.5", "1.2.4", "1.2.3", "1.2.2", "1.2.1",
|
||||
"1.1",
|
||||
"1.0"
|
||||
};
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -58,15 +59,15 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
private final int mainFileId;
|
||||
private final List<LatestFile> latestFiles;
|
||||
private final List<LatestFileIndex> latestFileIndices;
|
||||
private final Date dateCreated;
|
||||
private final Date dateModified;
|
||||
private final Date dateReleased;
|
||||
private final Instant dateCreated;
|
||||
private final Instant dateModified;
|
||||
private final Instant dateReleased;
|
||||
private final boolean allowModDistribution;
|
||||
private final int gamePopularityRank;
|
||||
private final boolean isAvailable;
|
||||
private final int thumbsUpCount;
|
||||
|
||||
public CurseAddon(int id, int gameId, String name, String slug, Links links, String summary, int status, int downloadCount, boolean isFeatured, int primaryCategoryId, List<Category> categories, int classId, List<Author> authors, Logo logo, int mainFileId, List<LatestFile> latestFiles, List<LatestFileIndex> latestFileIndices, Date dateCreated, Date dateModified, Date dateReleased, boolean allowModDistribution, int gamePopularityRank, boolean isAvailable, int thumbsUpCount) {
|
||||
public CurseAddon(int id, int gameId, String name, String slug, Links links, String summary, int status, int downloadCount, boolean isFeatured, int primaryCategoryId, List<Category> categories, int classId, List<Author> authors, Logo logo, int mainFileId, List<LatestFile> latestFiles, List<LatestFileIndex> latestFileIndices, Instant dateCreated, Instant dateModified, Instant dateReleased, boolean allowModDistribution, int gamePopularityRank, boolean isAvailable, int thumbsUpCount) {
|
||||
this.id = id;
|
||||
this.gameId = gameId;
|
||||
this.name = name;
|
||||
@@ -161,15 +162,15 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
return latestFileIndices;
|
||||
}
|
||||
|
||||
public Date getDateCreated() {
|
||||
public Instant getDateCreated() {
|
||||
return dateCreated;
|
||||
}
|
||||
|
||||
public Date getDateModified() {
|
||||
public Instant getDateModified() {
|
||||
return dateModified;
|
||||
}
|
||||
|
||||
public Date getDateReleased() {
|
||||
public Instant getDateReleased() {
|
||||
return dateReleased;
|
||||
}
|
||||
|
||||
@@ -438,7 +439,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
private final int releaseType;
|
||||
private final int fileStatus;
|
||||
private final List<LatestFileHash> hashes;
|
||||
private final Date fileDate;
|
||||
private final Instant fileDate;
|
||||
private final int fileLength;
|
||||
private final int downloadCount;
|
||||
private final String downloadUrl;
|
||||
@@ -448,7 +449,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
private final boolean isServerPack;
|
||||
private final long fileFingerprint;
|
||||
|
||||
public LatestFile(int id, int gameId, int modId, boolean isAvailable, String displayName, String fileName, int releaseType, int fileStatus, List<LatestFileHash> hashes, Date fileDate, int fileLength, int downloadCount, String downloadUrl, List<String> gameVersions, List<Dependency> dependencies, int alternateFileId, boolean isServerPack, long fileFingerprint) {
|
||||
public LatestFile(int id, int gameId, int modId, boolean isAvailable, String displayName, String fileName, int releaseType, int fileStatus, List<LatestFileHash> hashes, Instant fileDate, int fileLength, int downloadCount, String downloadUrl, List<String> gameVersions, List<Dependency> dependencies, int alternateFileId, boolean isServerPack, long fileFingerprint) {
|
||||
this.id = id;
|
||||
this.gameId = gameId;
|
||||
this.modId = modId;
|
||||
@@ -505,7 +506,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
public Date getFileDate() {
|
||||
public Instant getFileDate() {
|
||||
return fileDate;
|
||||
}
|
||||
|
||||
@@ -650,7 +651,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
private final String slug;
|
||||
private final String url;
|
||||
private final String iconUrl;
|
||||
private final Date dateModified;
|
||||
private final Instant dateModified;
|
||||
private final boolean isClass;
|
||||
private final int classId;
|
||||
private final int parentCategoryId;
|
||||
@@ -658,10 +659,10 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
private transient final List<Category> subcategories;
|
||||
|
||||
public Category() {
|
||||
this(0, 0, "", "", "", "", new Date(), false, 0, 0);
|
||||
this(0, 0, "", "", "", "", Instant.now(), false, 0, 0);
|
||||
}
|
||||
|
||||
public Category(int id, int gameId, String name, String slug, String url, String iconUrl, Date dateModified, boolean isClass, int classId, int parentCategoryId) {
|
||||
public Category(int id, int gameId, String name, String slug, String url, String iconUrl, Instant dateModified, boolean isClass, int classId, int parentCategoryId) {
|
||||
this.id = id;
|
||||
this.gameId = gameId;
|
||||
this.name = name;
|
||||
@@ -700,7 +701,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public Date getDateModified() {
|
||||
public Instant getDateModified() {
|
||||
return dateModified;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Complete the CurseForge version.
|
||||
@@ -53,7 +53,7 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
private final ModManager modManager;
|
||||
private final String version;
|
||||
private CurseManifest manifest;
|
||||
private final List<Task<?>> dependencies = new ArrayList<>();
|
||||
private List<Task<?>> dependencies;
|
||||
|
||||
private final AtomicBoolean allNameKnown = new AtomicBoolean(true);
|
||||
private final AtomicInteger finished = new AtomicInteger(0);
|
||||
@@ -138,28 +138,31 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
.collect(Collectors.toList()));
|
||||
FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(newManifest));
|
||||
|
||||
File resourcePacks = new File(repository.getVersionRoot(modManager.getVersion()), "resourcepacks");
|
||||
for (CurseManifestFile file : newManifest.getFiles())
|
||||
if (StringUtils.isNotBlank(file.getFileName())) {
|
||||
RemoteMod mod = CurseForgeRemoteModRepository.MODS.getModById(Integer.toString(file.getProjectID()));
|
||||
File target;
|
||||
if (((CurseAddon) mod.getData()).getClassId() == 12) {
|
||||
target = new File(resourcePacks, file.getFileName());
|
||||
if (target.exists()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (modManager.hasSimpleMod(file.getFileName())) {
|
||||
continue;
|
||||
}
|
||||
target = modManager.getSimpleModPath(file.getFileName()).toFile();
|
||||
}
|
||||
File versionRoot = repository.getVersionRoot(modManager.getVersion());
|
||||
File resourcePacksRoot = new File(versionRoot, "resourcepacks"), shaderPacksRoot = new File(versionRoot, "shaderpacks");
|
||||
finished.set(0);
|
||||
dependencies = newManifest.getFiles()
|
||||
.stream().parallel()
|
||||
.filter(f -> f.getFileName() != null)
|
||||
.flatMap(f -> {
|
||||
try {
|
||||
File path = guessFilePath(f, resourcePacksRoot, shaderPacksRoot);
|
||||
if (path == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
FileDownloadTask task = new FileDownloadTask(file.getUrl(), target);
|
||||
task.setCacheRepository(dependency.getCacheRepository());
|
||||
task.setCaching(true);
|
||||
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||
}
|
||||
FileDownloadTask task = new FileDownloadTask(f.getUrl(), path);
|
||||
task.setCacheRepository(dependency.getCacheRepository());
|
||||
task.setCaching(true);
|
||||
return Stream.of(task.withCounter("hmcl.modpack.download"));
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Could not query api.curseforge.com for mod: " + f.getProjectID() + ", " + f.getFileID(), e);
|
||||
return Stream.empty(); // Ignore this file.
|
||||
} finally {
|
||||
updateProgress(finished.incrementAndGet(), newManifest.getFiles().size());
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!dependencies.isEmpty()) {
|
||||
getProperties().put("total", dependencies.size());
|
||||
@@ -167,6 +170,33 @@ public final class CurseCompletionTask extends Task<Void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess where to store the file.
|
||||
* @param file The file.
|
||||
* @param resourcePacksRoot ./resourcepacks.
|
||||
* @param shaderPacksRoot ./shaderpacks.
|
||||
* @return ./resourcepacks/$filename or ./shaderpacks/$filename or ./mods/$filename if the file doesn't exist. null if the file existed.
|
||||
* @throws IOException If IOException was encountered during getting data from CurseForge.
|
||||
*/
|
||||
private File guessFilePath(CurseManifestFile file, File resourcePacksRoot, File shaderPacksRoot) throws IOException {
|
||||
RemoteMod mod = CurseForgeRemoteModRepository.MODS.getModById(Integer.toString(file.getProjectID()));
|
||||
int classID = ((CurseAddon) mod.getData()).getClassId();
|
||||
String fileName = file.getFileName();
|
||||
switch (classID) {
|
||||
case 12: // Resource pack
|
||||
case 6552: { // Shader pack
|
||||
File res = new File(classID == 12 ? resourcePacksRoot : shaderPacksRoot, fileName);
|
||||
return res.exists() ? null : res;
|
||||
}
|
||||
default: {
|
||||
if (modManager.hasSimpleMod(fileName)) {
|
||||
return null;
|
||||
}
|
||||
return modManager.getSimpleModPath(fileName).toFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doPostExecute() {
|
||||
return true;
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.io.InputStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -33,10 +33,6 @@ public final class ForgeNewModMetadata {
|
||||
|
||||
private final List<Mod> mods;
|
||||
|
||||
public ForgeNewModMetadata() {
|
||||
this("", "", "", "", Collections.emptyList());
|
||||
}
|
||||
|
||||
public ForgeNewModMetadata(String modLoader, String loaderVersion, String logoFile, String license, List<Mod> mods) {
|
||||
this.modLoader = modLoader;
|
||||
this.loaderVersion = loaderVersion;
|
||||
@@ -120,8 +116,9 @@ public final class ForgeNewModMetadata {
|
||||
public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException {
|
||||
Path modstoml = fs.getPath("META-INF/mods.toml");
|
||||
if (Files.notExists(modstoml))
|
||||
throw new IOException("File " + modFile + " is not a Forge 1.13+ mod.");
|
||||
ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class);
|
||||
throw new IOException("File " + modFile + " is not a Forge 1.13+ or NeoForge mod.");
|
||||
Toml toml = new Toml().read(FileUtils.readText(modstoml));
|
||||
ForgeNewModMetadata metadata = toml.to(ForgeNewModMetadata.class);
|
||||
if (metadata == null || metadata.getMods().isEmpty())
|
||||
throw new IOException("Mod " + modFile + " `mods.toml` is malformed..");
|
||||
Mod mod = metadata.getMods().get(0);
|
||||
@@ -135,9 +132,29 @@ public final class ForgeNewModMetadata {
|
||||
LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile);
|
||||
}
|
||||
}
|
||||
return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), ModLoaderType.FORGE), modFile, mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()),
|
||||
|
||||
ModLoaderType modLoaderType = analyzeModLoader(toml, mod.getModId());
|
||||
if (modLoaderType == null) {
|
||||
throw new IOException("File " + modFile + " is not a Forge 1.13+ or NeoForge mod.");
|
||||
}
|
||||
|
||||
return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), modLoaderType), modFile, mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()),
|
||||
mod.getAuthors(), jarVersion == null ? mod.getVersion() : mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
|
||||
mod.getDisplayURL(),
|
||||
metadata.getLogoFile());
|
||||
}
|
||||
|
||||
private static ModLoaderType analyzeModLoader(Toml toml, String modID) {
|
||||
List<HashMap<String, Object>> dependencies = toml.getList("dependencies." + modID);
|
||||
if (dependencies != null) {
|
||||
for (HashMap<String, Object> dependency : dependencies) {
|
||||
switch ((String) dependency.get("modId")) {
|
||||
case "forge": return ModLoaderType.FORGE;
|
||||
case "neoforge": return ModLoaderType.NEO_FORGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+14
-13
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -210,13 +211,13 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
|
||||
private final String team;
|
||||
|
||||
private final Date published;
|
||||
private final Instant published;
|
||||
|
||||
private final Date updated;
|
||||
private final Instant updated;
|
||||
|
||||
private final List<String> versions;
|
||||
|
||||
public Project(String slug, String title, String description, List<String> categories, String body, String projectType, int downloads, String iconUrl, String id, String team, Date published, Date updated, List<String> versions) {
|
||||
public Project(String slug, String title, String description, List<String> categories, String body, String projectType, int downloads, String iconUrl, String id, String team, Instant published, Instant updated, List<String> versions) {
|
||||
this.slug = slug;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
@@ -272,11 +273,11 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
return team;
|
||||
}
|
||||
|
||||
public Date getPublished() {
|
||||
public Instant getPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public Date getUpdated() {
|
||||
public Instant getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -381,7 +382,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
private final String authorId;
|
||||
|
||||
@SerializedName("date_published")
|
||||
private final Date datePublished;
|
||||
private final Instant datePublished;
|
||||
|
||||
private final int downloads;
|
||||
|
||||
@@ -390,7 +391,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
|
||||
private final List<ProjectVersionFile> files;
|
||||
|
||||
public ProjectVersion(String name, String versionNumber, String changelog, List<Dependency> dependencies, List<String> gameVersions, String versionType, List<String> loaders, boolean featured, String id, String projectId, String authorId, Date datePublished, int downloads, String changelogUrl, List<ProjectVersionFile> files) {
|
||||
public ProjectVersion(String name, String versionNumber, String changelog, List<Dependency> dependencies, List<String> gameVersions, String versionType, List<String> loaders, boolean featured, String id, String projectId, String authorId, Instant datePublished, int downloads, String changelogUrl, List<ProjectVersionFile> files) {
|
||||
this.name = name;
|
||||
this.versionNumber = versionNumber;
|
||||
this.changelog = changelog;
|
||||
@@ -452,7 +453,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
return authorId;
|
||||
}
|
||||
|
||||
public Date getDatePublished() {
|
||||
public Instant getDatePublished() {
|
||||
return datePublished;
|
||||
}
|
||||
|
||||
@@ -587,15 +588,15 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
private final List<String> versions;
|
||||
|
||||
@SerializedName("date_created")
|
||||
private final Date dateCreated;
|
||||
private final Instant dateCreated;
|
||||
|
||||
@SerializedName("date_modified")
|
||||
private final Date dateModified;
|
||||
private final Instant dateModified;
|
||||
|
||||
@SerializedName("latest_version")
|
||||
private final String latestVersion;
|
||||
|
||||
public ProjectSearchResult(String slug, String title, String description, List<String> categories, String projectType, int downloads, String iconUrl, String projectId, String author, List<String> versions, Date dateCreated, Date dateModified, String latestVersion) {
|
||||
public ProjectSearchResult(String slug, String title, String description, List<String> categories, String projectType, int downloads, String iconUrl, String projectId, String author, List<String> versions, Instant dateCreated, Instant dateModified, String latestVersion) {
|
||||
this.slug = slug;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
@@ -651,11 +652,11 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
||||
return versions;
|
||||
}
|
||||
|
||||
public Date getDateCreated() {
|
||||
public Instant getDateCreated() {
|
||||
return dateCreated;
|
||||
}
|
||||
|
||||
public Date getDateModified() {
|
||||
public Instant getDateModified() {
|
||||
return dateModified;
|
||||
}
|
||||
|
||||
|
||||
@@ -301,9 +301,16 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||
concurrency = Math.max(concurrency, 1);
|
||||
synchronized (Schedulers.class) {
|
||||
downloadExecutorConcurrency = concurrency;
|
||||
if (DOWNLOAD_EXECUTOR != null) {
|
||||
DOWNLOAD_EXECUTOR.setCorePoolSize(concurrency);
|
||||
DOWNLOAD_EXECUTOR.setMaximumPoolSize(concurrency);
|
||||
|
||||
ThreadPoolExecutor downloadExecutor = DOWNLOAD_EXECUTOR;
|
||||
if (downloadExecutor != null) {
|
||||
if (downloadExecutor.getMaximumPoolSize() <= concurrency) {
|
||||
downloadExecutor.setMaximumPoolSize(concurrency);
|
||||
downloadExecutor.setCorePoolSize(concurrency);
|
||||
} else {
|
||||
downloadExecutor.setCorePoolSize(concurrency);
|
||||
downloadExecutor.setMaximumPoolSize(concurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ public class CacheRepository {
|
||||
}
|
||||
|
||||
public void saveETagIndex() throws IOException {
|
||||
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
try (FileChannel channel = FileChannel.open(indexFile, StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
FileLock lock = channel.lock();
|
||||
try {
|
||||
ETagIndex indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
|
||||
|
||||
@@ -22,9 +22,10 @@ import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
@@ -120,35 +121,39 @@ public final class Logging {
|
||||
}
|
||||
}
|
||||
|
||||
private static final MessageFormat FORMAT = new MessageFormat("[{0,date,HH:mm:ss}] [{1}.{2}/{3}] {4}\n");
|
||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.systemDefault());
|
||||
|
||||
private static String format(LogRecord record) {
|
||||
String message = filterForbiddenToken(record.getMessage());
|
||||
|
||||
StringBuilder builder = new StringBuilder(128 + message.length());
|
||||
builder.append('[');
|
||||
TIME_FORMATTER.formatTo(Instant.ofEpochMilli(record.getMillis()), builder);
|
||||
builder.append(']');
|
||||
|
||||
builder.append(" [")
|
||||
.append(record.getSourceClassName())
|
||||
.append('.')
|
||||
.append(record.getSourceMethodName())
|
||||
.append('/')
|
||||
.append(record.getLevel().getName())
|
||||
.append("] ")
|
||||
.append(message)
|
||||
.append('\n');
|
||||
|
||||
|
||||
Throwable thrown = record.getThrown();
|
||||
|
||||
StringWriter writer;
|
||||
StringBuffer buffer;
|
||||
if (thrown == null) {
|
||||
writer = null;
|
||||
buffer = new StringBuffer(256);
|
||||
return builder.toString();
|
||||
} else {
|
||||
writer = new StringWriter(1024);
|
||||
buffer = writer.getBuffer();
|
||||
}
|
||||
|
||||
FORMAT.format(new Object[]{
|
||||
new Date(record.getMillis()),
|
||||
record.getSourceClassName(), record.getSourceMethodName(), record.getLevel().getName(),
|
||||
message
|
||||
}, buffer, null);
|
||||
|
||||
if (thrown != null) {
|
||||
StringWriter writer = new StringWriter(builder.length() + 2048);
|
||||
writer.getBuffer().append(builder);
|
||||
try (PrintWriter printWriter = new PrintWriter(writer)) {
|
||||
thrown.printStackTrace(printWriter);
|
||||
}
|
||||
|
||||
return writer.toString();
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static final class DefaultFormatter extends Formatter {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2020 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.util.gson;
|
||||
|
||||
import com.google.gson.*;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||
|
||||
public static final DateTypeAdapter INSTANCE = new DateTypeAdapter();
|
||||
|
||||
private DateTypeAdapter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Date t, Type type, JsonSerializationContext jsc) {
|
||||
synchronized (EN_US_FORMAT) {
|
||||
return new JsonPrimitive(serializeToString(t));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (!(json instanceof JsonPrimitive))
|
||||
throw new JsonParseException("The date should be a string value");
|
||||
else {
|
||||
Date date = deserializeToDate(json.getAsString());
|
||||
if (type == Date.class)
|
||||
return date;
|
||||
else
|
||||
throw new IllegalArgumentException(this.getClass().toString() + " cannot be deserialized to " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public static final DateFormat EN_US_FORMAT = DateFormat.getDateTimeInstance(2, 2, Locale.US);
|
||||
public static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
|
||||
|
||||
public static final DateTimeFormatter ISO_DATE_TIME = new DateTimeFormatterBuilder()
|
||||
.append(ISO_LOCAL_DATE_TIME)
|
||||
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
|
||||
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
|
||||
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
|
||||
.optionalStart().appendOffsetId().optionalEnd()
|
||||
.toFormatter();
|
||||
|
||||
public static Date deserializeToDate(String string) {
|
||||
synchronized (EN_US_FORMAT) {
|
||||
try {
|
||||
return EN_US_FORMAT.parse(string);
|
||||
} catch (ParseException ex1) {
|
||||
try {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.parse(string, ISO_DATE_TIME);
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
} catch (DateTimeParseException e) {
|
||||
try {
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(string, ISO_LOCAL_DATE_TIME);
|
||||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||
} catch (DateTimeParseException e2) {
|
||||
throw new JsonParseException("Invalid date: " + string, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String serializeToString(Date date) {
|
||||
synchronized (EN_US_FORMAT) {
|
||||
String result = ISO_8601_FORMAT.format(date);
|
||||
return result.substring(0, 22) + ":" + result.substring(22);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,9 +20,15 @@ package org.jackhuang.hmcl.util.gson;
|
||||
import com.google.gson.*;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
public final class InstantTypeAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
|
||||
public static final InstantTypeAdapter INSTANCE = new InstantTypeAdapter();
|
||||
@@ -31,21 +37,53 @@ public final class InstantTypeAdapter implements JsonSerializer<Instant>, JsonDe
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (!(json instanceof JsonPrimitive)) {
|
||||
public JsonElement serialize(Instant t, Type type, JsonSerializationContext jsc) {
|
||||
return new JsonPrimitive(serializeToString(t, ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
if (!(json instanceof JsonPrimitive))
|
||||
throw new JsonParseException("The instant should be a string value");
|
||||
} else {
|
||||
Instant instant = Instant.parse(json.getAsString());
|
||||
if (typeOfT == Instant.class) {
|
||||
return instant;
|
||||
} else {
|
||||
throw new IllegalArgumentException(this.getClass() + " cannot be deserialized to " + typeOfT);
|
||||
else {
|
||||
Instant time = deserializeToInstant(json.getAsString());
|
||||
if (type == Instant.class)
|
||||
return time;
|
||||
else
|
||||
throw new IllegalArgumentException(this.getClass() + " cannot be deserialized to " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter EN_US_FORMAT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM)
|
||||
.withLocale(Locale.US)
|
||||
.withZone(ZoneId.systemDefault());
|
||||
private static final DateTimeFormatter ISO_DATE_TIME = new DateTimeFormatterBuilder()
|
||||
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
|
||||
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
|
||||
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
|
||||
.optionalStart().appendOffsetId().optionalEnd()
|
||||
.toFormatter();
|
||||
|
||||
public static Instant deserializeToInstant(String string) {
|
||||
try {
|
||||
return ZonedDateTime.parse(string, EN_US_FORMAT).toInstant();
|
||||
} catch (DateTimeParseException ex1) {
|
||||
try {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.parse(string, ISO_DATE_TIME);
|
||||
return zonedDateTime.toInstant();
|
||||
} catch (DateTimeParseException e) {
|
||||
try {
|
||||
LocalDateTime localDateTime = LocalDateTime.parse(string, ISO_LOCAL_DATE_TIME);
|
||||
return localDateTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
} catch (DateTimeParseException e2) {
|
||||
throw new JsonParseException("Invalid instant: " + string, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()).format(src));
|
||||
public static String serializeToString(Instant instant, ZoneId zone) {
|
||||
return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(ZonedDateTime.ofInstant(instant, zone).truncatedTo(ChronoUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -113,7 +112,6 @@ public final class JsonUtils {
|
||||
.enableComplexMapKeySerialization()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(Instant.class, InstantTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(Date.class, DateTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE)
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class GameVersionNumber implements Comparable<GameVersionNumber> {
|
||||
|
||||
public static String[] getDefaultGameVersions() {
|
||||
return Versions.DEFAULT_GAME_VERSIONS;
|
||||
}
|
||||
|
||||
public static GameVersionNumber asGameVersion(String version) {
|
||||
try {
|
||||
if (!version.isEmpty()) {
|
||||
char ch = version.charAt(0);
|
||||
switch (ch) {
|
||||
case 'r':
|
||||
return Old.parsePreClassic(version);
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
return Old.parseAlphaBetaClassic(version);
|
||||
case 'i':
|
||||
return Old.parseInfdev(version);
|
||||
}
|
||||
|
||||
if (version.equals("0.0")) {
|
||||
return Release.ZERO;
|
||||
}
|
||||
|
||||
if (version.startsWith("1.")) {
|
||||
return Release.parse(version);
|
||||
}
|
||||
|
||||
if (version.length() == 6 && version.charAt(2) == 'w') {
|
||||
return Snapshot.parse(version);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
}
|
||||
|
||||
Special special = Versions.SPECIALS.get(version);
|
||||
if (special == null) {
|
||||
special = new Special(version);
|
||||
}
|
||||
return special;
|
||||
}
|
||||
|
||||
public static GameVersionNumber asGameVersion(Optional<String> version) {
|
||||
return version.isPresent() ? asGameVersion(version.get()) : unknown();
|
||||
}
|
||||
|
||||
public static GameVersionNumber unknown() {
|
||||
return Release.ZERO;
|
||||
}
|
||||
|
||||
public static int compare(String version1, String version2) {
|
||||
return asGameVersion(version1).compareTo(asGameVersion(version2));
|
||||
}
|
||||
|
||||
public static VersionRange<GameVersionNumber> between(String minimum, String maximum) {
|
||||
return VersionRange.between(asGameVersion(minimum), asGameVersion(maximum));
|
||||
}
|
||||
|
||||
public static VersionRange<GameVersionNumber> atLeast(String minimum) {
|
||||
return VersionRange.atLeast(asGameVersion(minimum));
|
||||
}
|
||||
|
||||
public static VersionRange<GameVersionNumber> atMost(String maximum) {
|
||||
return VersionRange.atMost(asGameVersion(maximum));
|
||||
}
|
||||
|
||||
final String value;
|
||||
|
||||
GameVersionNumber(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
PRE_CLASSIC, CLASSIC, INFDEV, ALPHA, BETA, NEW
|
||||
}
|
||||
|
||||
abstract Type getType();
|
||||
|
||||
abstract int compareToImpl(@NotNull GameVersionNumber other);
|
||||
|
||||
public int compareTo(@NotNull String other) {
|
||||
return this.compareTo(asGameVersion(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull GameVersionNumber other) {
|
||||
if (this.getType() != other.getType()) {
|
||||
return Integer.compare(this.getType().ordinal(), other.getType().ordinal());
|
||||
}
|
||||
|
||||
return compareToImpl(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
static final class Old extends GameVersionNumber {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("[abc](?<major>[0-9]+)\\.(?<minor>[0-9]+)(\\.(?<patch>[0-9]+))?([^0-9]*(?<additional>[0-9]+).*)?");
|
||||
|
||||
static Old parsePreClassic(String value) {
|
||||
int version;
|
||||
try {
|
||||
version = Integer.parseInt(value.substring("rd-".length()));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return new Old(value, Type.PRE_CLASSIC, version, 0, 0, 0);
|
||||
}
|
||||
|
||||
static Old parseAlphaBetaClassic(String value) {
|
||||
Matcher matcher = PATTERN.matcher(value);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
Type type;
|
||||
switch (value.charAt(0)) {
|
||||
case 'a':
|
||||
type = Type.ALPHA;
|
||||
break;
|
||||
case 'b':
|
||||
type = Type.BETA;
|
||||
break;
|
||||
case 'c':
|
||||
type = Type.CLASSIC;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(value);
|
||||
}
|
||||
|
||||
int major = Integer.parseInt(matcher.group("major"));
|
||||
int minor = Integer.parseInt(matcher.group("minor"));
|
||||
|
||||
String patchString = matcher.group("patch");
|
||||
int patch = patchString != null ? Integer.parseInt(patchString) : 0;
|
||||
|
||||
String additionalString = matcher.group("additional");
|
||||
int additional = additionalString != null ? Integer.parseInt(additionalString) : 0;
|
||||
|
||||
return new Old(value, type, major, minor, patch, additional);
|
||||
}
|
||||
|
||||
static Old parseInfdev(String value) {
|
||||
String version = value.substring("inf-".length());
|
||||
int major;
|
||||
int patch;
|
||||
|
||||
try {
|
||||
major = Integer.parseInt(version);
|
||||
patch = 0;
|
||||
} catch (NumberFormatException e) {
|
||||
int idx = version.indexOf('-');
|
||||
if (idx >= 0) {
|
||||
try {
|
||||
major = Integer.parseInt(version.substring(0, idx));
|
||||
patch = Integer.parseInt(version.substring(idx + 1));
|
||||
} catch (NumberFormatException ignore) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
}
|
||||
|
||||
return new Old(value, Type.INFDEV, major, 0, patch, 0);
|
||||
}
|
||||
|
||||
|
||||
final Type type;
|
||||
final int major;
|
||||
final int minor;
|
||||
final int patch;
|
||||
final int additional;
|
||||
|
||||
private Old(String value, Type type, int major, int minor, int patch, int additional) {
|
||||
super(value);
|
||||
this.type = type;
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.additional = additional;
|
||||
}
|
||||
|
||||
@Override
|
||||
Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber other) {
|
||||
Old that = (Old) other;
|
||||
int c = Integer.compare(this.major, that.major);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
c = Integer.compare(this.minor, that.minor);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
c = Integer.compare(this.patch, that.patch);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
return Integer.compare(this.additional, that.additional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Old other = (Old) o;
|
||||
return major == other.major && minor == other.minor && patch == other.patch && additional == other.additional && type == other.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, major, minor, patch, additional);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Release extends GameVersionNumber {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("1\\.(?<minor>[0-9]+)(\\.(?<patch>[0-9]+))?((?<eaType>(-[a-zA-Z]+| Pre-Release ))(?<eaVersion>[0-9]+))?");
|
||||
|
||||
static final int TYPE_GA = Integer.MAX_VALUE;
|
||||
|
||||
static final int TYPE_UNKNOWN = 0;
|
||||
static final int TYPE_EXP = 1;
|
||||
static final int TYPE_PRE = 2;
|
||||
static final int TYPE_RC = 3;
|
||||
|
||||
static final Release ZERO = new Release("0.0", 0, 0, 0, TYPE_GA, 0);
|
||||
|
||||
static Release parse(String value) {
|
||||
Matcher matcher = PATTERN.matcher(value);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
int minor = Integer.parseInt(matcher.group("minor"));
|
||||
|
||||
String patchString = matcher.group("patch");
|
||||
int patch = patchString != null ? Integer.parseInt(patchString) : 0;
|
||||
|
||||
String eaTypeString = matcher.group("eaType");
|
||||
int eaType;
|
||||
if (eaTypeString == null) {
|
||||
eaType = TYPE_GA;
|
||||
} else if ("-pre".equals(eaTypeString) || " Pre-Release ".equals(eaTypeString)) {
|
||||
eaType = TYPE_PRE;
|
||||
} else if ("-rc".equals(eaTypeString)) {
|
||||
eaType = TYPE_RC;
|
||||
} else if ("-exp".equals(eaTypeString)) {
|
||||
eaType = TYPE_EXP;
|
||||
} else {
|
||||
eaType = TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
String eaVersionString = matcher.group("eaVersion");
|
||||
int eaVersion = eaVersionString == null ? 0 : Integer.parseInt(eaVersionString);
|
||||
|
||||
return new Release(value, 1, minor, patch, eaType, eaVersion);
|
||||
}
|
||||
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int patch;
|
||||
|
||||
private final int eaType;
|
||||
private final int eaVersion;
|
||||
|
||||
Release(String value, int major, int minor, int patch, int eaType, int eaVersion) {
|
||||
super(value);
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.eaType = eaType;
|
||||
this.eaVersion = eaVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
Type getType() {
|
||||
return Type.NEW;
|
||||
}
|
||||
|
||||
int compareToRelease(Release other) {
|
||||
int c = Integer.compare(this.major, other.major);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
c = Integer.compare(this.minor, other.minor);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
c = Integer.compare(this.patch, other.patch);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
c = Integer.compare(this.eaType, other.eaType);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
return Integer.compare(this.eaVersion, other.eaVersion);
|
||||
}
|
||||
|
||||
int compareToSnapshot(Snapshot other) {
|
||||
int idx = Arrays.binarySearch(Versions.SNAPSHOT_INTS, other.intValue);
|
||||
if (idx >= 0) {
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
idx = -(idx + 1);
|
||||
if (idx == Versions.SNAPSHOT_INTS.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.compareToRelease(Versions.SNAPSHOT_PREV[idx]) <= 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber other) {
|
||||
if (other instanceof Release) {
|
||||
return compareToRelease((Release) other);
|
||||
}
|
||||
|
||||
if (other instanceof Snapshot) {
|
||||
return compareToSnapshot((Snapshot) other);
|
||||
}
|
||||
|
||||
if (other instanceof Special) {
|
||||
return -((Special) other).compareToReleaseOrSnapshot(this);
|
||||
}
|
||||
|
||||
throw new AssertionError(other.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(major, minor, patch, eaType, eaVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Release other = (Release) o;
|
||||
return major == other.major && minor == other.minor && patch == other.patch && eaType == other.eaType && eaVersion == other.eaVersion;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Snapshot extends GameVersionNumber {
|
||||
static Snapshot parse(String value) {
|
||||
if (value.length() != 6 || value.charAt(2) != 'w') {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
int year;
|
||||
int week;
|
||||
try {
|
||||
year = Integer.parseInt(value.substring(0, 2));
|
||||
week = Integer.parseInt(value.substring(3, 5));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
char suffix = value.charAt(5);
|
||||
if ((suffix < 'a' || suffix > 'z') && suffix != '~') {
|
||||
throw new IllegalArgumentException(value);
|
||||
}
|
||||
|
||||
return new Snapshot(value, year, week, suffix);
|
||||
}
|
||||
|
||||
static int toInt(int year, int week, char suffix) {
|
||||
return (year << 16) | (week << 8) | suffix;
|
||||
}
|
||||
|
||||
final int intValue;
|
||||
|
||||
Snapshot(String value, int year, int week, char suffix) {
|
||||
super(value);
|
||||
this.intValue = toInt(year, week, suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
Type getType() {
|
||||
return Type.NEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber other) {
|
||||
if (other instanceof Release) {
|
||||
return -((Release) other).compareToSnapshot(this);
|
||||
}
|
||||
|
||||
if (other instanceof Snapshot) {
|
||||
return Integer.compare(this.intValue, ((Snapshot) other).intValue);
|
||||
}
|
||||
|
||||
if (other instanceof Special) {
|
||||
return -((Special) other).compareToReleaseOrSnapshot(this);
|
||||
}
|
||||
|
||||
throw new AssertionError(other.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Snapshot other = (Snapshot) o;
|
||||
return this.intValue == other.intValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return intValue;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Special extends GameVersionNumber {
|
||||
private VersionNumber versionNumber;
|
||||
|
||||
private GameVersionNumber prev;
|
||||
|
||||
Special(String value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
Type getType() {
|
||||
return Type.NEW;
|
||||
}
|
||||
|
||||
boolean isUnknown() {
|
||||
return prev == null;
|
||||
}
|
||||
|
||||
VersionNumber asVersionNumber() {
|
||||
if (versionNumber != null) {
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
return versionNumber = VersionNumber.asVersion(value);
|
||||
}
|
||||
|
||||
GameVersionNumber getPrevNormalVersion() {
|
||||
GameVersionNumber v = prev;
|
||||
while (v instanceof Special) {
|
||||
v = ((Special) v).prev;
|
||||
}
|
||||
|
||||
if (v == null) {
|
||||
throw new AssertionError("version: " + value);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int compareToReleaseOrSnapshot(GameVersionNumber other) {
|
||||
if (isUnknown()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getPrevNormalVersion().compareTo(other) >= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int compareToSpecial(Special other) {
|
||||
if (this.isUnknown()) {
|
||||
return other.isUnknown() ? this.asVersionNumber().compareTo(other.asVersionNumber()) : 1;
|
||||
}
|
||||
|
||||
if (other.isUnknown()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (this.value.equals(other.value)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c = this.getPrevNormalVersion().compareTo(other.getPrevNormalVersion());
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
||||
GameVersionNumber v = prev;
|
||||
while (v instanceof Special) {
|
||||
if (v == other) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
v = ((Special) v).prev;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareToImpl(@NotNull GameVersionNumber o) {
|
||||
if (o instanceof Release) {
|
||||
return compareToReleaseOrSnapshot(o);
|
||||
}
|
||||
|
||||
if (o instanceof Snapshot) {
|
||||
return compareToReleaseOrSnapshot(o);
|
||||
}
|
||||
|
||||
if (o instanceof Special) {
|
||||
return compareToSpecial((Special) o);
|
||||
}
|
||||
|
||||
throw new AssertionError(o.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Special other = (Special) o;
|
||||
return Objects.equals(this.value, other.value);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Versions {
|
||||
static final HashMap<String, Special> SPECIALS = new HashMap<>();
|
||||
static final String[] DEFAULT_GAME_VERSIONS;
|
||||
|
||||
static final int[] SNAPSHOT_INTS;
|
||||
static final Release[] SNAPSHOT_PREV;
|
||||
|
||||
static {
|
||||
ArrayDeque<String> defaultGameVersions = new ArrayDeque<>(64);
|
||||
|
||||
List<Snapshot> snapshots = new ArrayList<>(1024);
|
||||
List<Release> snapshotPrev = new ArrayList<>(1024);
|
||||
|
||||
// Convert it to dynamic resource after the website is repaired?
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(GameVersionNumber.class.getResourceAsStream("/assets/game/versions.txt"), StandardCharsets.US_ASCII))) {
|
||||
Release currentRelease = null;
|
||||
GameVersionNumber prev = null;
|
||||
|
||||
for (String line; (line = reader.readLine()) != null; ) {
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
GameVersionNumber version = GameVersionNumber.asGameVersion(line);
|
||||
|
||||
if (currentRelease == null)
|
||||
currentRelease = (Release) version;
|
||||
|
||||
if (version instanceof Snapshot) {
|
||||
Snapshot snapshot = (Snapshot) version;
|
||||
snapshots.add(snapshot);
|
||||
snapshotPrev.add(currentRelease);
|
||||
} else if (version instanceof Release) {
|
||||
currentRelease = (Release) version;
|
||||
|
||||
if (currentRelease.eaType == Release.TYPE_GA) {
|
||||
defaultGameVersions.addFirst(currentRelease.value);
|
||||
}
|
||||
} else if (version instanceof Special) {
|
||||
Special special = (Special) version;
|
||||
special.prev = prev;
|
||||
SPECIALS.put(special.value, special);
|
||||
} else
|
||||
throw new AssertionError("version: " + version);
|
||||
|
||||
prev = version;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
DEFAULT_GAME_VERSIONS = defaultGameVersions.toArray(new String[0]);
|
||||
|
||||
SNAPSHOT_INTS = new int[snapshots.size()];
|
||||
for (int i = 0; i < snapshots.size(); i++) {
|
||||
SNAPSHOT_INTS[i] = snapshots.get(i).intValue;
|
||||
}
|
||||
|
||||
SNAPSHOT_PREV = snapshotPrev.toArray(new Release[SNAPSHOT_INTS.length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,18 @@ public final class VersionNumber implements Comparable<VersionNumber> {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static VersionRange<VersionNumber> between(String minimum, String maximum) {
|
||||
return VersionRange.between(asVersion(minimum), asVersion(maximum));
|
||||
}
|
||||
|
||||
public static VersionRange<VersionNumber> atLeast(String minimum) {
|
||||
return VersionRange.atLeast(asVersion(minimum));
|
||||
}
|
||||
|
||||
public static VersionRange<VersionNumber> atMost(String maximum) {
|
||||
return VersionRange.atMost(asVersion(maximum));
|
||||
}
|
||||
|
||||
private interface Item {
|
||||
int LONG_ITEM = 0;
|
||||
int BIGINTEGER_ITEM = 1;
|
||||
@@ -475,14 +487,6 @@ public final class VersionNumber implements Comparable<VersionNumber> {
|
||||
return canonical;
|
||||
}
|
||||
|
||||
public VersionNumber min(VersionNumber that) {
|
||||
return this.compareTo(that) <= 0 ? this : that;
|
||||
}
|
||||
|
||||
public VersionNumber max(VersionNumber that) {
|
||||
return this.compareTo(that) >= 0 ? this : that;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical);
|
||||
|
||||
@@ -2,58 +2,50 @@ package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class VersionRange {
|
||||
private static final VersionRange EMPTY = new VersionRange(null, null);
|
||||
private static final VersionRange ALL = new VersionRange(null, null);
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class VersionRange<T extends Comparable<T>> {
|
||||
private static final VersionRange<?> EMPTY = new VersionRange<>(null, null);
|
||||
private static final VersionRange<?> ALL = new VersionRange<>(null, null);
|
||||
|
||||
public static VersionRange empty() {
|
||||
return EMPTY;
|
||||
public static <T extends Comparable<T>> VersionRange<T> empty() {
|
||||
return (VersionRange<T>) EMPTY;
|
||||
}
|
||||
|
||||
public static VersionRange all() {
|
||||
return ALL;
|
||||
public static <T extends Comparable<T>> VersionRange<T> all() {
|
||||
return (VersionRange<T>) ALL;
|
||||
}
|
||||
|
||||
public static VersionRange between(String minimum, String maximum) {
|
||||
return between(VersionNumber.asVersion(minimum), VersionNumber.asVersion(maximum));
|
||||
}
|
||||
|
||||
public static VersionRange between(VersionNumber minimum, VersionNumber maximum) {
|
||||
public static <T extends Comparable<T>> VersionRange<T> between(T minimum, T maximum) {
|
||||
assert minimum.compareTo(maximum) <= 0;
|
||||
return new VersionRange(minimum, maximum);
|
||||
return new VersionRange<>(minimum, maximum);
|
||||
}
|
||||
|
||||
public static VersionRange atLeast(String minimum) {
|
||||
return atLeast(VersionNumber.asVersion(minimum));
|
||||
}
|
||||
|
||||
public static VersionRange atLeast(VersionNumber minimum) {
|
||||
public static <T extends Comparable<T>> VersionRange<T> atLeast(T minimum) {
|
||||
assert minimum != null;
|
||||
return new VersionRange(minimum, null);
|
||||
return new VersionRange<>(minimum, null);
|
||||
}
|
||||
|
||||
public static VersionRange atMost(String maximum) {
|
||||
return atMost(VersionNumber.asVersion(maximum));
|
||||
}
|
||||
|
||||
public static VersionRange atMost(VersionNumber maximum) {
|
||||
public static <T extends Comparable<T>> VersionRange<T> atMost(T maximum) {
|
||||
assert maximum != null;
|
||||
return new VersionRange(null, maximum);
|
||||
return new VersionRange<>(null, maximum);
|
||||
}
|
||||
|
||||
private final VersionNumber minimum;
|
||||
private final VersionNumber maximum;
|
||||
private final T minimum;
|
||||
private final T maximum;
|
||||
|
||||
private VersionRange(VersionNumber minimum, VersionNumber maximum) {
|
||||
private VersionRange(T minimum, T maximum) {
|
||||
this.minimum = minimum;
|
||||
this.maximum = maximum;
|
||||
}
|
||||
|
||||
public VersionNumber getMinimum() {
|
||||
public T getMinimum() {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
public VersionNumber getMaximum() {
|
||||
public T getMaximum() {
|
||||
return maximum;
|
||||
}
|
||||
|
||||
@@ -65,12 +57,7 @@ public final class VersionRange {
|
||||
return !isEmpty() && minimum == null && maximum == null;
|
||||
}
|
||||
|
||||
public boolean contains(String versionNumber) {
|
||||
if (versionNumber == null) return false;
|
||||
return contains(VersionNumber.asVersion(versionNumber));
|
||||
}
|
||||
|
||||
public boolean contains(VersionNumber versionNumber) {
|
||||
public boolean contains(T versionNumber) {
|
||||
if (versionNumber == null) return false;
|
||||
if (isEmpty()) return false;
|
||||
if (isAll()) return true;
|
||||
@@ -78,7 +65,7 @@ public final class VersionRange {
|
||||
return (minimum == null || minimum.compareTo(versionNumber) <= 0) && (maximum == null || maximum.compareTo(versionNumber) >= 0);
|
||||
}
|
||||
|
||||
public boolean isOverlappedBy(final VersionRange that) {
|
||||
public boolean isOverlappedBy(final VersionRange<T> that) {
|
||||
if (this.isEmpty() || that.isEmpty())
|
||||
return false;
|
||||
|
||||
@@ -94,32 +81,32 @@ public final class VersionRange {
|
||||
return that.contains(minimum) || that.contains(maximum) || (that.minimum != null && contains(that.minimum));
|
||||
}
|
||||
|
||||
public VersionRange intersectionWith(VersionRange that) {
|
||||
public VersionRange<T> intersectionWith(VersionRange<T> that) {
|
||||
if (this.isAll())
|
||||
return that;
|
||||
if (that.isAll())
|
||||
return this;
|
||||
|
||||
if (!isOverlappedBy(that))
|
||||
return EMPTY;
|
||||
return empty();
|
||||
|
||||
VersionNumber newMinimum;
|
||||
T newMinimum;
|
||||
if (this.minimum == null)
|
||||
newMinimum = that.minimum;
|
||||
else if (that.minimum == null)
|
||||
newMinimum = this.minimum;
|
||||
else
|
||||
newMinimum = this.minimum.max(that.minimum);
|
||||
newMinimum = this.minimum.compareTo(that.minimum) >= 0 ? this.minimum : that.minimum;
|
||||
|
||||
VersionNumber newMaximum;
|
||||
T newMaximum;
|
||||
if (this.maximum == null)
|
||||
newMaximum = that.maximum;
|
||||
else if (that.maximum == null)
|
||||
newMaximum = this.maximum;
|
||||
else
|
||||
newMaximum = this.maximum.min(that.maximum);
|
||||
newMaximum = this.maximum.compareTo(that.maximum) <= 0 ? this.maximum : that.maximum;
|
||||
|
||||
return new VersionRange(newMinimum, newMaximum);
|
||||
return new VersionRange<>(newMinimum, newMaximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +126,7 @@ public final class VersionRange {
|
||||
if (!(obj instanceof VersionRange))
|
||||
return false;
|
||||
|
||||
VersionRange that = (VersionRange) obj;
|
||||
VersionRange<T> that = (VersionRange<T>) obj;
|
||||
|
||||
return this.isEmpty() == that.isEmpty() && this.isAll() == that.isAll()
|
||||
&& Objects.equals(this.minimum, that.minimum)
|
||||
|
||||
@@ -0,0 +1,738 @@
|
||||
1.0
|
||||
11w47a
|
||||
11w48a
|
||||
11w49a
|
||||
11w50a
|
||||
12w01a
|
||||
1.1
|
||||
12w03a
|
||||
12w04a
|
||||
12w05a
|
||||
12w05b
|
||||
12w06a
|
||||
12w07a
|
||||
12w07b
|
||||
12w08a
|
||||
1.2
|
||||
1.2.1
|
||||
1.2.2
|
||||
1.2.3
|
||||
1.2.4
|
||||
1.2.5
|
||||
12w15a
|
||||
12w16a
|
||||
12w17a
|
||||
12w18a
|
||||
12w19a
|
||||
12w21a
|
||||
12w21b
|
||||
12w22a
|
||||
12w23a
|
||||
12w23b
|
||||
12w24a
|
||||
12w25a
|
||||
12w26a
|
||||
12w27a
|
||||
12w30a
|
||||
12w30b
|
||||
12w30c
|
||||
12w30d
|
||||
12w30e
|
||||
1.3
|
||||
1.3.1
|
||||
1.3.2
|
||||
12w32a
|
||||
12w34a
|
||||
12w34b
|
||||
12w36a
|
||||
12w37a
|
||||
12w38a
|
||||
12w38b
|
||||
12w39a
|
||||
12w39b
|
||||
12w40a
|
||||
12w40b
|
||||
12w41a
|
||||
12w41b
|
||||
12w42a
|
||||
12w42b
|
||||
1.4
|
||||
1.4.1
|
||||
1.4.2
|
||||
1.4.3
|
||||
1.4.4
|
||||
12w49a
|
||||
12w50a
|
||||
12w50b
|
||||
1.4.5
|
||||
1.4.6
|
||||
1.4.7
|
||||
13w01a
|
||||
13w01b
|
||||
13w02a
|
||||
13w02b
|
||||
13w03a
|
||||
13w04a
|
||||
13w05a
|
||||
13w05b
|
||||
13w06a
|
||||
13w07a
|
||||
13w09a
|
||||
13w09b
|
||||
13w09c
|
||||
13w10a
|
||||
13w10b
|
||||
1.5
|
||||
13w11a
|
||||
13w12~
|
||||
1.5.1
|
||||
2.0
|
||||
1.5.2
|
||||
13w16a
|
||||
13w16b
|
||||
13w17a
|
||||
13w18a
|
||||
13w18b
|
||||
13w18c
|
||||
13w19a
|
||||
13w21a
|
||||
13w21b
|
||||
13w22a
|
||||
13w23a
|
||||
13w23b
|
||||
13w24a
|
||||
13w24b
|
||||
13w25a
|
||||
13w25b
|
||||
13w25c
|
||||
13w26a
|
||||
1.6
|
||||
1.6.1
|
||||
1.6.2
|
||||
1.6.3
|
||||
1.6.4
|
||||
13w36a
|
||||
13w36b
|
||||
13w37a
|
||||
13w37b
|
||||
13w38a
|
||||
13w38b
|
||||
13w38c
|
||||
13w39a
|
||||
13w39b
|
||||
13w41a
|
||||
13w41b
|
||||
13w42a
|
||||
13w42b
|
||||
13w43a
|
||||
1.7
|
||||
1.7.1
|
||||
1.7.2
|
||||
13w47a
|
||||
13w47b
|
||||
13w47c
|
||||
13w47d
|
||||
13w47e
|
||||
13w48a
|
||||
13w48b
|
||||
13w49a
|
||||
1.7.3
|
||||
1.7.4
|
||||
1.7.5
|
||||
1.7.6-pre1
|
||||
1.7.6-pre2
|
||||
1.7.6
|
||||
1.7.7
|
||||
1.7.8
|
||||
1.7.9
|
||||
1.7.10-pre1
|
||||
1.7.10-pre2
|
||||
1.7.10-pre3
|
||||
1.7.10-pre4
|
||||
1.7.10
|
||||
14w02a
|
||||
14w02b
|
||||
14w02c
|
||||
14w03a
|
||||
14w03b
|
||||
14w04a
|
||||
14w04b
|
||||
14w05a
|
||||
14w05b
|
||||
14w06a
|
||||
14w06b
|
||||
14w07a
|
||||
14w08a
|
||||
14w10a
|
||||
14w10b
|
||||
14w10c
|
||||
14w11a
|
||||
14w11b
|
||||
14w17a
|
||||
14w18a
|
||||
14w18b
|
||||
14w19a
|
||||
14w20a
|
||||
14w20b
|
||||
14w21a
|
||||
14w21b
|
||||
14w25a
|
||||
14w25b
|
||||
14w26a
|
||||
14w26b
|
||||
14w26c
|
||||
14w27a
|
||||
14w27b
|
||||
14w28a
|
||||
14w28b
|
||||
14w29a
|
||||
14w29b
|
||||
14w30a
|
||||
14w30b
|
||||
14w30c
|
||||
14w31a
|
||||
14w32a
|
||||
14w32b
|
||||
14w32c
|
||||
14w32d
|
||||
14w33a
|
||||
14w33b
|
||||
14w33c
|
||||
14w34a
|
||||
14w34b
|
||||
14w34c
|
||||
14w34d
|
||||
1.8-pre1
|
||||
1.8-pre2
|
||||
1.8-pre3
|
||||
1.8
|
||||
1.8.1-pre1
|
||||
1.8.1-pre2
|
||||
1.8.1-pre3
|
||||
1.8.1-pre4
|
||||
1.8.1-pre5
|
||||
1.8.1
|
||||
1.8.2-pre1
|
||||
1.8.2-pre2
|
||||
1.8.2-pre3
|
||||
1.8.2-pre4
|
||||
1.8.2-pre5
|
||||
1.8.2-pre6
|
||||
1.8.2-pre7
|
||||
1.8.2
|
||||
1.8.3
|
||||
15w14a
|
||||
1.8.4
|
||||
1.8.5
|
||||
1.8.6
|
||||
1.8.7
|
||||
1.8.8
|
||||
1.8.9
|
||||
15w31a
|
||||
15w31b
|
||||
15w31c
|
||||
15w32a
|
||||
15w32b
|
||||
15w32c
|
||||
15w33a
|
||||
15w33b
|
||||
15w33c
|
||||
15w34a
|
||||
15w34b
|
||||
15w34c
|
||||
15w34d
|
||||
15w35a
|
||||
15w35b
|
||||
15w35c
|
||||
15w35d
|
||||
15w35e
|
||||
15w36a
|
||||
15w36b
|
||||
15w36c
|
||||
15w36d
|
||||
15w37a
|
||||
15w38a
|
||||
15w38b
|
||||
15w39a
|
||||
15w39b
|
||||
15w39c
|
||||
15w40a
|
||||
15w40b
|
||||
15w41a
|
||||
15w41b
|
||||
15w42a
|
||||
15w43a
|
||||
15w43b
|
||||
15w43c
|
||||
15w44a
|
||||
15w44b
|
||||
15w45a
|
||||
15w46a
|
||||
15w47a
|
||||
15w47b
|
||||
15w47c
|
||||
15w49a
|
||||
15w49b
|
||||
15w50a
|
||||
15w51a
|
||||
15w51b
|
||||
16w02a
|
||||
16w03a
|
||||
16w04a
|
||||
16w05a
|
||||
16w05b
|
||||
16w06a
|
||||
16w07a
|
||||
16w07b
|
||||
1.9-pre1
|
||||
1.9-pre2
|
||||
1.9-pre3
|
||||
1.9-pre4
|
||||
1.9
|
||||
1.9.1-pre1
|
||||
1.9.1-pre2
|
||||
1.9.1-pre3
|
||||
1.9.1
|
||||
1.9.2
|
||||
1.RV-Pre1
|
||||
16w14a
|
||||
16w15a
|
||||
16w15b
|
||||
1.9.3-pre1
|
||||
1.9.3-pre2
|
||||
1.9.3-pre3
|
||||
1.9.3
|
||||
1.9.4
|
||||
16w20a
|
||||
16w21a
|
||||
16w21b
|
||||
1.10-pre1
|
||||
1.10-pre2
|
||||
1.10
|
||||
1.10.1
|
||||
1.10.2
|
||||
16w32a
|
||||
16w32b
|
||||
16w33a
|
||||
16w35a
|
||||
16w36a
|
||||
16w38a
|
||||
16w39a
|
||||
16w39b
|
||||
16w39c
|
||||
16w40a
|
||||
16w41a
|
||||
16w42a
|
||||
16w43a
|
||||
16w44a
|
||||
1.11-pre1
|
||||
1.11
|
||||
16w50a
|
||||
1.11.1
|
||||
1.11.2
|
||||
17w06a
|
||||
17w13a
|
||||
17w13b
|
||||
17w14a
|
||||
17w15a
|
||||
17w16a
|
||||
17w16b
|
||||
17w17a
|
||||
17w17b
|
||||
17w18a
|
||||
17w18b
|
||||
1.12-pre1
|
||||
1.12-pre2
|
||||
1.12-pre3
|
||||
1.12-pre4
|
||||
1.12-pre5
|
||||
1.12-pre6
|
||||
1.12-pre7
|
||||
1.12
|
||||
17w31a
|
||||
1.12.1-pre1
|
||||
1.12.1
|
||||
1.12.2-pre1
|
||||
1.12.2-pre2
|
||||
1.12.2
|
||||
17w43a
|
||||
17w43b
|
||||
17w45a
|
||||
17w45b
|
||||
17w46a
|
||||
17w47a
|
||||
17w47b
|
||||
17w48a
|
||||
17w49a
|
||||
17w49b
|
||||
17w50a
|
||||
18w01a
|
||||
18w02a
|
||||
18w03a
|
||||
18w03b
|
||||
18w05a
|
||||
18w06a
|
||||
18w07a
|
||||
18w07b
|
||||
18w07c
|
||||
18w08a
|
||||
18w08b
|
||||
18w09a
|
||||
18w10a
|
||||
18w10b
|
||||
18w10c
|
||||
18w10d
|
||||
18w11a
|
||||
18w14a
|
||||
18w14b
|
||||
18w15a
|
||||
18w16a
|
||||
18w19a
|
||||
18w19b
|
||||
18w20a
|
||||
18w20b
|
||||
18w20c
|
||||
18w21a
|
||||
18w21b
|
||||
18w22a
|
||||
18w22b
|
||||
18w22c
|
||||
1.13-pre1
|
||||
1.13-pre2
|
||||
1.13-pre3
|
||||
1.13-pre4
|
||||
1.13-pre5
|
||||
1.13-pre6
|
||||
1.13-pre7
|
||||
1.13-pre8
|
||||
1.13-pre9
|
||||
1.13-pre10
|
||||
1.13
|
||||
18w30a
|
||||
18w30b
|
||||
18w31a
|
||||
18w32a
|
||||
18w33a
|
||||
1.13.1-pre1
|
||||
1.13.1-pre2
|
||||
1.13.1
|
||||
1.13.2-pre1
|
||||
1.13.2-pre2
|
||||
1.13.2
|
||||
18w43a
|
||||
18w43b
|
||||
18w43c
|
||||
18w44a
|
||||
18w45a
|
||||
18w46a
|
||||
18w47a
|
||||
18w47b
|
||||
18w48a
|
||||
18w48b
|
||||
18w49a
|
||||
18w50a
|
||||
19w02a
|
||||
19w03a
|
||||
19w03b
|
||||
19w03c
|
||||
19w04a
|
||||
19w04b
|
||||
19w05a
|
||||
19w06a
|
||||
19w07a
|
||||
19w08a
|
||||
19w08b
|
||||
19w09a
|
||||
19w11a
|
||||
19w11b
|
||||
19w12a
|
||||
19w12b
|
||||
19w13a
|
||||
19w13b
|
||||
3D Shareware v1.34
|
||||
19w14a
|
||||
19w14b
|
||||
1.14 Pre-Release 1
|
||||
1.14 Pre-Release 2
|
||||
1.14 Pre-Release 3
|
||||
1.14 Pre-Release 4
|
||||
1.14 Pre-Release 5
|
||||
1.14
|
||||
1.14.1 Pre-Release 1
|
||||
1.14.1 Pre-Release 2
|
||||
1.14.1
|
||||
1.14.2 Pre-Release 1
|
||||
1.14.2 Pre-Release 2
|
||||
1.14.2 Pre-Release 3
|
||||
1.14.2 Pre-Release 4
|
||||
1.14.2
|
||||
1.14.3-pre1
|
||||
1.14.3-pre2
|
||||
1.14.3-pre3
|
||||
1.14.3-pre4
|
||||
1.14.3
|
||||
1.14.4-pre1
|
||||
1.14.4-pre2
|
||||
1.14.4-pre3
|
||||
1.14.4-pre4
|
||||
1.14.4-pre5
|
||||
1.14.4-pre6
|
||||
1.14.4-pre7
|
||||
1.14.4
|
||||
19w34a
|
||||
19w35a
|
||||
19w36a
|
||||
19w37a
|
||||
19w38a
|
||||
19w38b
|
||||
19w39a
|
||||
19w40a
|
||||
19w41a
|
||||
19w42a
|
||||
19w44a
|
||||
19w45a
|
||||
19w45b
|
||||
19w46a
|
||||
19w46b
|
||||
1.15-pre1
|
||||
1.15-pre2
|
||||
1.15-pre3
|
||||
1.15-pre4
|
||||
1.15-pre5
|
||||
1.15-pre6
|
||||
1.15-pre7
|
||||
1.15
|
||||
1.15.1-pre1
|
||||
1.15.1
|
||||
1.15.2-pre1
|
||||
1.15.2-pre2
|
||||
1.15.2
|
||||
20w06a
|
||||
20w07a
|
||||
20w08a
|
||||
20w09a
|
||||
20w10a
|
||||
20w11a
|
||||
20w12a
|
||||
20w13a
|
||||
20w13b
|
||||
20w14infinite
|
||||
20w14a
|
||||
20w15a
|
||||
20w16a
|
||||
20w17a
|
||||
20w18a
|
||||
20w19a
|
||||
20w20a
|
||||
20w20b
|
||||
20w21a
|
||||
20w22a
|
||||
1.16-pre1
|
||||
1.16-pre2
|
||||
1.16-pre3
|
||||
1.16-pre4
|
||||
1.16-pre5
|
||||
1.16-pre6
|
||||
1.16-pre7
|
||||
1.16-pre8
|
||||
1.16-rc1
|
||||
1.16
|
||||
1.16.1
|
||||
20w27a
|
||||
20w28a
|
||||
20w29a
|
||||
20w30a
|
||||
1.16.2-pre1
|
||||
1.16.2-pre2
|
||||
1.16.2-pre3
|
||||
1.16.2-rc1
|
||||
1.16.2-rc2
|
||||
1.16.2
|
||||
1.16.3-rc1
|
||||
1.16.3
|
||||
1.16.4-pre1
|
||||
1.16.4-pre2
|
||||
1.16.4-rc1
|
||||
1.16.4
|
||||
1.16.5-rc1
|
||||
1.16.5
|
||||
20w45a
|
||||
20w46a
|
||||
20w48a
|
||||
20w49a
|
||||
20w51a
|
||||
21w03a
|
||||
21w05a
|
||||
21w05b
|
||||
21w06a
|
||||
21w07a
|
||||
21w08a
|
||||
21w08b
|
||||
21w10a
|
||||
21w11a
|
||||
21w13a
|
||||
21w14a
|
||||
21w15a
|
||||
21w16a
|
||||
21w17a
|
||||
21w18a
|
||||
21w19a
|
||||
21w20a
|
||||
1.17-pre1
|
||||
1.17-pre2
|
||||
1.17-pre3
|
||||
1.17-pre4
|
||||
1.17-pre5
|
||||
1.17-rc1
|
||||
1.17-rc2
|
||||
1.17
|
||||
1.17.1-pre1
|
||||
1.17.1-pre2
|
||||
1.17.1-pre3
|
||||
1.17.1-rc1
|
||||
1.17.1-rc2
|
||||
1.17.1
|
||||
21w37a
|
||||
21w38a
|
||||
21w39a
|
||||
21w40a
|
||||
21w41a
|
||||
21w42a
|
||||
21w43a
|
||||
21w44a
|
||||
1.18-pre1
|
||||
1.18-pre2
|
||||
1.18-pre3
|
||||
1.18-pre4
|
||||
1.18-pre5
|
||||
1.18-pre6
|
||||
1.18-pre7
|
||||
1.18-pre8
|
||||
1.18-rc1
|
||||
1.18-rc2
|
||||
1.18-rc3
|
||||
1.18-rc4
|
||||
1.18
|
||||
1.18.1-pre1
|
||||
1.18.1-rc1
|
||||
1.18.1-rc2
|
||||
1.18.1-rc3
|
||||
1.18.1
|
||||
22w03a
|
||||
22w05a
|
||||
22w06a
|
||||
22w07a
|
||||
1.18.2-pre1
|
||||
1.18.2-pre2
|
||||
1.18.2-pre3
|
||||
1.18.2-rc1
|
||||
1.18.2
|
||||
22w13oneblockatatime
|
||||
22w11a
|
||||
22w12a
|
||||
22w13a
|
||||
22w14a
|
||||
22w15a
|
||||
22w16a
|
||||
22w16b
|
||||
22w17a
|
||||
22w18a
|
||||
22w19a
|
||||
1.19-pre1
|
||||
1.19-pre2
|
||||
1.19-pre3
|
||||
1.19-pre4
|
||||
1.19-pre5
|
||||
1.19-rc1
|
||||
1.19-rc2
|
||||
1.19
|
||||
22w24a
|
||||
1.19.1-pre1
|
||||
1.19.1-pre2
|
||||
1.19.1-pre3
|
||||
1.19.1-pre4
|
||||
1.19.1-pre5
|
||||
1.19.1-pre6
|
||||
1.19.1-rc1
|
||||
1.19.1-rc2
|
||||
1.19.1-rc3
|
||||
1.19.1
|
||||
1.19.2-rc1
|
||||
1.19.2-rc2
|
||||
1.19.2
|
||||
22w42a
|
||||
22w43a
|
||||
22w44a
|
||||
22w45a
|
||||
22w46a
|
||||
1.19.3-pre1
|
||||
1.19.3-pre2
|
||||
1.19.3-pre3
|
||||
1.19.3-rc1
|
||||
1.19.3-rc2
|
||||
1.19.3-rc3
|
||||
1.19.3
|
||||
23w03a
|
||||
23w04a
|
||||
23w05a
|
||||
23w06a
|
||||
23w07a
|
||||
1.19.4-pre1
|
||||
1.19.4-pre2
|
||||
1.19.4-pre3
|
||||
1.19.4-pre4
|
||||
1.19.4-rc1
|
||||
1.19.4-rc2
|
||||
1.19.4-rc3
|
||||
1.19.4
|
||||
23w12a
|
||||
23w13a
|
||||
23w13a_or_b
|
||||
23w14a
|
||||
23w16a
|
||||
23w17a
|
||||
23w18a
|
||||
1.20-pre1
|
||||
1.20-pre2
|
||||
1.20-pre3
|
||||
1.20-pre4
|
||||
1.20-pre5
|
||||
1.20-pre6
|
||||
1.20-pre7
|
||||
1.20-rc1
|
||||
1.20
|
||||
1.20.1-rc1
|
||||
1.20.1
|
||||
23w31a
|
||||
23w32a
|
||||
23w33a
|
||||
23w35a
|
||||
1.20.2-pre1
|
||||
1.20.2-pre2
|
||||
1.20.2-pre3
|
||||
1.20.2-pre4
|
||||
1.20.2-rc1
|
||||
1.20.2-rc2
|
||||
1.20.2
|
||||
23w40a
|
||||
23w41a
|
||||
23w42a
|
||||
23w43a
|
||||
23w43b
|
||||
23w44a
|
||||
23w45a
|
||||
23w46a
|
||||
1.20.3-pre1
|
||||
1.20.3-pre2
|
||||
1.20.3-pre3
|
||||
1.20.3-pre4
|
||||
1.20.3-rc1
|
||||
1.20.3
|
||||
1.20.4-rc1
|
||||
1.20.4
|
||||
23w51a
|
||||
23w51b
|
||||
24w03a
|
||||
24w03b
|
||||
24w04a
|
||||
24w05a
|
||||
@@ -100,6 +100,15 @@ public class CrashReportAnalyzerTest {
|
||||
result.getMatcher().group("reason").replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forgeFoundDuplicateMods() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
CrashReportAnalyzer.anaylze(loadLog("/logs/forge_found_duplicate_mods.txt")),
|
||||
CrashReportAnalyzer.Rule.FORGE_FOUND_DUPLICATE_MODS);
|
||||
assertEquals(("\tMod ID: 'jei' from mod files: REIPluginCompatibilities-forge-12.0.93.jar, jei-1.20.1-forge-15.2.0.27.jar\n").replaceAll("\\s+", ""),
|
||||
result.getMatcher().group("reason").replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modResolutionCollection() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
@@ -166,6 +175,13 @@ public class CrashReportAnalyzerTest {
|
||||
assertEquals("52", result.getMatcher().group("expected"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaVersionIsTooHigh() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
CrashReportAnalyzer.anaylze(loadLog("/logs/java_version_is_too_high.txt")),
|
||||
CrashReportAnalyzer.Rule.JAVA_VERSION_IS_TOO_HIGH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityException() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
@@ -324,6 +340,13 @@ public class CrashReportAnalyzerTest {
|
||||
CrashReportAnalyzer.Rule.OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outOfMemoryJVM1() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
CrashReportAnalyzer.anaylze(loadLog("/logs/out_of_memory2.txt")),
|
||||
CrashReportAnalyzer.Rule.OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void memoryExceeded() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
@@ -370,6 +393,22 @@ public class CrashReportAnalyzerTest {
|
||||
result.getMatcher().group("reason").replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fabricWarnings2() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
CrashReportAnalyzer.anaylze(loadLog("/logs/fabric_warnings3.txt")),
|
||||
CrashReportAnalyzer.Rule.FABRIC_WARNINGS);
|
||||
assertEquals(("net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other!\n" +
|
||||
"确定了一种可能的解决方法,这样做可能会解决你的问题:\n" +
|
||||
"\t - 安装 fabric-api,任意版本。\n" +
|
||||
"\t - 安装 sodium,0.5.6 及以上版本。\n" +
|
||||
"更多信息:\n" +
|
||||
"\t - 模组 'Sodium Extra' (sodium-extra) 0.5.4+mc1.20.4-build.116 需要 fabric-api 的 任意版本,但没有安装它!\n" +
|
||||
"\t - 模组 'Sodium Extra' (sodium-extra) 0.5.4+mc1.20.4-build.116 需要 sodium 的 0.5.6 及以上版本,但没有安装它!\n" +
|
||||
"\tat net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:51) ~").replaceAll("\\s+", ""),
|
||||
result.getMatcher().group("reason").replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fabricConflicts() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
@@ -467,6 +506,13 @@ public class CrashReportAnalyzerTest {
|
||||
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incompleteForgeInstallation7() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
CrashReportAnalyzer.anaylze(loadLog("/logs/incomplete_forge_installation7.txt")),
|
||||
CrashReportAnalyzer.Rule.INCOMPLETE_FORGE_INSTALLATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forgeRepeatInstallation() throws IOException {
|
||||
CrashReportAnalyzer.Result result = findResultByRule(
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionRange;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -28,10 +28,10 @@ public class JavaVersionConstraintTest {
|
||||
@Test
|
||||
public void vanillaJava16() {
|
||||
JavaVersionConstraint.VersionRanges range = JavaVersionConstraint.findSuitableJavaVersionRange(
|
||||
VersionNumber.asVersion("1.17"),
|
||||
GameVersionNumber.asGameVersion("1.17"),
|
||||
null
|
||||
);
|
||||
|
||||
assertEquals(VersionRange.atLeast("16"), range.getMandatory());
|
||||
assertEquals(VersionNumber.atLeast("16"), range.getMandatory());
|
||||
}
|
||||
}
|
||||
+14
-5
@@ -2,29 +2,38 @@ package org.jackhuang.hmcl.util.gson;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class DateTypeAdapterTest {
|
||||
public class InstantTypeAdapterTest {
|
||||
|
||||
@Test
|
||||
public void parse() {
|
||||
public void testDeserialize() {
|
||||
assertEquals(
|
||||
LocalDateTime.of(2017, 6, 8, 4, 26, 33)
|
||||
.atOffset(ZoneOffset.UTC).toInstant(),
|
||||
DateTypeAdapter.deserializeToDate("2017-06-08T04:26:33+0000").toInstant());
|
||||
InstantTypeAdapter.deserializeToInstant("2017-06-08T04:26:33+0000"));
|
||||
|
||||
assertEquals(
|
||||
LocalDateTime.of(2021, 1, 3, 0, 53, 34)
|
||||
.atOffset(ZoneOffset.UTC).toInstant(),
|
||||
DateTypeAdapter.deserializeToDate("2021-01-03T00:53:34+00:00").toInstant());
|
||||
InstantTypeAdapter.deserializeToInstant("2021-01-03T00:53:34+00:00"));
|
||||
|
||||
assertEquals(
|
||||
LocalDateTime.of(2021, 1, 3, 0, 53, 34)
|
||||
.atZone(ZoneId.systemDefault()).toInstant(),
|
||||
DateTypeAdapter.deserializeToDate("2021-01-03T00:53:34").toInstant());
|
||||
InstantTypeAdapter.deserializeToInstant("2021-01-03T00:53:34"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialize() {
|
||||
assertEquals(
|
||||
"2024-02-13T15:11:06+08:00",
|
||||
InstantTypeAdapter.serializeToString(Instant.ofEpochMilli(1707808266154L), ZoneOffset.ofHours(8))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public class GameVersionNumberTest {
|
||||
|
||||
@Test
|
||||
public void testSortVersions() throws IOException {
|
||||
List<String> versions = new ArrayList<>();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(GameVersionNumber.class.getResourceAsStream("/assets/game/versions.txt"), StandardCharsets.UTF_8))) {
|
||||
for (String line; (line = reader.readLine()) != null && !line.isEmpty(); ) {
|
||||
versions.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> copied = new ArrayList<>(versions);
|
||||
Collections.shuffle(copied, new Random(0));
|
||||
copied.sort(Comparator.comparing(GameVersionNumber::asGameVersion));
|
||||
|
||||
assertIterableEquals(versions, copied);
|
||||
}
|
||||
|
||||
private static String errorMessage(String version1, String version2) {
|
||||
return String.format("version1=%s, version2=%s", version1, version2);
|
||||
}
|
||||
|
||||
private static void assertGameVersionEquals(String version) {
|
||||
assertGameVersionEquals(version, version);
|
||||
}
|
||||
|
||||
private static void assertGameVersionEquals(String version1, String version2) {
|
||||
assertEquals(0, GameVersionNumber.asGameVersion(version1).compareTo(version2), errorMessage(version1, version2));
|
||||
}
|
||||
|
||||
private static void assertLessThan(String version1, String version2) {
|
||||
assertTrue(GameVersionNumber.asGameVersion(version1).compareTo(version2) < 0, errorMessage(version1, version2));
|
||||
}
|
||||
|
||||
private static void assertOrder(String... versions) {
|
||||
for (int i = 0; i < versions.length - 1; i++) {
|
||||
GameVersionNumber version1 = GameVersionNumber.asGameVersion(versions[i]);
|
||||
|
||||
//noinspection EqualsWithItself
|
||||
assertEquals(0, version1.compareTo(version1), "version=" + versions[i]);
|
||||
|
||||
for (int j = i + 1; j < versions.length; j++) {
|
||||
GameVersionNumber version2 = GameVersionNumber.asGameVersion(versions[j]);
|
||||
|
||||
assertEquals(-1, version1.compareTo(version2), String.format("version1=%s, version2=%s", versions[i], versions[j]));
|
||||
assertEquals(1, version2.compareTo(version1), String.format("version1=%s, version2=%s", versions[i], versions[j]));
|
||||
}
|
||||
}
|
||||
|
||||
assertGameVersionEquals(versions[versions.length - 1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseOld() {
|
||||
{
|
||||
GameVersionNumber version = GameVersionNumber.asGameVersion("b1.0");
|
||||
assertInstanceOf(GameVersionNumber.Old.class, version);
|
||||
GameVersionNumber.Old old = (GameVersionNumber.Old) version;
|
||||
assertEquals(GameVersionNumber.Type.BETA, old.type);
|
||||
assertEquals(1, old.major);
|
||||
assertEquals(0, old.minor);
|
||||
assertEquals(0, old.patch);
|
||||
assertEquals(0, old.additional);
|
||||
}
|
||||
|
||||
{
|
||||
GameVersionNumber version = GameVersionNumber.asGameVersion("b1.0_01");
|
||||
assertInstanceOf(GameVersionNumber.Old.class, version);
|
||||
GameVersionNumber.Old old = (GameVersionNumber.Old) version;
|
||||
assertEquals(GameVersionNumber.Type.BETA, old.type);
|
||||
assertEquals(1, old.major);
|
||||
assertEquals(0, old.minor);
|
||||
assertEquals(0, old.patch);
|
||||
assertEquals(1, old.additional);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareRelease() {
|
||||
assertGameVersionEquals("0.0");
|
||||
assertGameVersionEquals("1.100");
|
||||
assertGameVersionEquals("1.100.1");
|
||||
assertGameVersionEquals("1.100.1-pre1");
|
||||
assertGameVersionEquals("1.100.1-pre1", "1.100.1 Pre-Release 1");
|
||||
|
||||
assertOrder(
|
||||
"0.0",
|
||||
"1.0",
|
||||
"1.99",
|
||||
"1.99.1-unknown1",
|
||||
"1.99.1-pre1",
|
||||
"1.99.1 Pre-Release 2",
|
||||
"1.99.1-rc1",
|
||||
"1.99.1",
|
||||
"1.100",
|
||||
"1.100.1"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareSnapshot() {
|
||||
assertOrder(
|
||||
"90w01a",
|
||||
"90w01b",
|
||||
"90w01e",
|
||||
"90w01~",
|
||||
"90w02a"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareMix() {
|
||||
assertOrder(
|
||||
"rd-132211",
|
||||
"rd-161348",
|
||||
"rd-20090515",
|
||||
"c0.0.11a",
|
||||
"c0.0.13a",
|
||||
"c0.0.13a_03",
|
||||
"c0.30_01c",
|
||||
"inf-20100330-1",
|
||||
"inf-20100330-2",
|
||||
"inf-20100618",
|
||||
"a1.0.4",
|
||||
"a1.0.17_02",
|
||||
"a1.0.17_04",
|
||||
"a1.1.0",
|
||||
"b1.0",
|
||||
"b1.0_01",
|
||||
"b1.1_02",
|
||||
"b1.2",
|
||||
"b1.8.1",
|
||||
"0.0",
|
||||
"1.0",
|
||||
"11w47a",
|
||||
"1.1",
|
||||
"1.5.1",
|
||||
"2.0",
|
||||
"1.5.2",
|
||||
"1.9.2",
|
||||
"1.RV-Pre1",
|
||||
"16w14a",
|
||||
"1.9.3-pre1",
|
||||
"1.13.2",
|
||||
"19w13b",
|
||||
"3D Shareware v1.34",
|
||||
"19w14a",
|
||||
"1.14 Pre-Release 1",
|
||||
"1.14",
|
||||
"1.15.2",
|
||||
"20w06a",
|
||||
"20w14infinite",
|
||||
"20w22a",
|
||||
"1.16-pre1",
|
||||
"1.16",
|
||||
"1.18.2",
|
||||
"22w13oneblockatatime",
|
||||
"22w11a",
|
||||
"1.19-pre1",
|
||||
"1.19.4",
|
||||
"23w13a",
|
||||
"23w13a_or_b",
|
||||
"23w14a",
|
||||
"1.20",
|
||||
|
||||
"Unknown",
|
||||
"100.0"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareUnknown() {
|
||||
assertOrder(
|
||||
"23w35a",
|
||||
"1.20.2-pre1",
|
||||
"1.20.2-rc1",
|
||||
"1.20.2",
|
||||
"23w35b", // fictional version number
|
||||
"23w40a"
|
||||
);
|
||||
|
||||
assertOrder(
|
||||
"1.20.4",
|
||||
"24w04a",
|
||||
"1.100" // fictional version number
|
||||
);
|
||||
|
||||
assertOrder(
|
||||
"1.19.4",
|
||||
"23w18a", // fictional version number
|
||||
"1.19.5",
|
||||
"1.20"
|
||||
);
|
||||
|
||||
assertOrder(
|
||||
"1.0",
|
||||
"10w47a", // fictional version number
|
||||
"11w47a",
|
||||
"1.1"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,43 +9,46 @@ public class VersionRangeTest {
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
assertTrue(between("10", "20").contains("10"));
|
||||
assertTrue(between("10", "20").contains("15"));
|
||||
assertTrue(between("10", "20").contains("20"));
|
||||
assertFalse(between("10", "20").contains("5"));
|
||||
assertFalse(between("10", "20").contains("25"));
|
||||
VersionRange<VersionNumber> empty = VersionRange.empty();
|
||||
VersionRange<VersionNumber> all = all();
|
||||
|
||||
assertTrue(between("10", "10").contains("10"));
|
||||
assertFalse(between("10", "10").contains("5"));
|
||||
assertFalse(between("10", "10").contains("15"));
|
||||
assertTrue(VersionNumber.between("10", "20").contains(VersionNumber.asVersion("10")));
|
||||
assertTrue(VersionNumber.between("10", "20").contains(VersionNumber.asVersion("15")));
|
||||
assertTrue(VersionNumber.between("10", "20").contains(VersionNumber.asVersion("20")));
|
||||
assertFalse(VersionNumber.between("10", "20").contains(VersionNumber.asVersion("5")));
|
||||
assertFalse(VersionNumber.between("10", "20").contains(VersionNumber.asVersion("25")));
|
||||
|
||||
assertTrue(atLeast("10").contains("10"));
|
||||
assertTrue(atLeast("10").contains("20"));
|
||||
assertFalse(atLeast("10").contains("5"));
|
||||
assertTrue(VersionNumber.between("10", "10").contains(VersionNumber.asVersion("10")));
|
||||
assertFalse(VersionNumber.between("10", "10").contains(VersionNumber.asVersion("5")));
|
||||
assertFalse(VersionNumber.between("10", "10").contains(VersionNumber.asVersion("15")));
|
||||
|
||||
assertTrue(atMost("10").contains("10"));
|
||||
assertTrue(atMost("10").contains("5"));
|
||||
assertFalse(atMost("10").contains("20"));
|
||||
assertTrue(VersionNumber.atLeast("10").contains(VersionNumber.asVersion("10")));
|
||||
assertTrue(VersionNumber.atLeast("10").contains(VersionNumber.asVersion("20")));
|
||||
assertFalse(VersionNumber.atLeast("10").contains(VersionNumber.asVersion("5")));
|
||||
|
||||
assertFalse(empty().contains("0"));
|
||||
assertFalse(empty().contains("10"));
|
||||
assertTrue(VersionNumber.atMost("10").contains(VersionNumber.asVersion("10")));
|
||||
assertTrue(VersionNumber.atMost("10").contains(VersionNumber.asVersion("5")));
|
||||
assertFalse(VersionNumber.atMost("10").contains(VersionNumber.asVersion("20")));
|
||||
|
||||
assertTrue(all().contains("0"));
|
||||
assertTrue(all().contains("10"));
|
||||
assertFalse(empty.contains(VersionNumber.asVersion("0")));
|
||||
assertFalse(empty.contains(VersionNumber.asVersion("10")));
|
||||
|
||||
assertFalse(all().contains((String) null));
|
||||
assertFalse(empty().contains((String) null));
|
||||
assertFalse(between("0", "10").contains((String) null));
|
||||
assertFalse(atLeast("10").contains((String) null));
|
||||
assertFalse(atMost("10").contains((String) null));
|
||||
assertFalse(all().contains((VersionNumber) null));
|
||||
assertFalse(empty().contains((VersionNumber) null));
|
||||
assertFalse(between("0", "10").contains((VersionNumber) null));
|
||||
assertFalse(atLeast("10").contains((VersionNumber) null));
|
||||
assertFalse(atMost("10").contains((VersionNumber) null));
|
||||
assertTrue(all.contains(VersionNumber.asVersion("0")));
|
||||
assertTrue(all.contains(VersionNumber.asVersion("10")));
|
||||
|
||||
assertFalse(all.contains(null));
|
||||
assertFalse(empty.contains( null));
|
||||
assertFalse(VersionNumber.between("0", "10").contains(null));
|
||||
assertFalse(VersionNumber.atLeast("10").contains(null));
|
||||
assertFalse(VersionNumber.atMost("10").contains(null));
|
||||
assertFalse(all.contains(null));
|
||||
assertFalse(empty.contains(null));
|
||||
assertFalse(VersionNumber.between("0", "10").contains(null));
|
||||
assertFalse(VersionNumber.atLeast("10").contains(null));
|
||||
assertFalse(VersionNumber.atMost("10").contains(null));
|
||||
}
|
||||
|
||||
private static void assertIsOverlappedBy(boolean value, VersionRange range1, VersionRange range2) {
|
||||
private static void assertIsOverlappedBy(boolean value, VersionRange<VersionNumber> range1, VersionRange<VersionNumber> range2) {
|
||||
assertEquals(value, range1.isOverlappedBy(range2));
|
||||
assertEquals(value, range2.isOverlappedBy(range1));
|
||||
}
|
||||
@@ -56,38 +59,38 @@ public class VersionRangeTest {
|
||||
assertIsOverlappedBy(false, all(), empty());
|
||||
assertIsOverlappedBy(false, empty(), empty());
|
||||
|
||||
assertIsOverlappedBy(true, all(), between("10", "20"));
|
||||
assertIsOverlappedBy(true, all(), atLeast("10"));
|
||||
assertIsOverlappedBy(true, all(), atMost("10"));
|
||||
assertIsOverlappedBy(true, all(), VersionNumber.between("10", "20"));
|
||||
assertIsOverlappedBy(true, all(), VersionNumber.atLeast("10"));
|
||||
assertIsOverlappedBy(true, all(), VersionNumber.atMost("10"));
|
||||
|
||||
assertIsOverlappedBy(false, empty(), between("10", "20"));
|
||||
assertIsOverlappedBy(false, empty(), atLeast("10"));
|
||||
assertIsOverlappedBy(false, empty(), atMost("10"));
|
||||
assertIsOverlappedBy(false, empty(), VersionNumber.between("10", "20"));
|
||||
assertIsOverlappedBy(false, empty(), VersionNumber.atLeast("10"));
|
||||
assertIsOverlappedBy(false, empty(), VersionNumber.atMost("10"));
|
||||
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("10", "20"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("5", "20"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("5", "15"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("5", "10"));
|
||||
assertIsOverlappedBy(false, between("10", "20"), between("5", "5"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("10", "30"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("15", "30"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), between("20", "30"));
|
||||
assertIsOverlappedBy(false, between("10", "20"), between("21", "30"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), atLeast("5"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), atLeast("10"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), atLeast("15"));
|
||||
assertIsOverlappedBy(true, between("10", "20"), atLeast("20"));
|
||||
assertIsOverlappedBy(false, between("10", "20"), atLeast("25"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("10", "20"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("5", "20"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("5", "15"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("5", "10"));
|
||||
assertIsOverlappedBy(false, VersionNumber.between("10", "20"), VersionNumber.between("5", "5"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("10", "30"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("15", "30"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.between("20", "30"));
|
||||
assertIsOverlappedBy(false, VersionNumber.between("10", "20"), VersionNumber.between("21", "30"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.atLeast("5"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.atLeast("10"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.atLeast("15"));
|
||||
assertIsOverlappedBy(true, VersionNumber.between("10", "20"), VersionNumber.atLeast("20"));
|
||||
assertIsOverlappedBy(false, VersionNumber.between("10", "20"), VersionNumber.atLeast("25"));
|
||||
|
||||
assertIsOverlappedBy(true, atLeast("10"), atLeast("10"));
|
||||
assertIsOverlappedBy(true, atLeast("10"), atLeast("20"));
|
||||
assertIsOverlappedBy(true, atLeast("10"), atLeast("5"));
|
||||
assertIsOverlappedBy(true, atLeast("10"), atMost("10"));
|
||||
assertIsOverlappedBy(true, atLeast("10"), atMost("20"));
|
||||
assertIsOverlappedBy(false, atLeast("10"), atMost("5"));
|
||||
assertIsOverlappedBy(true, VersionNumber.atLeast("10"), VersionNumber.atLeast("10"));
|
||||
assertIsOverlappedBy(true, VersionNumber.atLeast("10"), VersionNumber.atLeast("20"));
|
||||
assertIsOverlappedBy(true, VersionNumber.atLeast("10"), VersionNumber.atLeast("5"));
|
||||
assertIsOverlappedBy(true, VersionNumber.atLeast("10"), VersionNumber.atMost("10"));
|
||||
assertIsOverlappedBy(true, VersionNumber.atLeast("10"), VersionNumber.atMost("20"));
|
||||
assertIsOverlappedBy(false, VersionNumber.atLeast("10"), VersionNumber.atMost("5"));
|
||||
}
|
||||
|
||||
private static void assertIntersectionWith(VersionRange range1, VersionRange range2, VersionRange result) {
|
||||
private static void assertIntersectionWith(VersionRange<VersionNumber> range1, VersionRange<VersionNumber> range2, VersionRange<VersionNumber> result) {
|
||||
assertEquals(result, range1.intersectionWith(range2));
|
||||
assertEquals(result, range2.intersectionWith(range1));
|
||||
}
|
||||
@@ -96,35 +99,35 @@ public class VersionRangeTest {
|
||||
public void testIntersectionWith() {
|
||||
assertIntersectionWith(all(), all(), all());
|
||||
assertIntersectionWith(all(), empty(), empty());
|
||||
assertIntersectionWith(all(), between("10", "20"), between("10", "20"));
|
||||
assertIntersectionWith(all(), atLeast("10"), atLeast("10"));
|
||||
assertIntersectionWith(all(), atMost("10"), atMost("10"));
|
||||
assertIntersectionWith(all(), VersionNumber.between("10", "20"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(all(), VersionNumber.atLeast("10"), VersionNumber.atLeast("10"));
|
||||
assertIntersectionWith(all(), VersionNumber.atMost("10"), VersionNumber.atMost("10"));
|
||||
|
||||
assertIntersectionWith(empty(), empty(), empty());
|
||||
assertIntersectionWith(empty(), between("10", "20"), empty());
|
||||
assertIntersectionWith(empty(), atLeast("10"), empty());
|
||||
assertIntersectionWith(empty(), atMost("10"), empty());
|
||||
assertIntersectionWith(empty(), VersionNumber.between("10", "20"), empty());
|
||||
assertIntersectionWith(empty(), VersionNumber.atLeast("10"), empty());
|
||||
assertIntersectionWith(empty(), VersionNumber.atMost("10"), empty());
|
||||
|
||||
assertIntersectionWith(between("10", "20"), between("10", "20"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), between("5", "20"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), between("10", "25"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), between("5", "25"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), between("15", "20"), between("15", "20"));
|
||||
assertIntersectionWith(between("10", "20"), between("10", "15"), between("10", "15"));
|
||||
assertIntersectionWith(between("10", "20"), between("14", "16"), between("14", "16"));
|
||||
assertIntersectionWith(between("10", "20"), atLeast("5"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atLeast("10"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atLeast("15"), between("15", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atLeast("20"), between("20", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atLeast("25"), empty());
|
||||
assertIntersectionWith(between("10", "20"), atMost("25"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atMost("20"), between("10", "20"));
|
||||
assertIntersectionWith(between("10", "20"), atMost("15"), between("10", "15"));
|
||||
assertIntersectionWith(between("10", "20"), atMost("10"), between("10", "10"));
|
||||
assertIntersectionWith(between("10", "20"), atMost("5"), empty());
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("10", "20"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("5", "20"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("10", "25"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("5", "25"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("15", "20"), VersionNumber.between("15", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("10", "15"), VersionNumber.between("10", "15"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.between("14", "16"), VersionNumber.between("14", "16"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atLeast("5"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atLeast("10"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atLeast("15"), VersionNumber.between("15", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atLeast("20"), VersionNumber.between("20", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atLeast("25"), empty());
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atMost("25"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atMost("20"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atMost("15"), VersionNumber.between("10", "15"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atMost("10"), VersionNumber.between("10", "10"));
|
||||
assertIntersectionWith(VersionNumber.between("10", "20"), VersionNumber.atMost("5"), empty());
|
||||
|
||||
assertIntersectionWith(atLeast("10"), atMost("10"), between("10", "10"));
|
||||
assertIntersectionWith(atLeast("10"), atMost("20"), between("10", "20"));
|
||||
assertIntersectionWith(atLeast("10"), atMost("5"), empty());
|
||||
assertIntersectionWith(VersionNumber.atLeast("10"), VersionNumber.atMost("10"), VersionNumber.between("10", "10"));
|
||||
assertIntersectionWith(VersionNumber.atLeast("10"), VersionNumber.atMost("20"), VersionNumber.between("10", "20"));
|
||||
assertIntersectionWith(VersionNumber.atLeast("10"), VersionNumber.atMost("5"), empty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
[01:27:16] [main/INFO]: Loading Minecraft 1.20.4 with Fabric Loader 0.15.6
|
||||
[01:27:16] [main/WARN]: Mod resolution failed
|
||||
[01:27:16] [main/INFO]: Immediate reason: [HARD_DEP_NO_CANDIDATE sodium-extra 0.5.4+mc1.20.4-build.116 {depends fabric-api @ [*]}, ROOT_FORCELOAD_SINGLE sodium-extra 0.5.4+mc1.20.4-build.116]
|
||||
[01:27:16] [main/INFO]: Reason: [HARD_DEP sodium-extra 0.5.4+mc1.20.4-build.116 {depends fabric-api @ [*]}, HARD_DEP sodium-extra 0.5.4+mc1.20.4-build.116 {depends sodium @ [>=0.5.6]}]
|
||||
[01:27:16] [main/INFO]: Fix: add [add:fabric-api 1 ([(-∞,∞)]), add:sodium 0.5.6 ([[0.5.6,∞)])], remove [], replace []
|
||||
[01:27:17] [main/ERROR]: Incompatible mods found!
|
||||
net.fabricmc.loader.impl.FormattedException: Some of your mods are incompatible with the game or each other!
|
||||
确定了一种可能的解决方法,这样做可能会解决你的问题:
|
||||
- 安装 fabric-api,任意版本。
|
||||
- 安装 sodium,0.5.6 及以上版本。
|
||||
更多信息:
|
||||
- 模组 'Sodium Extra' (sodium-extra) 0.5.4+mc1.20.4-build.116 需要 fabric-api 的 任意版本,但没有安装它!
|
||||
- 模组 'Sodium Extra' (sodium-extra) 0.5.4+mc1.20.4-build.116 需要 sodium 的 0.5.6 及以上版本,但没有安装它!
|
||||
at net.fabricmc.loader.impl.FormattedException.ofLocalized(FormattedException.java:51) ~[fabric-loader-0.15.6.jar:?]
|
||||
at net.fabricmc.loader.impl.FabricLoaderImpl.load(FabricLoaderImpl.java:195) ~[fabric-loader-0.15.6.jar:?]
|
||||
at net.fabricmc.loader.impl.launch.knot.Knot.init(Knot.java:146) ~[fabric-loader-0.15.6.jar:?]
|
||||
at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:68) ~[fabric-loader-0.15.6.jar:?]
|
||||
at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.15.6.jar:?]
|
||||
@@ -0,0 +1,79 @@
|
||||
[10:41:20] [main/INFO]: ModLauncher running: args [--username, pretentiou, --version, 1.20.1, --gameDir, C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft, --assetsDir, C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\assets, --assetIndex, 5, --uuid, 202b1899ef5247368d9a289c5d2ec63f, --accessToken, ❄❄❄❄❄❄❄❄, --clientId, ${clientid}, --xuid, ${auth_xuid}, --userType, msa, --versionType, HMCL 3.5.5.235, --width, 854, --height, 480, --launchTarget, forgeclient, --fml.forgeVersion, 47.2.16, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412]
|
||||
[10:41:20] [main/INFO]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.8 by Microsoft; OS Windows 10 arch amd64 version 10.0
|
||||
[10:41:20] [main/INFO]: Loading ImmediateWindowProvider fmlearlywindow
|
||||
[10:41:20] [main/INFO]: Trying GL version 4.6
|
||||
[10:41:20] [main/INFO]: Requested GL version 4.6 got version 4.6
|
||||
[10:41:21] [main/INFO]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/C:/Users/姚资柱/Desktop/新建文件夹/game/.minecraft/libraries/org/spongepowered/mixin/0.8.5/mixin-0.8.5.jar%23100!/ Service=ModLauncher Env=CLIENT
|
||||
[10:41:21] [pool-2-thread-1/INFO]: GL info: AMD Radeon(TM) Graphics GL version 4.6.13596 Core Profile Forward-Compatible Context 20.10.28.10 27.20.11028.10001, ATI Technologies Inc.
|
||||
[10:41:21] [main/INFO]: Found mod file architectury-9.1.13-forge.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file cloth-config-11.1.118-forge.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file create-1.20.1-0.5.1.f.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file ftb-library-forge-2001.1.5.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file ftb-ultimine-forge-2001.1.4.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file Jade-1.20.1-forge-11.7.1.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file jei-1.20.1-forge-15.2.0.27.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file REIPluginCompatibilities-forge-12.0.93.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file RoughlyEnoughItems-12.0.684-forge.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file Xaeros_Minimap_23.9.7_Forge_1.20.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/INFO]: Found mod file XaerosWorldMap_1.37.7_Forge_1.20.jar of type MOD with provider {mods folder locator at C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\mods}
|
||||
[10:41:21] [main/WARN]: Mod file C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\libraries\net\minecraftforge\fmlcore\1.20.1-47.2.16\fmlcore-1.20.1-47.2.16.jar is missing mods.toml file
|
||||
[10:41:21] [main/WARN]: Mod file C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\libraries\net\minecraftforge\javafmllanguage\1.20.1-47.2.16\javafmllanguage-1.20.1-47.2.16.jar is missing mods.toml file
|
||||
[10:41:21] [main/WARN]: Mod file C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\libraries\net\minecraftforge\lowcodelanguage\1.20.1-47.2.16\lowcodelanguage-1.20.1-47.2.16.jar is missing mods.toml file
|
||||
[10:41:21] [main/WARN]: Mod file C:\Users\姚资柱\Desktop\新建文件夹\game\.minecraft\libraries\net\minecraftforge\mclanguage\1.20.1-47.2.16\mclanguage-1.20.1-47.2.16.jar is missing mods.toml file
|
||||
[10:41:21] [main/INFO]: Found mod file fmlcore-1.20.1-47.2.16.jar of type LIBRARY with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/INFO]: Found mod file javafmllanguage-1.20.1-47.2.16.jar of type LANGPROVIDER with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/INFO]: Found mod file lowcodelanguage-1.20.1-47.2.16.jar of type LANGPROVIDER with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/INFO]: Found mod file mclanguage-1.20.1-47.2.16.jar of type LANGPROVIDER with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/INFO]: Found mod file client-1.20.1-20230612.114412-srg.jar of type MOD with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/INFO]: Found mod file forge-1.20.1-47.2.16-universal.jar of type MOD with provider net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator@878537d
|
||||
[10:41:21] [main/ERROR]: Found duplicate mods:
|
||||
Mod ID: 'jei' from mod files: REIPluginCompatibilities-forge-12.0.93.jar, jei-1.20.1-forge-15.2.0.27.jar
|
||||
[10:41:21] [main/ERROR]: Failed to build unique mod list after mod discovery.
|
||||
net.minecraftforge.fml.loading.EarlyLoadingException: Duplicate mods found
|
||||
at net.minecraftforge.fml.loading.UniqueModListBuilder.buildUniqueList(UniqueModListBuilder.java:87) ~[fmlloader-1.20.1-47.2.16.jar:1.0]
|
||||
at net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer.discoverMods(ModDiscoverer.java:106) ~[fmlloader-1.20.1-47.2.16.jar:?]
|
||||
at net.minecraftforge.fml.loading.FMLLoader.beginModScan(FMLLoader.java:164) ~[fmlloader-1.20.1-47.2.16.jar:1.0]
|
||||
at net.minecraftforge.fml.loading.FMLServiceProvider.beginScanning(FMLServiceProvider.java:86) ~[fmlloader-1.20.1-47.2.16.jar:1.0]
|
||||
at cpw.mods.modlauncher.TransformationServiceDecorator.runScan(TransformationServiceDecorator.java:112) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler.lambda$runScanningTransformationServices$8(TransformationServicesHandler.java:100) ~[modlauncher-10.0.9.jar:?]
|
||||
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[?:?]
|
||||
at java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779) ~[?:?]
|
||||
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
|
||||
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
|
||||
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) ~[?:?]
|
||||
at java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) ~[?:?]
|
||||
at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) ~[?:?]
|
||||
at java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622) ~[?:?]
|
||||
at java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627) ~[?:?]
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler.runScanningTransformationServices(TransformationServicesHandler.java:102) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler.initializeTransformationServices(TransformationServicesHandler.java:55) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.Launcher.run(Launcher.java:88) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.Launcher.main(Launcher.java:78) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) ~[modlauncher-10.0.9.jar:?]
|
||||
at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141) ~[bootstraplauncher-1.1.2.jar:?]
|
||||
[10:41:21] [main/ERROR]: Mod Discovery failed. Skipping dependency discovery.
|
||||
Exception in thread "main" java.lang.IllegalStateException: Failed to find system mod: minecraft
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.ModSorter.detectSystemMods(ModSorter.java:181)
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.ModSorter.buildUniqueList(ModSorter.java:145)
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.ModSorter.sort(ModSorter.java:53)
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.moddiscovery.ModValidator.stage2Validation(ModValidator.java:98)
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.FMLLoader.completeScan(FMLLoader.java:172)
|
||||
at MC-BOOTSTRAP/fmlloader@1.20.1-47.2.16/net.minecraftforge.fml.loading.FMLServiceProvider.completeScan(FMLServiceProvider.java:91)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.TransformationServiceDecorator.onCompleteScan(TransformationServiceDecorator.java:174)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.TransformationServicesHandler.lambda$triggerScanCompletion$24(TransformationServicesHandler.java:145)
|
||||
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
|
||||
at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779)
|
||||
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
|
||||
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
|
||||
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
|
||||
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
|
||||
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
|
||||
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
|
||||
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.TransformationServicesHandler.triggerScanCompletion(TransformationServicesHandler.java:147)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.Launcher.run(Launcher.java:95)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.Launcher.main(Launcher.java:78)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@10.0.9/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23)
|
||||
at cpw.mods.bootstraplauncher@1.1.2/cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:141)
|
||||
@@ -0,0 +1,51 @@
|
||||
2024-02-19 11:23:55,738 main WARN Advanced terminal features are not available in this environment
|
||||
[11:23:55] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher running: args [--username, Lod_Superdark, --version, The Legend of Tinker, --gameDir, F:\我你爹\The Legend of Tinker\.minecraft\versions\The Legend of Tinker, --assetsDir, F:\我你爹\The Legend of Tinker\.minecraft\assets, --assetIndex, 1.18, --uuid, 000000000000300D9C90BD99F9099C10, --accessToken, ????????, --clientId, ${clientid}, --xuid, ${auth_xuid}, --userType, Legacy, --versionType, sad, --width, 854, --height, 480, --launchTarget, forgeclient, --fml.forgeVersion, 40.2.10, --fml.mcVersion, 1.18.2, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20220404.173914]
|
||||
[11:23:55] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher 9.1.3+9.1.3+main.9b69c82a starting: java version 17.0.2 by Oracle Corporation
|
||||
[11:23:56] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/F:/我你爹/The%20Legend%20of%20Tinker/.minecraft/libraries/org/spongepowered/mixin/0.8.5/mixin-0.8.5.jar%2348!/ Service=ModLauncher Env=CLIENT
|
||||
[2024-02-19 11:23:56] [INFO]: I18nUpdate Mod 3.4.1 is loaded in 1.18.2 with Forge
|
||||
Exception in thread "main" java.lang.reflect.InvocationTargetException
|
||||
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
|
||||
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
|
||||
at oolloo.jlw.Wrapper.invokeMain(Wrapper.java:58)
|
||||
at oolloo.jlw.Wrapper.main(Wrapper.java:51)
|
||||
Caused by: java.io.UncheckedIOException: java.io.IOException: Invalid paths argument, contained no existing paths: [F:\我你爹\The Legend of Tinker\.minecraft\libraries\net\minecraftforge\fmlcore\1.18.2-40.2.10\fmlcore-1.18.2-40.2.10.jar]
|
||||
at cpw.mods.securejarhandler@1.0.8/cpw.mods.jarhandling.impl.Jar.<init>(Jar.java:74)
|
||||
at cpw.mods.securejarhandler@1.0.8/cpw.mods.jarhandling.SecureJar.from(SecureJar.java:58)
|
||||
at cpw.mods.securejarhandler@1.0.8/cpw.mods.jarhandling.SecureJar.from(SecureJar.java:54)
|
||||
at cpw.mods.securejarhandler@1.0.8/cpw.mods.jarhandling.SecureJar.from(SecureJar.java:46)
|
||||
at cpw.mods.securejarhandler@1.0.8/cpw.mods.jarhandling.SecureJar.from(SecureJar.java:38)
|
||||
at MC-BOOTSTRAP/fmlloader@1.18.2-40.2.10/net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator.lambda$scanMods$3(MinecraftLocator.java:35)
|
||||
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
|
||||
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
|
||||
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
|
||||
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
|
||||
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
|
||||
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
|
||||
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
|
||||
at MC-BOOTSTRAP/fmlloader@1.18.2-40.2.10/net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator.scanMods(MinecraftLocator.java:37)
|
||||
at MC-BOOTSTRAP/fmlloader@1.18.2-40.2.10/net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer.discoverMods(ModDiscoverer.java:59)
|
||||
at MC-BOOTSTRAP/fmlloader@1.18.2-40.2.10/net.minecraftforge.fml.loading.FMLLoader.beginModScan(FMLLoader.java:166)
|
||||
at MC-BOOTSTRAP/fmlloader@1.18.2-40.2.10/net.minecraftforge.fml.loading.FMLServiceProvider.beginScanning(FMLServiceProvider.java:86)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.TransformationServiceDecorator.runScan(TransformationServiceDecorator.java:112)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.TransformationServicesHandler.lambda$runScanningTransformationServices$8(TransformationServicesHandler.java:100)
|
||||
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
|
||||
at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779)
|
||||
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
|
||||
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
|
||||
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
|
||||
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
|
||||
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
|
||||
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
|
||||
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.TransformationServicesHandler.runScanningTransformationServices(TransformationServicesHandler.java:102)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.TransformationServicesHandler.initializeTransformationServices(TransformationServicesHandler.java:55)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.Launcher.run(Launcher.java:87)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.Launcher.main(Launcher.java:77)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26)
|
||||
at MC-BOOTSTRAP/cpw.mods.modlauncher@9.1.3/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23)
|
||||
at cpw.mods.bootstraplauncher@1.0.0/cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:149)
|
||||
... 6 more
|
||||
Caused by: java.io.IOException: Invalid paths argument, contained no existing paths: [F:\我你爹\The Legend of Tinker\.minecraft\libraries\net\minecraftforge\fmlcore\1.18.2-40.2.10\fmlcore-1.18.2-40.2.10.jar]
|
||||
... 41 more
|
||||
@@ -0,0 +1,20 @@
|
||||
[authlib-injector] [INFO] Logging file: F:\.minecraft\authlib-injector.log
|
||||
[authlib-injector] [INFO] Version: 1.2.1
|
||||
[authlib-injector] [INFO] Authentication server: https://littleskin.cn/api/yggdrasil/
|
||||
2022-12-08 13:15:41,589 main ERROR Error processing element Queue ([Appenders: null]): CLASS_NOT_FOUND
|
||||
[13:15:43] [main/INFO]: ModLauncher running: args [--username, 哎呀呀呀, --version, 1.16.2, --gameDir, F:\\.minecraft, --assetsDir, F:\.minecraft\assets, --assetIndex, 1.16, --uuid, e3c2fb57f8764ecfa1564c1cc92143f2, --accessToken, ❄❄❄❄❄❄❄❄, --userType, mojang, --versionType, HMCL 3.5.3.228, --width, 854, --height, 480, --launchTarget, fmlclient, --fml.forgeVersion, 33.0.61, --fml.mcVersion, 1.16.2, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20200812.004259]
|
||||
[13:15:43] [main/INFO]: ModLauncher 7.0.1+78+master.e9771d8 starting: java version 17.0.4.1 by Oracle Corporation
|
||||
java.lang.NoSuchFieldException: ucp
|
||||
at java.base/java.lang.Class.getDeclaredField(Class.java:2610)
|
||||
at cpw.mods.gross.Java9ClassLoaderUtil.getSystemClassPathURLs(Java9ClassLoaderUtil.java:28)
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler.discoverServices(TransformationServicesHandler.java:139)
|
||||
at cpw.mods.modlauncher.Launcher.run(Launcher.java:74)
|
||||
at cpw.mods.modlauncher.Launcher.main(Launcher.java:65)
|
||||
Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "urls" is null
|
||||
at java.base/jdk.internal.loader.URLClassPath.<init>(URLClassPath.java:155)
|
||||
at java.base/jdk.internal.loader.URLClassPath.<init>(URLClassPath.java:176)
|
||||
at java.base/java.net.URLClassLoader.<init>(URLClassLoader.java:152)
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler$TransformerClassLoader.<init>(TransformationServicesHandler.java:159)
|
||||
at cpw.mods.modlauncher.TransformationServicesHandler.discoverServices(TransformationServicesHandler.java:139)
|
||||
at cpw.mods.modlauncher.Launcher.run(Launcher.java:74)
|
||||
at cpw.mods.modlauncher.Launcher.main(Launcher.java:65)
|
||||
@@ -0,0 +1,3 @@
|
||||
Error occurred during initialization of VM
|
||||
Too small maximum heap
|
||||
[HMCL ProcessListener] Minecraft exit with code 1(0x1).
|
||||
+1
-1
@@ -9,7 +9,7 @@ English | [简体中文](PLATFORM_cn.md)
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (use Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.8~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.4) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
| ARM64 | 👌 (Minecraft 1.8~1.18.2)<br/>✅ (Minecraft 1.19+) | 👌 (Minecraft 1.8~1.20.4) | 👌 (Minecraft 1.6~1.18.2)<br/>✅ (Minecraft 1.19+)<br/>✅ (使用 Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| MIPS64el | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.1) | / | / |
|
||||
| RISC-V 64 | / | 👌 (Minecraft 1.13~1.20.4) | / | / |
|
||||
| LoongArch64 | / | 👌 (Minecraft 1.6~1.20.1) | / | / |
|
||||
| PowerPC-64 (小端序) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
|
||||
@@ -66,9 +66,7 @@ Make sure you have Java installed with JavaFX 8 at least. Liberica Full JDK 8 or
|
||||
| `-Dhmcl.bmclapi.override=<version>` | Override API Root of BMCLAPI download provider, defaults to `https://bmclapi2.bangbang93.com`. e.g. `https://download.mcbbs.net`. |
|
||||
| `-Dhmcl.font.override=<font family>` | Override font family. |
|
||||
| `-Dhmcl.version.override=<version>` | Override the version number. |
|
||||
| ~~`-Dhmcl.update_source.override=<url>`~~ | Override the update source for HMCL itself. (Deprecated, please use `hmcl.hmcl_update_source.override` instead.) |
|
||||
| `-Dhmcl.hmcl_update_source.override=<url>` | Override the update source for HMCL itself. |
|
||||
| `-Dhmcl.resource_update_source.override=<url>` | Override the update source for dynamic remote resources. |
|
||||
| `-Dhmcl.update_source.override=<url>` | Override the update source for HMCL itself. |
|
||||
| `-Dhmcl.authlibinjector.location=<path>` | Use specified authlib-injector (instead of downloading one). |
|
||||
| `-Dhmcl.openjfx.repo=<maven repository url>` | Add custom Maven repository for download OpenJFX. |
|
||||
| `-Dhmcl.native.encoding=<encoding>` | Override the native encoding. |
|
||||
|
||||
Loaded 100 of 108 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user