Compare commits
16
Commits
v3.5.4.232
...
v3.5.4.233
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2e857f08 | ||
|
|
0280a34d00 | ||
|
|
811707b611 | ||
|
|
9152550bda | ||
|
|
6cb4d24600 | ||
|
|
49287453eb | ||
|
|
e7c108d843 | ||
|
|
6c9bf6cb09 | ||
|
|
efc27c8e21 | ||
|
|
7c5ccc906e | ||
|
|
69a6ff6053 | ||
|
|
605d0c6a92 | ||
|
|
9d54ddf456 | ||
|
|
c6afd53e73 | ||
|
|
fd9bb88033 | ||
|
|
316607b333 |
No files matched your search
@@ -19,6 +19,7 @@ public class SkinTransition extends Transition {
|
||||
return count;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public SkinTransition(Duration duration, Function<Double, Double> expression, WritableValue<Number>... observables) {
|
||||
setCycleDuration(duration);
|
||||
this.expression = expression;
|
||||
|
||||
@@ -222,7 +222,7 @@ public final class Launcher extends Application {
|
||||
LOG.info("Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor"));
|
||||
LOG.info("Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"));
|
||||
LOG.info("Java Home: " + System.getProperty("java.home"));
|
||||
LOG.info("Current Directory: " + Paths.get("").toAbsolutePath());
|
||||
LOG.info("Current Directory: " + System.getProperty("user.dir"));
|
||||
LOG.info("HMCL Directory: " + Metadata.HMCL_DIRECTORY);
|
||||
LOG.info("HMCL Jar Path: " + JarUtils.thisJar().map(it -> it.toAbsolutePath().toString()).orElse("Not Found"));
|
||||
LOG.info("Memory: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
|
||||
@@ -231,6 +231,8 @@ public final class Launcher extends Application {
|
||||
.findAny()
|
||||
.map(bean -> bean.getUsage().getUsed() / 1024 / 1024 + "MB")
|
||||
.orElse("Unknown"));
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX)
|
||||
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
|
||||
|
||||
launch(Launcher.class, args);
|
||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
@@ -427,7 +428,11 @@ public final class Accounts {
|
||||
if (exception instanceof NoCharacterException) {
|
||||
return i18n("account.failed.no_character");
|
||||
} else if (exception instanceof ServerDisconnectException) {
|
||||
return i18n("account.failed.connect_authentication_server");
|
||||
if (exception.getCause() instanceof SSLException) {
|
||||
return i18n("account.failed.ssl");
|
||||
} else {
|
||||
return i18n("account.failed.connect_authentication_server");
|
||||
}
|
||||
} else if (exception instanceof ServerResponseMalformedException) {
|
||||
return i18n("account.failed.server_response_malformed");
|
||||
} else if (exception instanceof RemoteAuthenticationException) {
|
||||
|
||||
@@ -172,6 +172,9 @@ public final class Config implements Cloneable, Observable {
|
||||
@SerializedName("promptedVersion")
|
||||
private StringProperty promptedVersion = new SimpleStringProperty();
|
||||
|
||||
@SerializedName("hideAnnouncementVersion")
|
||||
private StringProperty hideAnnouncementVersion = new SimpleStringProperty();
|
||||
|
||||
@SerializedName("_version")
|
||||
private IntegerProperty configVersion = new SimpleIntegerProperty(0);
|
||||
|
||||
@@ -639,4 +642,16 @@ public final class Config implements Cloneable, Observable {
|
||||
public void setPromptedVersion(String promptedVersion) {
|
||||
this.promptedVersion.set(promptedVersion);
|
||||
}
|
||||
|
||||
public String getHideAnnouncementVersion() {
|
||||
return hideAnnouncementVersion.get();
|
||||
}
|
||||
|
||||
public StringProperty hideAnnouncementVersionProperty() {
|
||||
return hideAnnouncementVersion;
|
||||
}
|
||||
|
||||
public void setHideAnnouncementVersion(String hideAnnouncementDevVersion) {
|
||||
this.hideAnnouncementVersion.set(hideAnnouncementDevVersion);
|
||||
}
|
||||
}
|
||||
@@ -323,9 +323,14 @@ public final class Controllers {
|
||||
|
||||
public static void onHyperlinkAction(String href) {
|
||||
if (href.startsWith("hmcl://")) {
|
||||
if ("hmcl://settings/feedback".equals(href)) {
|
||||
Controllers.getSettingsPage().showFeedback();
|
||||
Controllers.navigate(Controllers.getSettingsPage());
|
||||
switch (href) {
|
||||
case "hmcl://settings/feedback":
|
||||
Controllers.getSettingsPage().showFeedback();
|
||||
Controllers.navigate(Controllers.getSettingsPage());
|
||||
break;
|
||||
case "hmcl://hide-announcement":
|
||||
Controllers.getRootPage().getMainPage().hideAnnouncementPane();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FXUtils.openLink(href);
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -162,7 +163,9 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
||||
}
|
||||
|
||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||
if (exception instanceof IOException) {
|
||||
if (exception instanceof SSLException) {
|
||||
return i18n("account.failed.ssl");
|
||||
} else if (exception instanceof IOException) {
|
||||
return i18n("account.failed.connect_injector_server");
|
||||
} else {
|
||||
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
||||
|
||||
@@ -39,6 +39,7 @@ import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class TabHeader extends Control implements TabControl, PageAware {
|
||||
|
||||
public TabHeader(Tab<?>... tabs) {
|
||||
|
||||
@@ -88,7 +88,9 @@ public class TaskExecutorDialogPane extends BorderPane {
|
||||
|
||||
btnCancel.setOnAction(e -> {
|
||||
Optional.ofNullable(executor).ifPresent(TaskExecutor::cancel);
|
||||
onCancel.getCancellationAction().accept(this);
|
||||
if (onCancel.getCancellationAction() != null) {
|
||||
onCancel.getCancellationAction().accept(this);
|
||||
}
|
||||
});
|
||||
|
||||
speedEventHandler = speedEvent -> {
|
||||
|
||||
@@ -33,6 +33,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
@@ -83,7 +84,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
private final ObservableList<Node> versionNodes;
|
||||
private Profile profile;
|
||||
|
||||
private final VBox announcementPane;
|
||||
private VBox announcementPane;
|
||||
private final StackPane updatePane;
|
||||
private final JFXButton menuButton;
|
||||
|
||||
@@ -112,7 +113,15 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
|
||||
setPadding(new Insets(20));
|
||||
|
||||
announcementPane = new VBox(16);
|
||||
if (Metadata.isNightly() || (Metadata.isDev() && !Objects.equals(Metadata.VERSION, config().getHideAnnouncementVersion()))) {
|
||||
announcementPane = new VBox(16);
|
||||
if (Metadata.isNightly()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
|
||||
} else if (Metadata.isDev()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
|
||||
}
|
||||
getChildren().add(announcementPane);
|
||||
}
|
||||
|
||||
updatePane = new StackPane();
|
||||
updatePane.setVisible(false);
|
||||
@@ -221,7 +230,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
launchPane.getChildren().setAll(launchButton, separator, menuButton);
|
||||
}
|
||||
|
||||
getChildren().setAll(announcementPane, updatePane, launchPane);
|
||||
getChildren().addAll(updatePane, launchPane);
|
||||
|
||||
menu.setMaxHeight(365);
|
||||
menu.setMaxWidth(545);
|
||||
@@ -235,14 +244,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
Bindings.bindContent(menu.getContent(), versionNodes);
|
||||
}
|
||||
|
||||
public MainPage() {
|
||||
if (Metadata.isNightly()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint")));
|
||||
} else if (Metadata.isDev()) {
|
||||
announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint")));
|
||||
}
|
||||
}
|
||||
|
||||
private void showUpdate(boolean show) {
|
||||
doAnimation(show);
|
||||
|
||||
@@ -294,6 +295,16 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
showUpdate.set(false);
|
||||
}
|
||||
|
||||
public void hideAnnouncementPane() {
|
||||
if (announcementPane != null) {
|
||||
config().setHideAnnouncementVersion(Metadata.VERSION);
|
||||
Pane parent = (Pane) announcementPane.getParent();
|
||||
if (parent != null)
|
||||
parent.getChildren().remove(announcementPane);
|
||||
announcementPane = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadOnlyObjectWrapper<State> stateProperty() {
|
||||
return state;
|
||||
|
||||
@@ -82,7 +82,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
return new Skin(this);
|
||||
}
|
||||
|
||||
private MainPage getMainPage() {
|
||||
public MainPage getMainPage() {
|
||||
if (mainPage == null) {
|
||||
MainPage mainPage = new MainPage();
|
||||
FXUtils.applyDragListener(mainPage, ModpackHelper::isFileModpackByExtension, modpacks -> {
|
||||
|
||||
@@ -472,6 +472,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadVersion(Profile profile, String versionId) {
|
||||
this.profile = profile;
|
||||
this.versionId = versionId;
|
||||
@@ -555,7 +556,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
} else if (javaAutoDeterminedOption.isSelected()) {
|
||||
versionSetting.setJavaAutoSelected();
|
||||
} else {
|
||||
//noinspection unchecked
|
||||
versionSetting.setJavaVersion(((Pair<JavaVersionType, JavaVersion>) newValue.getUserData()).getValue());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,13 +7,16 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
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 java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
@@ -23,10 +26,24 @@ public final class NativePatcher {
|
||||
|
||||
private static final Library NONEXISTENT_LIBRARY = new Library(null);
|
||||
|
||||
public static Version patchNative(Version version, String gameVersion, JavaVersion javaVersion, VersionSetting settings) {
|
||||
if (settings.isNotPatchNatives())
|
||||
return version;
|
||||
private static final Map<Platform, Map<String, Library>> natives = new HashMap<>();
|
||||
|
||||
private static Map<String, Library> getNatives(Platform platform) {
|
||||
return natives.computeIfAbsent(platform, p -> {
|
||||
//noinspection ConstantConditions
|
||||
try (Reader reader = new InputStreamReader(NativePatcher.class.getResourceAsStream("/assets/natives.json"), StandardCharsets.UTF_8)) {
|
||||
Map<String, Map<String, Library>> natives = JsonUtils.GSON.fromJson(reader, new TypeToken<Map<String, Map<String, Library>>>() {
|
||||
}.getType());
|
||||
|
||||
return natives.getOrDefault(p.toString(), Collections.emptyMap());
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to load native library list", e);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Version patchNative(Version version, String gameVersion, JavaVersion javaVersion, VersionSetting settings) {
|
||||
if (settings.getNativesDirType() == NativesDirectoryType.CUSTOM) {
|
||||
if (gameVersion != null && VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") < 0)
|
||||
return version;
|
||||
@@ -45,6 +62,34 @@ public final class NativePatcher {
|
||||
return version.setLibraries(newLibraries);
|
||||
}
|
||||
|
||||
final boolean useNativeGLFW = settings.isUseNativeGLFW();
|
||||
final boolean useNativeOpenAL = settings.isUseNativeOpenAL();
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX
|
||||
&& (useNativeGLFW || useNativeOpenAL)
|
||||
&& VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") >= 0) {
|
||||
|
||||
version = version.setLibraries(version.getLibraries().stream()
|
||||
.filter(library -> {
|
||||
if (library.getClassifier() != null && library.getClassifier().startsWith("natives")
|
||||
&& "org.lwjgl".equals(library.getGroupId())) {
|
||||
if ((useNativeGLFW && "lwjgl-glfw".equals(library.getArtifactId()))
|
||||
|| (useNativeOpenAL && "lwjgl-openal".equals(library.getArtifactId()))) {
|
||||
LOG.info("Filter out " + library.getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
// Try patch natives
|
||||
|
||||
if (settings.isNotPatchNatives())
|
||||
return version;
|
||||
|
||||
if (javaVersion.getArchitecture().isX86())
|
||||
return version;
|
||||
|
||||
@@ -54,8 +99,8 @@ public final class NativePatcher {
|
||||
&& VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.19") >= 0)
|
||||
return version;
|
||||
|
||||
Map<String, Library> replacements = Hole.nativeReplacement.get(javaVersion.getPlatform().toString());
|
||||
if (replacements == null) {
|
||||
Map<String, Library> replacements = getNatives(javaVersion.getPlatform());
|
||||
if (replacements.isEmpty()) {
|
||||
LOG.warning("No alternative native library provided for platform " + javaVersion.getPlatform());
|
||||
return version;
|
||||
}
|
||||
@@ -68,11 +113,7 @@ public final class NativePatcher {
|
||||
if (library.isNative()) {
|
||||
Library replacement = replacements.getOrDefault(library.getName() + ":natives", NONEXISTENT_LIBRARY);
|
||||
if (replacement == NONEXISTENT_LIBRARY) {
|
||||
if (!(settings.isUseNativeGLFW() && library.getArtifactId().contains("glfw"))
|
||||
&& !(settings.isUseNativeOpenAL() && library.getArtifactId().contains("openal"))) {
|
||||
LOG.warning("No alternative native library " + library.getName() + " provided for platform " + javaVersion.getPlatform());
|
||||
return version;
|
||||
}
|
||||
LOG.warning("No alternative native library " + library.getName() + " provided for platform " + javaVersion.getPlatform());
|
||||
newLibraries.add(library);
|
||||
} else if (replacement != null) {
|
||||
newLibraries.add(replacement);
|
||||
@@ -91,22 +132,6 @@ public final class NativePatcher {
|
||||
}
|
||||
|
||||
public static Library getMesaLoader(JavaVersion javaVersion, Renderer renderer) {
|
||||
Map<String, Library> map = Hole.nativeReplacement.get(javaVersion.getPlatform().toString());
|
||||
return map != null ? map.get(renderer == Renderer.LLVMPIPE ? "software-renderer-loader" : "mesa-loader") : null;
|
||||
}
|
||||
|
||||
private static final class Hole {
|
||||
static Map<String, Map<String, Library>> nativeReplacement;
|
||||
|
||||
static {
|
||||
//noinspection ConstantConditions
|
||||
try (Reader reader = new InputStreamReader(NativePatcher.class.getResourceAsStream("/assets/natives.json"))) {
|
||||
nativeReplacement = JsonUtils.GSON.fromJson(reader, new TypeToken<Map<String, Map<String, Library>>>() {
|
||||
}.getType());
|
||||
} catch (IOException e) {
|
||||
nativeReplacement = Collections.emptyMap();
|
||||
LOG.log(Level.WARNING, "Failed to load native library list", e);
|
||||
}
|
||||
}
|
||||
return getNatives(javaVersion.getPlatform()).get(renderer == Renderer.LLVMPIPE ? "software-renderer-loader" : "mesa-loader");
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ account.failed.migration=Your account needs to be migrated to a Microsoft accoun
|
||||
account.failed.no_character=There are no characters linked to this account.
|
||||
account.failed.server_disconnected=Cannot access authentication server. You can log in offline or try to re-login.
|
||||
account.failed.server_response_malformed=Invalid server response, the authentication server may not be working.
|
||||
account.failed.ssl=An SSL error occurred while connecting to the server. Please try updating your Java.
|
||||
account.failed.wrong_account=You have logged in to the wrong account.
|
||||
account.hmcl.hint=You need to click on "Login" and complete the process in the opened tab in your browser.
|
||||
# avoid too long sequence.
|
||||
@@ -1007,7 +1008,7 @@ settings.advanced.post_exit_command.prompt=Commands to execute after the game ex
|
||||
settings.advanced.renderer=Renderer
|
||||
settings.advanced.renderer.default=OpenGL (Default)
|
||||
settings.advanced.renderer.d3d12=DirectX 12 (Poor performance and compatibility)
|
||||
settings.advanced.renderer.llvmpipe=軟渲染器 (Poor efficiency and best compatibility)
|
||||
settings.advanced.renderer.llvmpipe=Software (Poor efficiency and best compatibility)
|
||||
settings.advanced.renderer.zink=Vulkan (Best performance and poor compatibility)
|
||||
settings.advanced.server_ip=Server Address
|
||||
settings.advanced.server_ip.prompt=Join automatically after launching the game.
|
||||
@@ -1114,14 +1115,12 @@ update.channel.dev.hint=You are currently using a beta build of the launcher, wh
|
||||
\n\
|
||||
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.\n\
|
||||
\n\
|
||||
To hide this tooltip, you need to make a <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donation</a> or submit a valuable feedback.
|
||||
<a href="hmcl://hide-announcement">Click here</a> to hide this tooltip for the current version.
|
||||
update.channel.dev.title=Beta Version Notice
|
||||
update.channel.nightly=Nightly
|
||||
update.channel.nightly.hint=You are currently using a nightly build of the launcher, which may include some extra features, but is also always more unstable than the other versions.\n\
|
||||
\n\
|
||||
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.\n\
|
||||
\n\
|
||||
To hide this tooltip, you need to make a <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donation</a> or submit a valuable feedback.
|
||||
If you encounter any bug or issue, you can go to <a href="hmcl://settings/feedback">the feedback page</a> to report it, or tell us in our <a href="https://discord.gg/jVvC7HfM6U">Discord</a> or <a href="https://kook.top/Kx7n3t">KOOK</a> community.
|
||||
update.channel.nightly.title=Nightly Version Notice
|
||||
update.channel.stable=Release
|
||||
update.checking=Checking for Updates
|
||||
|
||||
@@ -1017,14 +1017,12 @@ update.channel.dev.hint=Actualmente está utilizando una versión beta del launc
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a>.\n\
|
||||
\n\
|
||||
Para ocultar esta información, es necesario hacer una <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donación</a> o enviar un valioso comentario.
|
||||
<a href="hmcl://hide-announcement">Haga clic aquí</a> para ocultar el recordatorio para la versión de prueba actual.
|
||||
update.channel.dev.title=Aviso de versión beta
|
||||
update.channel.nightly=Nocturna
|
||||
update.channel.nightly.hint=Estás utilizando una versión nocturna del launcher, que puede incluir algunas características adicionales, pero también es siempre más inestable que las otras versiones.\n\
|
||||
\n\
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a> community.\n\
|
||||
\n\
|
||||
Para ocultar esta información sobre herramientas, es necesario hacer una <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">donación</a> o enviar un valioso comentario.
|
||||
Si encuentra algún error o problema, puede ir a la <a href="hmcl://settings/feedback">página de comentarios</a> para informar de ello, o bien para decírnoslo en nuestro <a href="https://discord.gg/jVvC7HfM6U">Discord</a> o en la comunidad <a href="https://kookapp.co/Kx7n3t">KOOK</a> community.
|
||||
update.channel.nightly.title=Aviso de versión nocturna
|
||||
update.channel.stable=Estable
|
||||
update.checking=Buscando actualizaciones
|
||||
|
||||
@@ -844,16 +844,15 @@ update=更新
|
||||
update.accept=更新
|
||||
update.changelog=変更
|
||||
update.channel.dev=ベータ
|
||||
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n \
|
||||
ベータ版は不安定なはずです!\n \
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n \n \
|
||||
このヒントを非表示にするには、<a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">寄付</a>するか、フィードバックに貢献をする必要があります。
|
||||
update.channel.dev.hint=ベータ版を使用しています。ベータ版には、リリースバージョンと比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
|
||||
ベータ版は不安定なはずです!\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n\n\
|
||||
現在のテスト版のヒントを非表示にするには、<a href="hmcl://hide-announcement">ここをクリックしてください</a>。
|
||||
update.channel.dev.title=ベータ版のヒント
|
||||
update.channel.nightly=アルファ
|
||||
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n \
|
||||
アルファ版は不安定です!\n \
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。\n \n \
|
||||
このヒントを非表示にするには、<a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">寄付</a>するか、フィードバックに印象的な貢献をする必要があります。
|
||||
update.channel.nightly.hint=アルファ版を使用しています。これには、ベータ版およびリリース版と比較していくつかの追加機能が含まれている可能性があり、テストにのみ使用されます。\n\
|
||||
アルファ版は不安定です!\n\
|
||||
問題が発生した場合は、<a href="hmcl://settings/feedback">フィードバックページ</a>にアクセスして報告するか、チャット <a href="https://discord.gg/jVvC7HfM6U">Discord</a>または<a href="https://kook.top/Kx7n3t">KOOK</a>で問題を報告します。
|
||||
update.channel.nightly.title=アルファ版のヒント
|
||||
update.channel.stable=リリース
|
||||
update.checking=更新の確認
|
||||
|
||||
@@ -853,13 +853,12 @@ update.channel.dev=Бета
|
||||
update.channel.dev.hint=Вы используете бета-версию, которая может включать некоторые дополнительные функции по сравнению с релизной версией, используемой только для тестирования.\n\
|
||||
Бета-версия может быть нестабильной!\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.\n\n\
|
||||
Чтобы скрыть эту подсказку, нужно <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">пожертвовать</a> или сделать внушительный вклад через обратную связь.
|
||||
<a href="hmcl://hide-announcement">Нажмите здесь</a>, чтобы скрыть подсказку для текущей тестовой версии.
|
||||
update.channel.dev.title=Подсказки для бета-версии
|
||||
update.channel.nightly=Альфа
|
||||
update.channel.nightly.hint=Вы используете альфа-версию, которая может включать некоторые дополнительные функциональные возможности по сравнению с бета-версией и версией релиза, используемой только для тестирования.\n\
|
||||
Альфа-версия может быть нестабильной!\n\
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.\n\n\
|
||||
Чтобы скрыть эту подсказку, нужно <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">пожертвовать</a> или сделать внушительный вклад через обратную связь.
|
||||
Если вы встретили какие-то проблемы, вы можете зайти на <a href="hmcl://settings/feedback">страницу обратной связи</a>, чтобы сообщить о них, или присоединиться к чату <a href="https://discord.gg/jVvC7HfM6U">Discord</a> или <a href="https://kook.top/Kx7n3t">KOOK</a>, чтобы сообщить о проблемах.
|
||||
update.channel.nightly.title=Подсказки для альфа-версии
|
||||
update.channel.stable=Релиз
|
||||
update.checking=Проверка наличия обновлений
|
||||
|
||||
@@ -69,6 +69,7 @@ account.failed.server_disconnected=無法訪問登錄伺服器,賬戶信息刷
|
||||
若最近沒有刷新賬戶信息,則可能導致賬戶信息過期失效,\n\
|
||||
若為 微軟賬戶 啟動遊戲,賬戶信息過期失效可能將無法進入需賬戶驗證的伺服器。
|
||||
account.failed.server_response_malformed=無法解析認證伺服器回應,可能是伺服器故障
|
||||
account.failed.ssl=連接服務器時發生了 SSL 錯誤,可能網站證書已過期或你使用的 Java 版本過低,請嘗試更新 Java
|
||||
account.failed.wrong_account=登錄了錯誤的帳號
|
||||
account.hmcl.hint=你需要點擊“登入”按鈕,並在打開的網頁中完成登入
|
||||
account.injector.add=新增認證伺服器
|
||||
@@ -81,7 +82,7 @@ account.injector.server_url=伺服器位址
|
||||
account.injector.server_name=伺服器名稱
|
||||
account.login=登入
|
||||
account.login.hint=我們不會保存你的密碼
|
||||
account.login.skip=重新刷新帳戶
|
||||
account.login.skip=跳過刷新帳戶
|
||||
account.login.retry=再次刷新帳戶
|
||||
account.login.refresh=重新登錄
|
||||
account.login.refresh.microsoft.hint=因為賬戶授權失效,你需要重新添加微軟賬戶
|
||||
@@ -983,13 +984,12 @@ update.channel.dev=測試版
|
||||
update.channel.dev.hint=你正在使用測試版。測試版包含一些未在正式版中包含的測試性功能,僅用於體驗新功能。\n\
|
||||
測試版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。\n\n\
|
||||
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
|
||||
<a href="hmcl://hide-announcement">點擊此處</a>為當前版本隱藏該提示。
|
||||
update.channel.dev.title=測試版提示
|
||||
update.channel.nightly=預覽版
|
||||
update.channel.nightly.hint=你正在使用預覽版。預覽版可能會每天更新一次,包含一些未在正式版和測試版中包含的測試性功能,僅用於體驗新功能\n\
|
||||
測試版功能未受充分驗證,使用起來可能不穩定!\n\
|
||||
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。\n\n\
|
||||
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">贊助</a> 或在回饋過程中作出貢獻以隱藏本提示。
|
||||
如果你遇到了使用問題,可以在設置的 <a href="hmcl://settings/feedback">回饋頁面</a> 中進行回饋,或加入回饋頁面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK 群</a>以回饋問題。
|
||||
update.channel.nightly.title=預覽版提示
|
||||
update.channel.stable=建議版本
|
||||
update.checking=正在檢查更新
|
||||
|
||||
@@ -69,6 +69,7 @@ account.failed.server_disconnected=无法访问登录服务器,账户信息刷
|
||||
若最近没有刷新账户信息,则可能导致账户信息过期失效,\n\
|
||||
若为 微软账户 启动游戏,账户信息过期失效可能将无法进入需账户验证的服务器。
|
||||
account.failed.server_response_malformed=无法解析认证服务器响应,可能是服务器故障
|
||||
account.failed.ssl=连接服务器时发生了 SSL 错误,可能网站证书已过期或你使用的 Java 版本过低,请尝试更新 Java
|
||||
account.failed.wrong_account=登录了错误的帐号
|
||||
account.hmcl.hint=你需要点击“登录”按钮,并在打开的网页中完成登录
|
||||
account.injector.add=添加认证服务器
|
||||
@@ -981,13 +982,12 @@ update.channel.dev=测试版
|
||||
update.channel.dev.hint=你正在使用测试版。测试版包含一些未在正式版中包含的测试性功能,仅用于体验新功能。\n\
|
||||
测试版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。\n\n\
|
||||
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
|
||||
<a href="hmcl://hide-announcement">点击此处</a>为当前版本隐藏该提示。
|
||||
update.channel.dev.title=测试版提示
|
||||
update.channel.nightly=预览版
|
||||
update.channel.nightly.hint=你正在使用预览版。预览版可能会每天更新一次,包含一些未在正式版和测试版中包含的测试性功能,仅用于体验新功能。\n\
|
||||
测试版功能未受充分验证,使用起来可能不稳定!<a href="https://hmcl.huangyuhui.net/download">下载稳定版</a>\n\
|
||||
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。\n\n\
|
||||
你需要 <a href="https://hmcl.huangyuhui.net/api/redirect/sponsor">赞助</a> 或在反馈过程中作出贡献以隐藏本提示。
|
||||
如果你遇到了使用问题,可以在设置的 <a href="hmcl://settings/feedback">反馈页面</a> 中进行反馈,或加入反馈页面中提供的 <a href="https://discord.gg/jVvC7HfM6U">Discord</a> 或 <a href="https://kook.top/Kx7n3t">KOOK</a>群以反馈问题。欢迎关注 B 站账号 <a href="https://space.bilibili.com/1445341">huanghongxun</a> 以关注 HMCL 的开发进展。
|
||||
update.channel.nightly.title=预览版提示
|
||||
update.channel.stable=推荐版本
|
||||
update.checking=正在检查更新
|
||||
|
||||
@@ -1519,6 +1519,248 @@
|
||||
"com.mojang:text2speech:1.12.4:natives": null,
|
||||
"com.mojang:text2speech:1.13.9:natives-linux": null
|
||||
},
|
||||
"linux-riscv64": {
|
||||
"org.lwjgl:lwjgl:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
|
||||
"sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb",
|
||||
"size": 724243
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl:3.1.6:natives": {
|
||||
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
|
||||
"downloads": {
|
||||
"classifiers": {
|
||||
"linux-riscv64": {
|
||||
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
|
||||
"size": 11905844
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"exclude": [
|
||||
"META-INF/"
|
||||
]
|
||||
},
|
||||
"natives": {
|
||||
"linux": "linux-riscv64"
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-jemalloc:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
|
||||
"sha1": "a817bcf213db49f710603677457567c37d53e103",
|
||||
"size": 36601
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl-openal:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-openal:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
|
||||
"sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd",
|
||||
"size": 88237
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-openal:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl-opengl:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-opengl:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
|
||||
"sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411",
|
||||
"size": 921563
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-opengl:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl-glfw:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-glfw:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
|
||||
"sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0",
|
||||
"size": 128801
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-glfw:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl-stb:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-stb:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
|
||||
"sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f",
|
||||
"size": 112380
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-stb:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl-tinyfd:3.1.6": {
|
||||
"name": "org.lwjgl:lwjgl-tinyfd:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
|
||||
"sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347",
|
||||
"size": 6767
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-tinyfd:3.1.6:natives": null,
|
||||
"org.lwjgl:lwjgl:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar",
|
||||
"sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb",
|
||||
"size": 724243
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl:3.2.2:natives": {
|
||||
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
|
||||
"downloads": {
|
||||
"classifiers": {
|
||||
"linux-riscv64": {
|
||||
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
|
||||
"size": 11905844
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"exclude": [
|
||||
"META-INF/"
|
||||
]
|
||||
},
|
||||
"natives": {
|
||||
"linux": "linux-riscv64"
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-jemalloc:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar",
|
||||
"sha1": "a817bcf213db49f710603677457567c37d53e103",
|
||||
"size": 36601
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl-openal:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-openal:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar",
|
||||
"sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd",
|
||||
"size": 88237
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-openal:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl-opengl:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-opengl:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar",
|
||||
"sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411",
|
||||
"size": 921563
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-opengl:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl-glfw:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-glfw:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar",
|
||||
"sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0",
|
||||
"size": 128801
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-glfw:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl-stb:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-stb:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar",
|
||||
"sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f",
|
||||
"size": 112380
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-stb:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl-tinyfd:3.2.2": {
|
||||
"name": "org.lwjgl:lwjgl-tinyfd:3.3.1",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar",
|
||||
"sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347",
|
||||
"size": 6767
|
||||
}
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-tinyfd:3.2.2:natives": null,
|
||||
"org.lwjgl:lwjgl:3.3.1:natives-linux": {
|
||||
"name": "org.glavo.hmcl:lwjgl3-natives:3.3.1-rc1",
|
||||
"downloads": {
|
||||
"classifiers": {
|
||||
"linux-riscv64": {
|
||||
"path": "org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/hmcl/lwjgl3-natives/3.3.1-rc1-linux-riscv64/lwjgl3-natives-3.3.1-rc1-linux-riscv64.jar",
|
||||
"sha1": "8544853aa77b10692548b2a728cd890afd740ba5",
|
||||
"size": 11905844
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"exclude": [
|
||||
"META-INF/"
|
||||
]
|
||||
},
|
||||
"natives": {
|
||||
"linux": "linux-riscv64"
|
||||
}
|
||||
},
|
||||
"org.lwjgl:lwjgl-jemalloc:3.3.1:natives-linux": null,
|
||||
"org.lwjgl:lwjgl-openal:3.3.1:natives-linux": null,
|
||||
"org.lwjgl:lwjgl-opengl:3.3.1:natives-linux": null,
|
||||
"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,
|
||||
"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,
|
||||
"com.mojang:text2speech:1.12.4:natives": null,
|
||||
"com.mojang:text2speech:1.13.9:natives-linux": null
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"software-renderer-loader": {
|
||||
"name": "org.glavo:llvmpipe-loader:1.0",
|
||||
|
||||
@@ -9,8 +9,8 @@ dependencies {
|
||||
api("org.tukaani:xz:1.9")
|
||||
api("org.hildan.fxgson:fx-gson:5.0.0")
|
||||
api("org.jenkins-ci:constant-pool-scanner:1.2")
|
||||
api("com.github.steveice10:opennbt:1.4")
|
||||
api("com.github.steveice10:opennbt:1.5")
|
||||
api("org.nanohttpd:nanohttpd:2.3.1")
|
||||
api("org.apache.commons:commons-compress:1.22")
|
||||
compileOnlyApi("org.jetbrains:annotations:24.0.0")
|
||||
api("org.apache.commons:commons-compress:1.23.0")
|
||||
compileOnlyApi("org.jetbrains:annotations:24.0.1")
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import java.util.UUID;
|
||||
*/
|
||||
@Immutable
|
||||
public class AuthInfo implements AutoCloseable {
|
||||
public static final String USER_TYPE_MSA = "msa";
|
||||
public static final String USER_TYPE_MOJANG = "mojang";
|
||||
public static final String USER_TYPE_LEGACY = "legacy";
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ public class MicrosoftSession {
|
||||
public AuthInfo toAuthInfo() {
|
||||
requireNonNull(profile);
|
||||
|
||||
return new AuthInfo(profile.getName(), profile.getId(), accessToken, AuthInfo.USER_TYPE_MOJANG, "{}");
|
||||
return new AuthInfo(profile.getName(), profile.getId(), accessToken, AuthInfo.USER_TYPE_MSA, "{}");
|
||||
}
|
||||
|
||||
public static class User {
|
||||
|
||||
@@ -114,7 +114,7 @@ public class OfflineAccount extends Account {
|
||||
@Override
|
||||
public AuthInfo logIn() throws AuthenticationException {
|
||||
// Using "legacy" user type here because "mojang" user type may cause "invalid session token" or "disconnected" when connecting to a game server.
|
||||
AuthInfo authInfo = new AuthInfo(username, uuid, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), AuthInfo.USER_TYPE_LEGACY, "{}");
|
||||
AuthInfo authInfo = new AuthInfo(username, uuid, UUIDTypeAdapter.fromUUID(UUID.randomUUID()), AuthInfo.USER_TYPE_MSA, "{}");
|
||||
|
||||
if (loadAuthlibInjector(skin)) {
|
||||
CompletableFuture<AuthlibInjectorArtifactInfo> artifactTask = CompletableFuture.supplyAsync(() -> {
|
||||
@@ -154,7 +154,7 @@ public class OfflineAccount extends Account {
|
||||
private YggdrasilServer server;
|
||||
|
||||
public OfflineAuthInfo(AuthInfo authInfo, AuthlibInjectorArtifactInfo artifact) {
|
||||
super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), USER_TYPE_LEGACY, authInfo.getUserProperties());
|
||||
super(authInfo.getUsername(), authInfo.getUUID(), authInfo.getAccessToken(), USER_TYPE_MSA, authInfo.getUserProperties());
|
||||
|
||||
this.artifact = artifact;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ public class YggdrasilSession {
|
||||
String name = tryCast(storage.get("displayName"), String.class).orElseThrow(() -> new IllegalArgumentException("displayName is missing"));
|
||||
String clientToken = tryCast(storage.get("clientToken"), String.class).orElseThrow(() -> new IllegalArgumentException("clientToken is missing"));
|
||||
String accessToken = tryCast(storage.get("accessToken"), String.class).orElseThrow(() -> new IllegalArgumentException("accessToken is missing"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> userProperties = tryCast(storage.get("userProperties"), Map.class).orElse(null);
|
||||
return new YggdrasilSession(clientToken, accessToken, new GameProfile(uuid, name), null, userProperties);
|
||||
}
|
||||
@@ -104,7 +105,7 @@ public class YggdrasilSession {
|
||||
if (selectedProfile == null)
|
||||
throw new IllegalStateException("No character is selected");
|
||||
|
||||
return new AuthInfo(selectedProfile.getName(), selectedProfile.getId(), accessToken, AuthInfo.USER_TYPE_MOJANG,
|
||||
return new AuthInfo(selectedProfile.getName(), selectedProfile.getId(), accessToken, AuthInfo.USER_TYPE_MSA,
|
||||
Optional.ofNullable(userProperties)
|
||||
.map(properties -> properties.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey,
|
||||
|
||||
@@ -23,10 +23,10 @@ import org.jackhuang.hmcl.util.platform.Platform;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Supports operations on versioning.
|
||||
@@ -250,8 +250,8 @@ public interface GameRepository extends VersionProvider {
|
||||
*/
|
||||
Path getLoggingObject(String version, String assetId, LoggingInfo loggingInfo);
|
||||
|
||||
default List<String> getClasspath(Version version) {
|
||||
List<String> classpath = new ArrayList<>();
|
||||
default Set<String> getClasspath(Version version) {
|
||||
Set<String> classpath = new LinkedHashSet<>();
|
||||
for (Library library : version.getLibraries())
|
||||
if (library.appliesToCurrentEnvironment() && !library.isNative()) {
|
||||
File f = getLibraryFile(version, library);
|
||||
|
||||
@@ -207,7 +207,7 @@ public class DefaultLauncher extends Launcher {
|
||||
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
|
||||
}
|
||||
|
||||
List<String> classpath = repository.getClasspath(version);
|
||||
Set<String> classpath = repository.getClasspath(version);
|
||||
|
||||
File jar = repository.getVersionJar(version);
|
||||
if (!jar.exists() || !jar.isFile())
|
||||
|
||||
@@ -25,10 +25,8 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
@@ -41,20 +39,20 @@ public final class Logging {
|
||||
public static final Logger LOG = Logger.getLogger("HMCL");
|
||||
private static final ByteArrayOutputStream storedLogs = new ByteArrayOutputStream(IOUtils.DEFAULT_BUFFER_SIZE);
|
||||
|
||||
private static final ConcurrentMap<String, String> forbiddenTokens = new ConcurrentHashMap<>();
|
||||
private static volatile String[] accessTokens = new String[0];
|
||||
|
||||
public static void registerForbiddenToken(String token, String replacement) {
|
||||
forbiddenTokens.put(token, replacement);
|
||||
}
|
||||
public static synchronized void registerAccessToken(String token) {
|
||||
final String[] oldAccessTokens = accessTokens;
|
||||
final String[] newAccessTokens = Arrays.copyOf(oldAccessTokens, oldAccessTokens.length + 1);
|
||||
|
||||
public static void registerAccessToken(String accessToken) {
|
||||
registerForbiddenToken(accessToken, "<access token>");
|
||||
newAccessTokens[oldAccessTokens.length] = token;
|
||||
|
||||
accessTokens = newAccessTokens;
|
||||
}
|
||||
|
||||
public static String filterForbiddenToken(String message) {
|
||||
for (Map.Entry<String, String> entry : forbiddenTokens.entrySet()) {
|
||||
message = message.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
for (String token : accessTokens)
|
||||
message = message.replace(token, "<access token>");
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -30,20 +27,6 @@ public final class ReflectionHelper {
|
||||
private ReflectionHelper() {
|
||||
}
|
||||
|
||||
private static Method accessible0;
|
||||
|
||||
static {
|
||||
try {
|
||||
accessible0 = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class);
|
||||
accessible0.setAccessible(true);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAccessible(AccessibleObject obj) throws InvocationTargetException, IllegalAccessException {
|
||||
accessible0.invoke(obj, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get caller, this method is caller sensitive.
|
||||
*
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class Result<T> {
|
||||
|
||||
public T get() {
|
||||
throw new IllegalStateException("TriState not ok");
|
||||
}
|
||||
|
||||
public boolean isOK() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(T result) {
|
||||
return new OK<>(Objects.requireNonNull(result));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Result<T> error() {
|
||||
return (Result<T>) Error.INSTANCE;
|
||||
}
|
||||
|
||||
private static class OK<T> extends Result<T> {
|
||||
private final T result;
|
||||
|
||||
public OK(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Error<T> extends Result<T> {
|
||||
public static final Error<Void> INSTANCE = new Error<>();
|
||||
|
||||
@Override
|
||||
public boolean isError() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.gson;
|
||||
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
public abstract class GsonSerializerHelper<T> implements JsonSerializer<T>, JsonDeserializer<T> {
|
||||
|
||||
protected static void add(JsonObject object, String property, JsonElement value) {
|
||||
if (value == null) return;
|
||||
object.add(property, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -51,8 +52,10 @@ public final class UUIDTypeAdapter extends TypeAdapter<UUID> {
|
||||
return value.toString().replace("-", "");
|
||||
}
|
||||
|
||||
private static final Pattern regex = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})");
|
||||
|
||||
public static UUID fromString(String input) {
|
||||
return UUID.fromString(input.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
|
||||
return UUID.fromString(regex.matcher(input).replaceFirst("$1-$2-$3-$4-$5"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,8 +46,8 @@ public enum Architecture {
|
||||
PPC64LE(BIT_64, "PowerPC-64 (Little-Endian)"),
|
||||
S390(BIT_32),
|
||||
S390X(BIT_64, "S390x"),
|
||||
RISCV32(BIT_32, "RISC-V (32 Bit)"),
|
||||
RISCV64(BIT_64, "RISC-V"),
|
||||
RISCV32(BIT_32, "RISC-V 32"),
|
||||
RISCV64(BIT_64, "RISC-V 64"),
|
||||
LOONGARCH32(BIT_32, "LoongArch32"),
|
||||
LOONGARCH64_OW(BIT_64, "LoongArch64 (old world)"),
|
||||
LOONGARCH64(BIT_64, "LoongArch64"),
|
||||
@@ -69,12 +69,6 @@ public enum Architecture {
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
Architecture(Bits bits, String displayName, String identifier) {
|
||||
this.checkedName = identifier;
|
||||
this.displayName = displayName;
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
public Bits getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
@@ -29,7 +27,9 @@ import java.util.*;
|
||||
* Maybe we can migrate to org.jenkins-ci:version-number:1.7?
|
||||
* @see <a href="http://maven.apache.org/pom.html#Version_Order_Specification">Specification</a>
|
||||
*/
|
||||
public class VersionNumber implements Comparable<VersionNumber> {
|
||||
public final class VersionNumber implements Comparable<VersionNumber> {
|
||||
|
||||
public static final Comparator<String> VERSION_COMPARATOR = Comparator.comparing(VersionNumber::asVersion);
|
||||
|
||||
public static VersionNumber asVersion(String version) {
|
||||
Objects.requireNonNull(version);
|
||||
@@ -41,68 +41,85 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
}
|
||||
|
||||
public static boolean isIntVersionNumber(String version) {
|
||||
if (version.chars().noneMatch(ch -> ch != '.' && (ch < '0' || ch > '9'))
|
||||
&& !version.contains("..") && StringUtils.isNotBlank(version)) {
|
||||
String[] arr = version.split("\\.");
|
||||
for (String str : arr)
|
||||
if (str.length() > 9)
|
||||
// Numbers which are larger than 1e9 cannot be stored as integer.
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
if (version.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
boolean cont = true;
|
||||
do {
|
||||
int dotIndex = version.indexOf('.', idx);
|
||||
if (dotIndex == idx || dotIndex == version.length() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int endIndex;
|
||||
if (dotIndex < 0) {
|
||||
cont = false;
|
||||
endIndex = version.length();
|
||||
} else {
|
||||
endIndex = dotIndex;
|
||||
}
|
||||
|
||||
if (endIndex - idx > 9)
|
||||
// Numbers which are larger than 10^10 cannot be stored as integer
|
||||
return false;
|
||||
|
||||
for (int i = idx; i < endIndex; i++) {
|
||||
char ch = version.charAt(i);
|
||||
if (ch < '0' || ch > '9')
|
||||
return false;
|
||||
}
|
||||
|
||||
idx = endIndex + 1;
|
||||
} while (cont);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String value;
|
||||
private String canonical;
|
||||
private ListItem items;
|
||||
|
||||
private interface Item {
|
||||
int INTEGER_ITEM = 0;
|
||||
int STRING_ITEM = 1;
|
||||
int LIST_ITEM = 2;
|
||||
int LONG_ITEM = 0;
|
||||
int BIGINTEGER_ITEM = 1;
|
||||
int STRING_ITEM = 2;
|
||||
int LIST_ITEM = 3;
|
||||
|
||||
int compareTo(Item item);
|
||||
|
||||
int getType();
|
||||
|
||||
boolean isNull();
|
||||
|
||||
void appendTo(StringBuilder buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a numeric item in the version item list.
|
||||
*/
|
||||
private static class IntegerItem
|
||||
implements Item {
|
||||
private final BigInteger value;
|
||||
private static final class LongItem implements Item {
|
||||
private final long value;
|
||||
|
||||
public static final IntegerItem ZERO = new IntegerItem();
|
||||
public static final LongItem ZERO = new LongItem(0L);
|
||||
|
||||
private IntegerItem() {
|
||||
this.value = BigInteger.ZERO;
|
||||
}
|
||||
|
||||
IntegerItem(String str) {
|
||||
this.value = new BigInteger(str);
|
||||
LongItem(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return INTEGER_ITEM;
|
||||
return LONG_ITEM;
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return BigInteger.ZERO.equals(value);
|
||||
return value == 0L;
|
||||
}
|
||||
|
||||
public int compareTo(Item item) {
|
||||
if (item == null) {
|
||||
return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
||||
return value == 0L ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
||||
}
|
||||
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
return value.compareTo(((IntegerItem) item).value);
|
||||
case LONG_ITEM:
|
||||
long itemValue = ((LongItem) item).value;
|
||||
return Long.compare(value, itemValue);
|
||||
case BIGINTEGER_ITEM:
|
||||
return -1;
|
||||
|
||||
case STRING_ITEM:
|
||||
return 1; // 1.1 > 1-sp
|
||||
@@ -111,10 +128,68 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
return 1; // 1.1 > 1-1
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
throw new AssertionError("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
buffer.append(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return Long.toString(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a numeric item in the version item list.
|
||||
*/
|
||||
private static final class BigIntegerItem implements Item {
|
||||
private final BigInteger value;
|
||||
|
||||
BigIntegerItem(String str) {
|
||||
this.value = new BigInteger(str);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return BIGINTEGER_ITEM;
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
// Never be 0
|
||||
// return BigInteger.ZERO.equals(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
public int compareTo(Item item) {
|
||||
if (item == null) {
|
||||
// return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (item.getType()) {
|
||||
case LONG_ITEM:
|
||||
return 1;
|
||||
case BIGINTEGER_ITEM:
|
||||
return value.compareTo(((BigIntegerItem) item).value);
|
||||
|
||||
case STRING_ITEM:
|
||||
return 1; // 1.1 > 1-sp
|
||||
|
||||
case LIST_ITEM:
|
||||
return 1; // 1.1 > 1-1
|
||||
|
||||
default:
|
||||
throw new AssertionError("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
buffer.append(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
@@ -123,9 +198,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
/**
|
||||
* Represents a string in the version item list, usually a qualifier.
|
||||
*/
|
||||
private static class StringItem
|
||||
implements Item {
|
||||
private String value;
|
||||
private static final class StringItem implements Item {
|
||||
private final String value;
|
||||
|
||||
StringItem(String value) {
|
||||
this.value = value;
|
||||
@@ -145,7 +219,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
return 1;
|
||||
}
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
case LONG_ITEM:
|
||||
case BIGINTEGER_ITEM:
|
||||
return -1; // 1.any < 1.1 ?
|
||||
|
||||
case STRING_ITEM:
|
||||
@@ -155,10 +230,15 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
return -1; // 1.any < 1-1
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
throw new AssertionError("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
buffer.append(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
@@ -168,14 +248,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
* Represents a version list item. This class is used both for the global item list and for sub-lists (which start
|
||||
* with '-(number)' in the version specification).
|
||||
*/
|
||||
private static class ListItem
|
||||
extends ArrayList<Item>
|
||||
implements Item {
|
||||
Character separator;
|
||||
private static final class ListItem extends ArrayList<Item> implements Item {
|
||||
private final Character separator;
|
||||
|
||||
public ListItem() {}
|
||||
ListItem() {
|
||||
this.separator = null;
|
||||
}
|
||||
|
||||
public ListItem(char separator) {
|
||||
ListItem(char separator) {
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
@@ -184,7 +264,7 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
}
|
||||
|
||||
public boolean isNull() {
|
||||
return (size() == 0);
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
void normalize() {
|
||||
@@ -209,7 +289,8 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
return first.compareTo(null);
|
||||
}
|
||||
switch (item.getType()) {
|
||||
case INTEGER_ITEM:
|
||||
case LONG_ITEM:
|
||||
case BIGINTEGER_ITEM:
|
||||
return -1; // 1-1 < 1.0.x
|
||||
|
||||
case STRING_ITEM:
|
||||
@@ -234,36 +315,46 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
return 0;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("invalid item: " + item.getClass());
|
||||
throw new AssertionError("invalid item: " + item.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuilder buffer) {
|
||||
if (separator != null) {
|
||||
buffer.append((char) separator);
|
||||
}
|
||||
|
||||
final int initLength = buffer.length();
|
||||
|
||||
for (Item item : this) {
|
||||
if (buffer.length() > initLength) {
|
||||
if (!(item instanceof ListItem))
|
||||
buffer.append('.');
|
||||
}
|
||||
item.appendTo(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (Item item : this) {
|
||||
if (buffer.length() > 0) {
|
||||
if (!(item instanceof ListItem))
|
||||
buffer.append('.');
|
||||
}
|
||||
buffer.append(item);
|
||||
}
|
||||
if (separator != null)
|
||||
return separator + buffer.toString();
|
||||
else
|
||||
return buffer.toString();
|
||||
appendTo(buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public VersionNumber(String version) {
|
||||
parseVersion(version);
|
||||
}
|
||||
private static final int MAX_LONGITEM_LENGTH = 18;
|
||||
|
||||
private void parseVersion(String version) {
|
||||
private final String value;
|
||||
public final ListItem items;
|
||||
private final String canonical;
|
||||
|
||||
private VersionNumber(String version) {
|
||||
this.value = version;
|
||||
|
||||
ListItem list = items = new ListItem();
|
||||
ListItem list = this.items = new ListItem();
|
||||
|
||||
Stack<Item> stack = new Stack<>();
|
||||
Deque<Item> stack = new ArrayDeque<>();
|
||||
stack.push(list);
|
||||
|
||||
boolean isDigit = false;
|
||||
@@ -275,14 +366,14 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
|
||||
if (c == '.') {
|
||||
if (i == startIndex) {
|
||||
list.add(IntegerItem.ZERO);
|
||||
list.add(LongItem.ZERO);
|
||||
} else {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
}
|
||||
startIndex = i + 1;
|
||||
} else if ("!\"#$%&'()*+,-/:;<=>?@[\\]^_`{|}~".indexOf(c) != -1) {
|
||||
if (i == startIndex) {
|
||||
list.add(IntegerItem.ZERO);
|
||||
list.add(LongItem.ZERO);
|
||||
} else {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
}
|
||||
@@ -290,7 +381,7 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
|
||||
list.add(list = new ListItem(c));
|
||||
stack.push(list);
|
||||
} else if (Character.isDigit(c)) {
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
if (!isDigit && i > startIndex) {
|
||||
list.add(parseItem(version.substring(startIndex, i)));
|
||||
startIndex = i;
|
||||
@@ -322,11 +413,42 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
list.normalize();
|
||||
}
|
||||
|
||||
canonical = items.toString();
|
||||
this.canonical = items.toString();
|
||||
}
|
||||
|
||||
// For simple version
|
||||
private VersionNumber(String version, ListItem items) {
|
||||
this.value = version;
|
||||
this.items = items;
|
||||
this.canonical = version;
|
||||
}
|
||||
|
||||
private static Item parseItem(String buf) {
|
||||
return buf.chars().allMatch(Character::isDigit) ? new IntegerItem(buf) : new StringItem(buf);
|
||||
int numberLength = 0;
|
||||
boolean leadingZero = true;
|
||||
for (int i = 0; i < buf.length(); i++) {
|
||||
char ch = buf.charAt(i);
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (ch != '0') {
|
||||
leadingZero = false;
|
||||
}
|
||||
|
||||
if (!leadingZero) {
|
||||
numberLength++;
|
||||
}
|
||||
} else {
|
||||
return new StringItem(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (numberLength == 0) {
|
||||
return LongItem.ZERO;
|
||||
} else if (numberLength <= MAX_LONGITEM_LENGTH) {
|
||||
// Numbers which are larger than 10^19 cannot be stored as long
|
||||
return new LongItem(Long.parseLong(buf));
|
||||
} else {
|
||||
return new BigIntegerItem(buf);
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(String o) {
|
||||
@@ -364,6 +486,4 @@ public class VersionNumber implements Comparable<VersionNumber> {
|
||||
public int hashCode() {
|
||||
return canonical.hashCode();
|
||||
}
|
||||
|
||||
public static final Comparator<String> VERSION_COMPARATOR = Comparator.comparing(VersionNumber::asVersion);
|
||||
}
|
||||
@@ -19,105 +19,135 @@ package org.jackhuang.hmcl.util.versioning;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.jackhuang.hmcl.util.versioning.VersionNumber.isIntVersionNumber;
|
||||
import static org.jackhuang.hmcl.util.versioning.VersionNumber.normalize;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class VersionNumberTest {
|
||||
|
||||
@Test
|
||||
public void testCanonical() {
|
||||
VersionNumber u, v;
|
||||
assertEquals("3.2", normalize("3.2.0.0"));
|
||||
assertEquals("3.2-5", normalize("3.2.0.0-5"));
|
||||
assertEquals("3.2", normalize("3.2.0.0-0"));
|
||||
assertEquals("3.2", normalize("3.2--------"));
|
||||
assertEquals("3.2", normalize("3.0002"));
|
||||
assertEquals("1.7.2$%%^@&snapshot-3.1.1", normalize("1.7.2$%%^@&snapshot-3.1.1"));
|
||||
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999"));
|
||||
assertEquals("1.99999999999999999999", normalize("1.0099999999999999999999"));
|
||||
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999.0"));
|
||||
assertEquals("1.99999999999999999999", normalize("1.99999999999999999999--------"));
|
||||
}
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0");
|
||||
assertEquals("3.2", v.getCanonical());
|
||||
@Test
|
||||
public void testIsIntVersion() {
|
||||
assertFalse(isIntVersionNumber(""));
|
||||
assertFalse(isIntVersionNumber(" "));
|
||||
assertFalse(isIntVersionNumber("."));
|
||||
assertFalse(isIntVersionNumber("1."));
|
||||
assertFalse(isIntVersionNumber(".1"));
|
||||
assertFalse(isIntVersionNumber(".1."));
|
||||
assertFalse(isIntVersionNumber("1..8"));
|
||||
assertFalse(isIntVersionNumber("1.8."));
|
||||
assertFalse(isIntVersionNumber(".1.8"));
|
||||
assertFalse(isIntVersionNumber("1.7.10forge1614_FTBInfinity"));
|
||||
assertFalse(isIntVersionNumber("3.2-5"));
|
||||
assertFalse(isIntVersionNumber("1.9999999999"));
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0-5");
|
||||
assertEquals("3.2-5", v.getCanonical());
|
||||
assertTrue(isIntVersionNumber("0"));
|
||||
assertTrue(isIntVersionNumber("1"));
|
||||
assertTrue(isIntVersionNumber("0.1"));
|
||||
assertTrue(isIntVersionNumber("0.1.0"));
|
||||
assertTrue(isIntVersionNumber("1.8"));
|
||||
assertTrue(isIntVersionNumber("1.12.2"));
|
||||
assertTrue(isIntVersionNumber("1.13.1"));
|
||||
assertTrue(isIntVersionNumber("1.999999999"));
|
||||
assertTrue(isIntVersionNumber("999999999.0"));
|
||||
}
|
||||
|
||||
v = VersionNumber.asVersion("3.2.0.0-0");
|
||||
assertEquals("3.2", v.getCanonical());
|
||||
private static void assertLessThan(String s1, String s2) {
|
||||
Supplier<String> messageSupplier = () -> String.format("%s should be less than %s", s1, s2);
|
||||
|
||||
v = VersionNumber.asVersion("3.2--------");
|
||||
assertEquals("3.2", v.getCanonical());
|
||||
VersionNumber v1 = VersionNumber.asVersion(s1);
|
||||
VersionNumber v2 = VersionNumber.asVersion(s2);
|
||||
|
||||
v = VersionNumber.asVersion("1.7.2$%%^@&snapshot-3.1.1");
|
||||
assertEquals("1.7.2$%%^@&snapshot-3.1.1", v.getCanonical());
|
||||
assertTrue(v1.compareTo(v2) < 0, messageSupplier);
|
||||
assertTrue(v2.compareTo(v1) > 0, messageSupplier);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparator() {
|
||||
VersionNumber u, v;
|
||||
|
||||
u = VersionNumber.asVersion("1.7.10forge1614_FTBInfinity");
|
||||
v = VersionNumber.asVersion("1.12.2");
|
||||
assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.8.0_51");
|
||||
v = VersionNumber.asVersion("1.8.0.51");
|
||||
assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.8.0_151");
|
||||
v = VersionNumber.asVersion("1.8.0_77");
|
||||
assertTrue(u.compareTo(v) > 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.6.0_22");
|
||||
v = VersionNumber.asVersion("1.8.0_11");
|
||||
assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.7.0_22");
|
||||
v = VersionNumber.asVersion("1.7.99");
|
||||
assertTrue(u.compareTo(v) < 0);
|
||||
|
||||
u = VersionNumber.asVersion("1.12.2-14.23.5.2760");
|
||||
v = VersionNumber.asVersion("1.12.2-14.23.4.2739");
|
||||
assertTrue(u.compareTo(v) > 0);
|
||||
assertLessThan("1.7.10forge1614_FTBInfinity", "1.12.2");
|
||||
assertLessThan("1.8.0_51", "1.8.0.51");
|
||||
assertLessThan("1.8.0_77", "1.8.0_151");
|
||||
assertLessThan("1.6.0_22", "1.8.0_11");
|
||||
assertLessThan("1.7.0_22", "1.7.99");
|
||||
assertLessThan("1.12.2-14.23.4.2739", "1.12.2-14.23.5.2760");
|
||||
assertLessThan("1.9", "1.99999999999999999999");
|
||||
assertLessThan("1.99999999999999999999", "1.199999999999999999999");
|
||||
assertLessThan("1.99999999999999999999", "2");
|
||||
assertLessThan("1.99999999999999999999", "2.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSorting() {
|
||||
List<String> input = Arrays.asList(
|
||||
"1.10",
|
||||
"1.10.2",
|
||||
"1.10.2-All the Mods",
|
||||
"1.10.2-AOE",
|
||||
"1.10.2-AOE-1.1.5",
|
||||
"1.10.2-forge2511-Age_of_Progression",
|
||||
"1.10.2-forge2511-AOE-1.1.2",
|
||||
"1.10.2-forge2511-ATM-E",
|
||||
"1.10.2-forge2511-simple_life_2",
|
||||
"1.10.2-forge2511_bxztest",
|
||||
"1.10.2-forge2511_Farming_Valley",
|
||||
"1.10.2-forge2511中文",
|
||||
"1.10.2-FTB_Beyond",
|
||||
"1.10.2-LiteLoader1.10.2",
|
||||
"1.12.2",
|
||||
"1.12.2_Modern_Skyblock-3.4.2",
|
||||
"1.13.1",
|
||||
final Comparator<String> comparator = VersionNumber.VERSION_COMPARATOR.thenComparing(String::compareTo);
|
||||
final List<String> input = Collections.unmodifiableList(Arrays.asList(
|
||||
"0",
|
||||
"0.10.0",
|
||||
"1.6.4",
|
||||
"1.6.4-Forge9.11.1.1345",
|
||||
"1.7.10",
|
||||
"1.7.10-1614",
|
||||
"1.7.10-1614-test",
|
||||
"1.7.10Agrarian_Skies_2",
|
||||
"1.7.10-F1614-L",
|
||||
"1.7.10-FL1614_04",
|
||||
"1.7.10-Forge10.13.4.1614-1.7.10",
|
||||
"1.7.10-Forge1614",
|
||||
"1.7.10-Forge1614.1",
|
||||
"1.7.10Agrarian_Skies_2",
|
||||
"1.7.10forge1614test",
|
||||
"1.7.10forge1614_ATlauncher",
|
||||
"1.7.10forge1614_FTBInfinity",
|
||||
"1.7.10Forge1614_FTBInfinity-2.6.0",
|
||||
"1.7.10Forge1614_FTBInfinity-3.0.1",
|
||||
"1.7.10-Forge1614.1",
|
||||
"1.7.10forge1614_ATlauncher",
|
||||
"1.7.10forge1614_FTBInfinity",
|
||||
"1.7.10forge1614_FTBInfinity_server",
|
||||
"1.7.10forge1614test",
|
||||
"1.7.10-1614",
|
||||
"1.7.10-1614-test",
|
||||
"1.8",
|
||||
"1.8-forge1577",
|
||||
"1.8.9",
|
||||
"1.8.9-forge1902",
|
||||
"1.9");
|
||||
input.sort(Comparator.comparing(VersionNumber::asVersion));
|
||||
"1.9",
|
||||
"1.10",
|
||||
"1.10.2",
|
||||
"1.10.2-AOE",
|
||||
"1.10.2-AOE-1.1.5",
|
||||
"1.10.2-All the Mods",
|
||||
"1.10.2-FTB_Beyond",
|
||||
"1.10.2-LiteLoader1.10.2",
|
||||
"1.10.2-forge2511-AOE-1.1.2",
|
||||
"1.10.2-forge2511-ATM-E",
|
||||
"1.10.2-forge2511-Age_of_Progression",
|
||||
"1.10.2-forge2511_Farming_Valley",
|
||||
"1.10.2-forge2511_bxztest",
|
||||
"1.10.2-forge2511-simple_life_2",
|
||||
"1.10.2-forge2511中文",
|
||||
"1.12.2",
|
||||
"1.12.2_Modern_Skyblock-3.4.2",
|
||||
"1.13.1",
|
||||
"1.99999999999999999999",
|
||||
"2",
|
||||
"2.0",
|
||||
"2.1"));
|
||||
|
||||
List<String> output = new ArrayList<>(input);
|
||||
output.sort(comparator);
|
||||
assertIterableEquals(input, output);
|
||||
|
||||
Collections.shuffle(output, new Random(0));
|
||||
output.sort(comparator);
|
||||
assertIterableEquals(input, output);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,10 +7,10 @@
|
||||
| ARM64 | 👌 (1.8+)<br/>✅ (use x86 emulation) | 👌 | 👌 (1.6~1.18.2)<br/>✅ (1.19+)<br/>✅ (use Rosetta 2) | ❔ |
|
||||
| ARM32 | /️ | 👌 | / | / |
|
||||
| MIPS64el | / | 👌 | / | / |
|
||||
| RISC-V 64 | / | 👌 (1.13+) | / | / |
|
||||
| LoongArch64 | / | 👌 (for Old World)<br/>❔ (for New World) | / | / |
|
||||
| PowerPC-64 (Little-Endian) | / | ❔ | / | / |
|
||||
| S390x | / | ❔ | / | / |
|
||||
| RISC-V | / | ❔ | / | / |
|
||||
|
||||
Legend:
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ val jfxPlatforms = listOf(
|
||||
Platform("linux-arm32", "linux-arm32-monocle", unsupportedModules = listOf("media", "web")),
|
||||
Platform("linux-arm64", "linux-aarch64"),
|
||||
Platform("linux-loongarch64_ow", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19-ea+10-loongson64", unsupportedModules = listOf("media", "web")),
|
||||
Platform("linux-riscv64", "linux", groupId = "org.glavo.hmcl.openjfx", version = "19.0.2.1-riscv64", unsupportedModules = listOf("media", "web")),
|
||||
)
|
||||
|
||||
val jfxInClasspath =
|
||||
|
||||
Reference in new issue
Block a user