Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d70712a18f | ||
|
|
05c5b53c52 | ||
|
|
e98c7dfef1 | ||
|
|
8ba0e16dd9 | ||
|
|
77397f6722 | ||
|
|
c9ba789b7c | ||
|
|
91135b961e | ||
|
|
435790aa03 | ||
|
|
c252d7027e | ||
|
|
9d0c8da310 | ||
|
|
84b3312ebb | ||
|
|
f351d1d133 | ||
|
|
5ca1120223 | ||
|
|
6aa4019221 | ||
|
|
73fb8bda10 | ||
|
|
cc16f84992 | ||
|
|
b4945a150e | ||
|
|
8bcbe74d82 | ||
|
|
69c2429e92 | ||
|
|
88e83dc44a | ||
|
|
87c2126e5a | ||
|
|
abf1ec3eb8 | ||
|
|
139fb41149 | ||
|
|
6c986921d6 | ||
|
|
b8e6d6fc96 | ||
|
|
75e4aee8aa | ||
|
|
abda9f50e1 | ||
|
|
c33ef5170b | ||
|
|
a3cb871928 | ||
|
|
62e965c6fe | ||
|
|
99a031cea4 | ||
|
|
f97b9b9382 |
No files matched your search
@@ -9,12 +9,12 @@ body:
|
||||
请确认 Issues 列表无重复的项目。
|
||||
Please make sure that no duplicate issues have already been submitted.
|
||||
- type: textarea
|
||||
id: description
|
||||
id: summary
|
||||
attributes:
|
||||
label: 描述 | Description
|
||||
label: 概述 | Summary
|
||||
description: |
|
||||
请详细描述你想加入的新功能。
|
||||
Please describe the new feature in detail.
|
||||
请介绍你想加入的新功能。
|
||||
Please describe the new feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -26,3 +26,10 @@ body:
|
||||
Please describe why you want to add the feature or enhancement to HMCL.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 详情 | Description
|
||||
description: |
|
||||
在这里可以补充描述该功能的具体实现方式或建议。(可选)
|
||||
Describe implementation details or suggestions here. (Optional)
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Build HMCLauncher
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'HMCLauncher/**'
|
||||
- '.github/workflows/build-launcher.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'HMCLauncher/**'
|
||||
- '.github/workflows/build-launcher.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
msbuild-architecture: x86
|
||||
- name: Build HMCLauncher
|
||||
run: msbuild /p:Configuration=Release /t:Rebuild /verbosity:detailed .\HMCLauncher\
|
||||
- name: Copy HMCLauncher to assets
|
||||
run: Copy-Item .\HMCLauncher\Release\HMCLauncher.exe -Destination .\HMCL\src\main\resources\assets\HMCLauncher.exe
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
run: .\gradlew makeExecutables --no-daemon
|
||||
env:
|
||||
MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }}
|
||||
MICROSOFT_AUTH_SECRET: ${{ secrets.MICROSOFT_AUTH_SECRET }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
- name: Get short SHA
|
||||
run: echo "SHORT_SHA=$("${{ github.sha }}".SubString(0, 7))" >> $env:GITHUB_ENV
|
||||
- name: Copy HMCLauncher to libs
|
||||
run: Copy-Item .\HMCLauncher\Release\HMCLauncher.exe -Destination .\HMCL\build\libs\HMCLauncher.exe
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: HMCLauncher-${{ env.SHORT_SHA }}
|
||||
path: HMCL/build/libs/*.exe
|
||||
@@ -5,7 +5,6 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'HMCLauncher/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -42,7 +42,6 @@ minecraft-exported-crash-info*
|
||||
.nb-gradle
|
||||
|
||||
*.exe
|
||||
!/HMCL/src/main/resources/assets/HMCLauncher.exe
|
||||
|
||||
# macos
|
||||
.DS_Store
|
||||
|
||||
+52
-19
@@ -33,12 +33,18 @@ val microsoftAuthId = System.getenv("MICROSOFT_AUTH_ID") ?: ""
|
||||
val microsoftAuthSecret = System.getenv("MICROSOFT_AUTH_SECRET") ?: ""
|
||||
val curseForgeApiKey = System.getenv("CURSEFORGE_API_KEY") ?: ""
|
||||
|
||||
val launcherExe = System.getenv("HMCL_LAUNCHER_EXE")
|
||||
|
||||
version = "$versionRoot.$buildNumber"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":HMCLCore"))
|
||||
implementation("libs:JFoenix")
|
||||
implementation("com.twelvemonkeys.imageio:imageio-webp:3.12.0")
|
||||
|
||||
if (launcherExe == null) {
|
||||
implementation("org.glavo.hmcl:HMCLauncher:3.6.0.1")
|
||||
}
|
||||
}
|
||||
|
||||
fun digest(algorithm: String, bytes: ByteArray): ByteArray = MessageDigest.getInstance(algorithm).digest(bytes)
|
||||
@@ -106,7 +112,7 @@ tasks.jar {
|
||||
|
||||
val jarPath = tasks.jar.get().archiveFile.get().asFile
|
||||
|
||||
tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
|
||||
tasks.shadowJar {
|
||||
archiveClassifier.set(null as String?)
|
||||
|
||||
exclude("**/package-info.class")
|
||||
@@ -154,23 +160,18 @@ tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("sha
|
||||
}
|
||||
}
|
||||
|
||||
if (launcherExe != null) {
|
||||
into("assets") {
|
||||
from(file(launcherExe))
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
attachSignature(jarPath)
|
||||
createChecksum(jarPath)
|
||||
}
|
||||
}
|
||||
|
||||
fun createExecutable(suffix: String, header: String) {
|
||||
val output = File(jarPath.parentFile, jarPath.nameWithoutExtension + '.' + suffix)
|
||||
|
||||
output.outputStream().use {
|
||||
it.write(File(project.projectDir, header).readBytes())
|
||||
it.write(jarPath.readBytes())
|
||||
}
|
||||
|
||||
createChecksum(output)
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
into("META-INF/versions/11") {
|
||||
from(sourceSets["java11"].output)
|
||||
@@ -178,11 +179,31 @@ tasks.processResources {
|
||||
dependsOn(tasks["java11Classes"])
|
||||
}
|
||||
|
||||
val makeExecutables = tasks.create("makeExecutables") {
|
||||
val makeExecutables by tasks.registering {
|
||||
val extensions = listOf("exe", "sh")
|
||||
|
||||
dependsOn(tasks.jar)
|
||||
|
||||
inputs.file(jarPath)
|
||||
outputs.files(extensions.map { File(jarPath.parentFile, jarPath.nameWithoutExtension + '.' + it) })
|
||||
|
||||
doLast {
|
||||
createExecutable("exe", "src/main/resources/assets/HMCLauncher.exe")
|
||||
createExecutable("sh", "src/main/resources/assets/HMCLauncher.sh")
|
||||
val jarContent = jarPath.readBytes()
|
||||
|
||||
ZipFile(jarPath).use { zipFile ->
|
||||
for (extension in extensions) {
|
||||
val output = File(jarPath.parentFile, jarPath.nameWithoutExtension + '.' + extension)
|
||||
val entry = zipFile.getEntry("assets/HMCLauncher.$extension")
|
||||
?: throw GradleException("HMCLauncher.$extension not found")
|
||||
|
||||
output.outputStream().use { outputStream ->
|
||||
zipFile.getInputStream(entry).use { it.copyTo(outputStream) }
|
||||
outputStream.write(jarContent)
|
||||
}
|
||||
|
||||
createChecksum(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,9 +211,9 @@ tasks.build {
|
||||
dependsOn(makeExecutables)
|
||||
}
|
||||
|
||||
fun parseToolOptions(options: String?): List<String> {
|
||||
fun parseToolOptions(options: String?): MutableList<String> {
|
||||
if (options == null)
|
||||
return listOf()
|
||||
return mutableListOf()
|
||||
|
||||
val builder = StringBuilder()
|
||||
val result = mutableListOf<String>()
|
||||
@@ -240,7 +261,7 @@ fun parseToolOptions(options: String?): List<String> {
|
||||
return result
|
||||
}
|
||||
|
||||
tasks.create<JavaExec>("run") {
|
||||
tasks.register<JavaExec>("run") {
|
||||
dependsOn(tasks.jar)
|
||||
|
||||
group = "application"
|
||||
@@ -249,10 +270,22 @@ tasks.create<JavaExec>("run") {
|
||||
workingDir = rootProject.rootDir
|
||||
|
||||
val vmOptions = parseToolOptions(System.getenv("HMCL_JAVA_OPTS"))
|
||||
if (vmOptions.none { it.startsWith("-Dhmcl.offline.auth.restricted=") })
|
||||
vmOptions += "-Dhmcl.offline.auth.restricted=false"
|
||||
|
||||
jvmArgs(vmOptions)
|
||||
|
||||
val hmclJavaHome = System.getenv("HMCL_JAVA_HOME")
|
||||
if (hmclJavaHome != null) {
|
||||
this.executable(
|
||||
file(hmclJavaHome).resolve("bin")
|
||||
.resolve(if (System.getProperty("os.name").lowercase().startsWith("windows")) "java.exe" else "java")
|
||||
)
|
||||
}
|
||||
|
||||
doFirst {
|
||||
logger.quiet("HMCL_JAVA_OPTS: $vmOptions")
|
||||
logger.quiet("HMCL_JAVA_OPTS: {}", vmOptions)
|
||||
logger.quiet("HMCL_JAVA_HOME: {}", hmclJavaHome ?: System.getProperty("java.home"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -509,16 +509,16 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
public static long getAllocatedMemory(long minimum, long available, boolean auto) {
|
||||
if (auto) {
|
||||
available -= 384 * 1024 * 1024; // Reserve 384MiB memory for off-heap memory and HMCL itself
|
||||
available -= 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself
|
||||
if (available <= 0) {
|
||||
return minimum;
|
||||
}
|
||||
|
||||
final long threshold = 8L * 1024 * 1024 * 1024;
|
||||
final long threshold = 8L * 1024 * 1024 * 1024; // 8 GiB
|
||||
final long suggested = Math.min(available <= threshold
|
||||
? (long) (available * 0.8)
|
||||
: (long) (threshold * 0.8 + (available - threshold) * 0.2),
|
||||
16384L * 1024 * 1024);
|
||||
16L * 1024 * 1024 * 1024);
|
||||
return Math.max(minimum, suggested);
|
||||
} else {
|
||||
return minimum;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -64,7 +64,7 @@ public final class HMCLModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile file, Path path, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader file, Path path, Charset encoding) throws IOException, JsonParseException {
|
||||
String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json");
|
||||
Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, HMCLModpack.class).setEncoding(encoding);
|
||||
String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json");
|
||||
|
||||
@@ -894,9 +894,6 @@ public final class LauncherHelper {
|
||||
|
||||
}
|
||||
|
||||
private static final String ORACLEJDK_DOWNLOAD_LINK = "https://www.java.com/download/";
|
||||
private static final String OPENJDK_DOWNLOAD_LINK = "https://learn.microsoft.com/java/openjdk/download";
|
||||
|
||||
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public static void stopManagedProcesses() {
|
||||
|
||||
@@ -17,12 +17,13 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.logging.Logger;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.Zipper;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
@@ -93,9 +94,8 @@ public final class LogExporter {
|
||||
if (Files.isRegularFile(file)) {
|
||||
FileTime time = Files.readAttributes(file, BasicFileAttributes.class).lastModifiedTime();
|
||||
if (time.toMillis() >= processStartTime) {
|
||||
try {
|
||||
String crashLog = Logger.filterForbiddenToken(FileUtils.readText(file, OperatingSystem.NATIVE_CHARSET));
|
||||
zipper.putTextFile(crashLog, file.getFileName().toString());
|
||||
try (BufferedReader reader = IOUtils.newBufferedReaderMaybeNativeEncoding(file)) {
|
||||
zipper.putLines(reader.lines().map(Logger::filterForbiddenToken), file.getFileName().toString());
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to read log file: " + file, e);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseModpackProvider;
|
||||
import org.jackhuang.hmcl.mod.mcbbs.McbbsModpackManifest;
|
||||
@@ -83,7 +83,7 @@ public final class ModpackHelper {
|
||||
}
|
||||
|
||||
public static Modpack readModpackManifest(Path file, Charset charset) throws UnsupportedModpackException, ManuallyCreatedModpackException {
|
||||
try (ZipFile zipFile = CompressingUtils.openZipFile(file, charset)) {
|
||||
try (ZipArchiveReader zipFile = CompressingUtils.openZipFile(file, charset)) {
|
||||
// Order for trying detecting manifest is necessary here.
|
||||
// Do not change to iterating providers.
|
||||
for (ModpackProvider provider : new ModpackProvider[]{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.java;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.DigestUtils;
|
||||
import org.jackhuang.hmcl.util.Hex;
|
||||
|
||||
@@ -22,6 +22,7 @@ import javafx.beans.Observable;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
@@ -50,6 +51,7 @@ import java.util.*;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static javafx.collections.FXCollections.observableArrayList;
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
|
||||
import static org.jackhuang.hmcl.util.Lang.immutableListOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
@@ -277,6 +279,30 @@ public final class Accounts {
|
||||
selected = accounts.get(0);
|
||||
}
|
||||
|
||||
if (!globalConfig().isEnableOfflineAccount())
|
||||
for (Account account : accounts) {
|
||||
if (account instanceof MicrosoftAccount) {
|
||||
globalConfig().setEnableOfflineAccount(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!globalConfig().isEnableOfflineAccount())
|
||||
accounts.addListener(new ListChangeListener<Account>() {
|
||||
@Override
|
||||
public void onChanged(Change<? extends Account> change) {
|
||||
while (change.next()) {
|
||||
for (Account account : change.getAddedSubList()) {
|
||||
if (account instanceof MicrosoftAccount) {
|
||||
accounts.removeListener(this);
|
||||
globalConfig().setEnableOfflineAccount(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
selectedAccount.set(selected);
|
||||
|
||||
InvalidationListener listener = o -> {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.setting;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.ToNumberPolicy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
@@ -60,6 +61,7 @@ public final class Config implements Observable {
|
||||
.registerTypeAdapter(EnumBackgroundImage.class, new EnumOrdinalDeserializer<>(EnumBackgroundImage.class)) // backward compatibility for backgroundType
|
||||
.registerTypeAdapter(Proxy.Type.class, new EnumOrdinalDeserializer<>(Proxy.Type.class)) // backward compatibility for hasProxy
|
||||
.setPrettyPrinting()
|
||||
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
|
||||
.create();
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
@@ -53,6 +55,8 @@ public final class GlobalConfig implements Observable {
|
||||
|
||||
private final IntegerProperty logRetention = new SimpleIntegerProperty();
|
||||
|
||||
private final BooleanProperty enableOfflineAccount = new SimpleBooleanProperty(false);
|
||||
|
||||
private final ObservableSet<String> userJava = FXCollections.observableSet(new LinkedHashSet<>());
|
||||
|
||||
private final ObservableSet<String> disabledJava = FXCollections.observableSet(new LinkedHashSet<>());
|
||||
@@ -115,6 +119,18 @@ public final class GlobalConfig implements Observable {
|
||||
this.logRetention.set(logRetention);
|
||||
}
|
||||
|
||||
public boolean isEnableOfflineAccount() {
|
||||
return enableOfflineAccount.get();
|
||||
}
|
||||
|
||||
public BooleanProperty enableOfflineAccountProperty() {
|
||||
return enableOfflineAccount;
|
||||
}
|
||||
|
||||
public void setEnableOfflineAccount(boolean value) {
|
||||
enableOfflineAccount.set(value);
|
||||
}
|
||||
|
||||
public ObservableSet<String> getUserJava() {
|
||||
return userJava;
|
||||
}
|
||||
@@ -129,7 +145,8 @@ public final class GlobalConfig implements Observable {
|
||||
"platformPromptVersion",
|
||||
"logRetention",
|
||||
"userJava",
|
||||
"disabledJava"
|
||||
"disabledJava",
|
||||
"enableOfflineAccount"
|
||||
));
|
||||
|
||||
@Override
|
||||
@@ -142,6 +159,9 @@ public final class GlobalConfig implements Observable {
|
||||
jsonObject.add("agreementVersion", context.serialize(src.getAgreementVersion()));
|
||||
jsonObject.add("platformPromptVersion", context.serialize(src.getPlatformPromptVersion()));
|
||||
jsonObject.add("logRetention", context.serialize(src.getLogRetention()));
|
||||
if (src.enableOfflineAccount.get())
|
||||
jsonObject.addProperty("enableOfflineAccount", true);
|
||||
|
||||
if (!src.getUserJava().isEmpty())
|
||||
jsonObject.add("userJava", context.serialize(src.getUserJava()));
|
||||
|
||||
@@ -165,6 +185,7 @@ public final class GlobalConfig implements Observable {
|
||||
config.setAgreementVersion(Optional.ofNullable(obj.get("agreementVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setPlatformPromptVersion(Optional.ofNullable(obj.get("platformPromptVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setLogRetention(Optional.ofNullable(obj.get("logRetention")).map(JsonElement::getAsInt).orElse(20));
|
||||
config.setEnableOfflineAccount(Optional.ofNullable(obj.get("enableOfflineAccount")).map(JsonElement::getAsBoolean).orElse(false));
|
||||
|
||||
JsonElement userJava = obj.get("userJava");
|
||||
if (userJava != null && userJava.isJsonArray()) {
|
||||
|
||||
@@ -142,7 +142,7 @@ public class GameCrashWindow extends Stage {
|
||||
|
||||
String log;
|
||||
try {
|
||||
log = FileUtils.readText(latestLog);
|
||||
log = FileUtils.readTextMaybeNativeEncoding(latestLog);
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to read logs/latest.log", e);
|
||||
return pair(new HashSet<CrashReportAnalyzer.Result>(), new HashSet<String>());
|
||||
|
||||
@@ -362,7 +362,9 @@ public class InstallerItem extends Control {
|
||||
}
|
||||
return i18n("install.installer.version", s.version);
|
||||
} else if (state instanceof InstallableState) {
|
||||
return i18n("install.installer.not_installed");
|
||||
return control.style == Style.CARD
|
||||
? i18n("install.installer.do_not_install")
|
||||
: i18n("install.installer.not_installed");
|
||||
} else if (state instanceof IncompatibleState) {
|
||||
return i18n("install.installer.incompatible", i18n("install.installer." + ((IncompatibleState) state).incompatibleItemName));
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.account;
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -44,14 +45,37 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.createSelectedItemPropertyFor;
|
||||
|
||||
public class AccountListPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
public final class AccountListPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
static final BooleanProperty RESTRICTED = new SimpleBooleanProperty(true);
|
||||
|
||||
static {
|
||||
String property = System.getProperty("hmcl.offline.auth.restricted", "auto");
|
||||
|
||||
if ("false".equals(property)
|
||||
|| "auto".equals(property) && "Asia/Shanghai".equals(ZoneId.systemDefault().getId())
|
||||
|| globalConfig().isEnableOfflineAccount())
|
||||
RESTRICTED.set(false);
|
||||
else
|
||||
globalConfig().enableOfflineAccountProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> o, Boolean oldValue, Boolean newValue) {
|
||||
if (newValue) {
|
||||
globalConfig().enableOfflineAccountProperty().removeListener(this);
|
||||
RESTRICTED.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final ObservableList<AccountListItem> items;
|
||||
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("account.manage")));
|
||||
private final ListProperty<Account> accounts = new SimpleListProperty<>(this, "accounts", FXCollections.observableArrayList());
|
||||
@@ -88,6 +112,7 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
private static class AccountListPageSkin extends DecoratorAnimatedPageSkin<AccountListPage> {
|
||||
|
||||
private final ObservableList<AdvancedListItem> authServerItems;
|
||||
private ChangeListener<Boolean> holder;
|
||||
|
||||
public AccountListPageSkin(AccountListPage skinnable) {
|
||||
super(skinnable);
|
||||
@@ -96,24 +121,21 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
VBox boxMethods = new VBox();
|
||||
{
|
||||
boxMethods.getStyleClass().add("advanced-list-box-content");
|
||||
boxMethods.getChildren().add(new ClassTitle(i18n("account.create").toUpperCase(Locale.ROOT)));
|
||||
FXUtils.setLimitWidth(boxMethods, 200);
|
||||
|
||||
AdvancedListItem offlineItem = new AdvancedListItem();
|
||||
offlineItem.getStyleClass().add("navigation-drawer-item");
|
||||
offlineItem.setActionButtonVisible(false);
|
||||
offlineItem.setTitle(i18n("account.methods.offline"));
|
||||
offlineItem.setLeftGraphic(wrap(SVG.PERSON));
|
||||
offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE)));
|
||||
boxMethods.getChildren().add(offlineItem);
|
||||
|
||||
AdvancedListItem microsoftItem = new AdvancedListItem();
|
||||
microsoftItem.getStyleClass().add("navigation-drawer-item");
|
||||
microsoftItem.setActionButtonVisible(false);
|
||||
microsoftItem.setTitle(i18n("account.methods.microsoft"));
|
||||
microsoftItem.setLeftGraphic(wrap(SVG.MICROSOFT));
|
||||
microsoftItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MICROSOFT)));
|
||||
boxMethods.getChildren().add(microsoftItem);
|
||||
|
||||
AdvancedListItem offlineItem = new AdvancedListItem();
|
||||
offlineItem.getStyleClass().add("navigation-drawer-item");
|
||||
offlineItem.setActionButtonVisible(false);
|
||||
offlineItem.setTitle(i18n("account.methods.offline"));
|
||||
offlineItem.setLeftGraphic(wrap(SVG.PERSON));
|
||||
offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE)));
|
||||
|
||||
VBox boxAuthServers = new VBox();
|
||||
authServerItems = MappedObservableList.create(skinnable.authServersProperty(), server -> {
|
||||
@@ -149,7 +171,29 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
return item;
|
||||
});
|
||||
Bindings.bindContent(boxAuthServers.getChildren(), authServerItems);
|
||||
boxMethods.getChildren().add(boxAuthServers);
|
||||
|
||||
ClassTitle title = new ClassTitle(i18n("account.create").toUpperCase(Locale.ROOT));
|
||||
if (RESTRICTED.get()) {
|
||||
VBox wrapper = new VBox(offlineItem, boxAuthServers);
|
||||
wrapper.setPadding(Insets.EMPTY);
|
||||
FXUtils.installFastTooltip(wrapper, i18n("account.login.restricted"));
|
||||
|
||||
offlineItem.setDisable(true);
|
||||
boxAuthServers.setDisable(true);
|
||||
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, wrapper);
|
||||
|
||||
holder = FXUtils.onWeakChange(RESTRICTED, value -> {
|
||||
if (!value) {
|
||||
holder = null;
|
||||
offlineItem.setDisable(false);
|
||||
boxAuthServers.setDisable(false);
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, offlineItem, boxAuthServers);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
boxMethods.getChildren().setAll(title, microsoftItem, offlineItem, boxAuthServers);
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedListItem addAuthServerItem = new AdvancedListItem();
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.jackhuang.hmcl.ui.account;
|
||||
|
||||
import com.jfoenix.controls.*;
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -36,6 +39,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.auth.CharacterSelector;
|
||||
@@ -108,12 +112,17 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
|
||||
public CreateAccountPane(AccountFactory<?> factory) {
|
||||
if (factory == null) {
|
||||
showMethodSwitcher = true;
|
||||
String preferred = config().getPreferredLoginType();
|
||||
try {
|
||||
factory = Accounts.getAccountFactory(preferred);
|
||||
} catch (IllegalArgumentException e) {
|
||||
factory = Accounts.FACTORY_OFFLINE;
|
||||
if (AccountListPage.RESTRICTED.get()) {
|
||||
showMethodSwitcher = false;
|
||||
factory = Accounts.FACTORY_MICROSOFT;
|
||||
} else {
|
||||
showMethodSwitcher = true;
|
||||
String preferred = config().getPreferredLoginType();
|
||||
try {
|
||||
factory = Accounts.getAccountFactory(preferred);
|
||||
} catch (IllegalArgumentException e) {
|
||||
factory = Accounts.FACTORY_OFFLINE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showMethodSwitcher = false;
|
||||
@@ -259,16 +268,33 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
};
|
||||
|
||||
if (factory instanceof OfflineAccountFactory && username != null && !USERNAME_CHECKER_PATTERN.matcher(username).matches()) {
|
||||
Controllers.confirm(
|
||||
JFXButton btnYes = new JFXButton(i18n("button.ok"));
|
||||
btnYes.getStyleClass().add("dialog-error");
|
||||
btnYes.setOnAction(e -> doCreate.run());
|
||||
btnYes.setDisable(true);
|
||||
|
||||
int countdown = 10;
|
||||
KeyFrame[] keyFrames = new KeyFrame[countdown + 1];
|
||||
for (int i = 0; i < countdown; i++) {
|
||||
keyFrames[i] = new KeyFrame(Duration.seconds(i),
|
||||
new KeyValue(btnYes.textProperty(), i18n("button.ok.countdown", countdown - i)));
|
||||
}
|
||||
keyFrames[countdown] = new KeyFrame(Duration.seconds(countdown),
|
||||
new KeyValue(btnYes.textProperty(), i18n("button.ok")),
|
||||
new KeyValue(btnYes.disableProperty(), false));
|
||||
|
||||
Timeline timeline = new Timeline(keyFrames);
|
||||
Controllers.confirmAction(
|
||||
i18n("account.methods.offline.name.invalid"), i18n("message.warning"),
|
||||
MessageDialogPane.MessageType.WARNING,
|
||||
doCreate,
|
||||
btnYes,
|
||||
() -> {
|
||||
lblErrorMessage.setText(i18n("account.methods.offline.name.invalid.tip"));
|
||||
timeline.stop();
|
||||
body.setDisable(false);
|
||||
spinner.hideSpinner();
|
||||
}
|
||||
);
|
||||
timeline.play();
|
||||
} else {
|
||||
doCreate.run();
|
||||
}
|
||||
|
||||
@@ -203,7 +203,11 @@ public class DecoratorController {
|
||||
case CUSTOM:
|
||||
String backgroundImage = config().getBackgroundImage();
|
||||
if (backgroundImage != null)
|
||||
image = tryLoadImage(Paths.get(backgroundImage));
|
||||
try {
|
||||
image = tryLoadImage(Paths.get(backgroundImage));
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Couldn't load background image", e);
|
||||
}
|
||||
break;
|
||||
case NETWORK:
|
||||
String backgroundImageUrl = config().getBackgroundImageUrl();
|
||||
|
||||
@@ -130,7 +130,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
};
|
||||
}
|
||||
|
||||
private static void download(Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) {
|
||||
public static void download(Profile profile, @Nullable String version, RemoteMod.Version file, String subdirectoryName) {
|
||||
if (version == null) version = profile.getSelectedVersion();
|
||||
|
||||
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version).toPath() : profile.getRepository().getBaseDirectory().toPath();
|
||||
|
||||
@@ -43,12 +43,15 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
|
||||
|
||||
public ModpackTypeSelectionPage(WizardController controller) {
|
||||
this.controller = controller;
|
||||
this.setPadding(new Insets(10));
|
||||
|
||||
Label title = new Label(i18n("modpack.export.as"));
|
||||
title.setPadding(new Insets(8));
|
||||
VBox.setMargin(title, new Insets(8, 0, 8, 12));
|
||||
|
||||
this.getStyleClass().add("jfx-list-view");
|
||||
this.setMaxSize(300, 150);
|
||||
this.setMaxSize(400, 150);
|
||||
this.setSpacing(8);
|
||||
|
||||
this.getChildren().setAll(
|
||||
title,
|
||||
createButton(MODPACK_TYPE_MCBBS, McbbsModpackExportTask.OPTION),
|
||||
@@ -59,6 +62,8 @@ public final class ModpackTypeSelectionPage extends VBox implements WizardPage {
|
||||
|
||||
private JFXButton createButton(String type, ModpackExportInfo.Options option) {
|
||||
JFXButton button = new JFXButton();
|
||||
|
||||
button.getStyleClass().add("card");
|
||||
button.setOnAction(e -> {
|
||||
controller.getSettings().put(MODPACK_TYPE, type);
|
||||
controller.getSettings().put(MODPACK_INFO_OPTION, option);
|
||||
|
||||
@@ -26,12 +26,15 @@ import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@@ -254,6 +257,15 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
FXUtils.installFastTooltip(menuButton, i18n("version.switch"));
|
||||
menuButton.setGraphic(graphic);
|
||||
|
||||
EventHandler<MouseEvent> secondaryClickHandle = event -> {
|
||||
if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) {
|
||||
menuButton.fire();
|
||||
event.consume();
|
||||
}
|
||||
};
|
||||
launchButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle);
|
||||
menuButton.addEventHandler(MouseEvent.MOUSE_CLICKED, secondaryClickHandle);
|
||||
|
||||
launchPane.getChildren().setAll(launchButton, separator, menuButton);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.IntTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.apache.commons.compress.utils.BoundedInputStream;
|
||||
import kala.compress.utils.BoundedInputStream;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -60,10 +60,7 @@ import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent;
|
||||
@@ -156,7 +153,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
FXUtils.runInFX(() -> selectedVersion.set(versionID));
|
||||
}
|
||||
|
||||
public void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) {
|
||||
private void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) {
|
||||
retrySearch = null;
|
||||
setLoading(true);
|
||||
setFailed(false);
|
||||
@@ -171,7 +168,9 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
? version.getProfile().getRepository().getGameVersion(version.getVersion()).orElse("")
|
||||
: "";
|
||||
}
|
||||
}).thenApplyAsync(gameVersion -> repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC)).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
}).thenApplyAsync(
|
||||
gameVersion -> repository.search(gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteModRepository.SortOrder.DESC)
|
||||
).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (searchID != currentSearchID) {
|
||||
return;
|
||||
}
|
||||
@@ -312,7 +311,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
|
||||
StackPane categoryStackPane = new StackPane();
|
||||
JFXComboBox<CategoryIndented> categoryComboBox = new JFXComboBox<>();
|
||||
categoryComboBox.getItems().setAll(new CategoryIndented(0, null));
|
||||
categoryComboBox.getItems().setAll(CategoryIndented.ALL);
|
||||
categoryStackPane.getChildren().setAll(categoryComboBox);
|
||||
categoryComboBox.prefWidthProperty().bind(categoryStackPane.widthProperty());
|
||||
categoryComboBox.getStyleClass().add("fit-width");
|
||||
@@ -320,14 +319,22 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
categoryComboBox.setConverter(stringConverter(getSkinnable()::getLocalizedCategoryIndent));
|
||||
FXUtils.onChangeAndOperate(getSkinnable().downloadSource, downloadSource -> {
|
||||
categoryComboBox.getItems().setAll(CategoryIndented.ALL);
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
|
||||
Task.supplyAsync(() -> getSkinnable().repository.getCategories())
|
||||
.thenAcceptAsync(Schedulers.javafx(), categories -> {
|
||||
if (!Objects.equals(getSkinnable().downloadSource.get(), downloadSource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CategoryIndented> result = new ArrayList<>();
|
||||
result.add(new CategoryIndented(0, null));
|
||||
result.add(CategoryIndented.ALL);
|
||||
for (RemoteModRepository.Category category : Lang.toIterable(categories)) {
|
||||
resolveCategory(category, 0, result);
|
||||
}
|
||||
categoryComboBox.getItems().setAll(result);
|
||||
categoryComboBox.getSelectionModel().select(0);
|
||||
}).start();
|
||||
});
|
||||
|
||||
@@ -344,7 +351,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
IntegerProperty filterID = new SimpleIntegerProperty(this, "Filter ID", 0);
|
||||
IntegerProperty currentFilterID = new SimpleIntegerProperty(this, "Current Filter ID", -1);
|
||||
EventHandler<ActionEvent> searchAction = e -> {
|
||||
if (currentFilterID.get() != filterID.get()) {
|
||||
if (currentFilterID.get() != -1 && currentFilterID.get() != filterID.get()) {
|
||||
control.pageOffset.set(0);
|
||||
}
|
||||
currentFilterID.set(filterID.get());
|
||||
@@ -379,8 +386,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
JFXButton firstPageButton = FXUtils.newBorderButton(i18n("search.first_page"));
|
||||
firstPageButton.setOnAction(event -> {
|
||||
control.pageOffset.set(0);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
});
|
||||
|
||||
JFXButton previousPageButton = FXUtils.newBorderButton(i18n("search.previous_page"));
|
||||
@@ -388,8 +395,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
int pageOffset = control.pageOffset.get();
|
||||
if (pageOffset > 0) {
|
||||
control.pageOffset.set(pageOffset - 1);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -404,16 +411,16 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
int nv = control.pageOffset.get() + 1;
|
||||
if (nv < control.pageCount.get()) {
|
||||
control.pageOffset.set(nv);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
}
|
||||
});
|
||||
|
||||
JFXButton lastPageButton = FXUtils.newBorderButton(i18n("search.last_page"));
|
||||
lastPageButton.setOnAction(event -> {
|
||||
control.pageOffset.set(control.pageCount.get() - 1);
|
||||
changeButton.value.run();
|
||||
searchAction.handle(event);
|
||||
changeButton.value.run();
|
||||
});
|
||||
|
||||
firstPageButton.setDisable(true);
|
||||
@@ -529,6 +536,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
}
|
||||
|
||||
private static class CategoryIndented {
|
||||
private static final CategoryIndented ALL = new CategoryIndented(0, null);
|
||||
|
||||
private final int indent;
|
||||
private final RemoteModRepository.Category category;
|
||||
|
||||
|
||||
@@ -479,14 +479,17 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
|
||||
this.setBody(box);
|
||||
|
||||
JFXButton downloadButton = new JFXButton(type == RemoteModRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install"));
|
||||
downloadButton.getStyleClass().add("dialog-accept");
|
||||
downloadButton.setOnAction(e -> {
|
||||
if (type == RemoteModRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
selfPage.download(version);
|
||||
});
|
||||
JFXButton downloadButton = null;
|
||||
if (selfPage.callback != null) {
|
||||
downloadButton = new JFXButton(type == RemoteModRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install"));
|
||||
downloadButton.getStyleClass().add("dialog-accept");
|
||||
downloadButton.setOnAction(e -> {
|
||||
if (type == RemoteModRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
selfPage.download(version);
|
||||
});
|
||||
}
|
||||
|
||||
JFXButton saveAsButton = new JFXButton(i18n("mods.save_as"));
|
||||
saveAsButton.getStyleClass().add("dialog-accept");
|
||||
@@ -501,7 +504,11 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
cancelButton.getStyleClass().add("dialog-cancel");
|
||||
cancelButton.setOnAction(e -> fireEvent(new DialogCloseEvent()));
|
||||
|
||||
this.setActions(downloadButton, saveAsButton, cancelButton);
|
||||
if (downloadButton == null) {
|
||||
this.setActions(saveAsButton, cancelButton);
|
||||
} else {
|
||||
this.setActions(downloadButton, saveAsButton, cancelButton);
|
||||
}
|
||||
|
||||
this.prefWidthProperty().bind(BindingMapping.of(Controllers.getStage().widthProperty()).map(w -> w.doubleValue() * 0.7));
|
||||
this.prefHeightProperty().bind(BindingMapping.of(Controllers.getStage().heightProperty()).map(w -> w.doubleValue() * 0.7));
|
||||
|
||||
+5
-5
@@ -55,11 +55,11 @@ public final class HMCLLocalizedDownloadListPage extends DownloadListPage {
|
||||
repository = new Repository(type, curseForge, modrinth);
|
||||
|
||||
supportChinese.set(true);
|
||||
downloadSources.get().setAll("mods.curseforge", "mods.modrinth");
|
||||
if (curseForge != null) {
|
||||
downloadSource.set("mods.curseforge");
|
||||
} else if (modrinth != null) {
|
||||
downloadSources.get().setAll("mods.modrinth", "mods.curseforge");
|
||||
if (modrinth != null) {
|
||||
downloadSource.set("mods.modrinth");
|
||||
} else if (curseForge != null) {
|
||||
downloadSource.set("mods.curseforge");
|
||||
} else {
|
||||
throw new AssertionError("Should not be here.");
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public final class HMCLLocalizedDownloadListPage extends DownloadListPage {
|
||||
try {
|
||||
return I18n.getResourceBundle().getString(key);
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.warning("Cannot find key " + key + " in resource bundle", e);
|
||||
LOG.warning("Cannot find key " + key + " in resource bundle");
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
repository instanceof CurseForgeRemoteModRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false),
|
||||
remoteMod,
|
||||
new Profile.ProfileVersion(ModListPageSkin.this.getSkinnable().getProfile(), ModListPageSkin.this.getSkinnable().getVersionId()),
|
||||
null
|
||||
(profile, version, file) -> org.jackhuang.hmcl.ui.download.DownloadPage.download(profile, version, file, "mods")
|
||||
));
|
||||
});
|
||||
button.setDisable(false);
|
||||
|
||||
@@ -140,7 +140,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
}
|
||||
|
||||
void createBackup() {
|
||||
Controllers.taskDialog(new WorldBackupTask(world, backupsDir).setName(i18n("world.backup")).thenApplyAsync(path -> {
|
||||
Controllers.taskDialog(new WorldBackupTask(world, backupsDir).setName(i18n("world.backup.processing")).thenApplyAsync(path -> {
|
||||
Matcher matcher = backupFileNamePattern.matcher(path.getFileName().toString());
|
||||
if (!matcher.matches()) {
|
||||
throw new AssertionError("Wrong backup file name" + path);
|
||||
@@ -176,7 +176,7 @@ public final class WorldBackupsPage extends ListPageBase<WorldBackupsPage.Backup
|
||||
|
||||
@Override
|
||||
protected List<Node> initializeToolbar(WorldBackupsPage skinnable) {
|
||||
return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("world.backup"), SVG.ARCHIVE, skinnable::createBackup));
|
||||
return Arrays.asList(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refresh), createToolbarButton2(i18n("world.backup.create.new_one"), SVG.ARCHIVE, skinnable::createBackup));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -88,6 +88,7 @@ account.login.skip=Log in offline
|
||||
account.login.retry=Retry
|
||||
account.login.refresh=Log in again
|
||||
account.login.refresh.microsoft.hint=You need to log in to your Microsoft account again because the account authorization is invalid.
|
||||
account.login.restricted=Sign in to your Microsoft account to enable this feature
|
||||
account.logout=Logout
|
||||
account.register=Register
|
||||
account.manage=Account List
|
||||
@@ -123,13 +124,12 @@ account.methods.microsoft.snapshot=You are using an unofficial build of HMCL. Pl
|
||||
account.methods.microsoft.snapshot.website=Official Website
|
||||
account.methods.offline=Offline
|
||||
account.methods.offline.name.special_characters=Using only English letters, numbers, and underscores is recommended
|
||||
account.methods.offline.name.invalid.tip=It is recommended to use only English letters, numbers and underscores for the username, and the length should not exceed 16 characters.
|
||||
account.methods.offline.name.invalid=It is recommended to use only English letters, numbers and underscores for the username, and the length should not exceed 16 characters.\n\
|
||||
\n\
|
||||
\ · Legitimate: HuangYu, huang_Yu, Huang_Yu_123;\n\
|
||||
\ · Illegal: Huang Yu, Huang-Yu_%%%, Huang_Yu_hello_world_hello_world.\n\
|
||||
\n\
|
||||
If you believe there is a corresponding mod or plugin on the server side to remove this restriction, you can ignore this warning.
|
||||
Using the illegal username will prevent you from joining most servers and may conflict with some mods, causing the game to crash.
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID is a unique identifier for Minecraft players, and each launcher may generate UUIDs differently. Changing it to the one generated by other launchers allows you to keep your items in your offline account inventory.\n\
|
||||
\n\
|
||||
@@ -180,6 +180,7 @@ button.install=Install
|
||||
button.export=Export
|
||||
button.no=No
|
||||
button.ok=OK
|
||||
button.ok.countdown=OK (%d)
|
||||
button.refresh=Refresh
|
||||
button.remove=Remove
|
||||
button.remove.confirm=Are you sure you want to permanently remove it? This action cannot be undone!
|
||||
@@ -328,7 +329,7 @@ download=Download
|
||||
download.hint=Install games and modpacks or download mods, resource packs, and worlds.
|
||||
download.code.404=File "%s" not found on the remote server.
|
||||
download.content=Addons
|
||||
download.curseforge.unavailable=HMCL Nightly channel build does not support access to CurseForge. Please use Release or Beta channel builds to download.
|
||||
download.curseforge.unavailable=This HMCL build does not support access to CurseForge. Please use the official build to access CurseForge.
|
||||
download.existing=The file cannot be saved because it already exists. You can click "Save As" to save the file elsewhere.
|
||||
download.external_link=Visit Download Website
|
||||
download.failed=Failed to download "%1$s", response code: %2$d.
|
||||
@@ -680,6 +681,7 @@ install.failed.version_mismatch=This loader requires the game version %s, but th
|
||||
install.installer.change_version=%s Incompatible
|
||||
install.installer.choose=Choose Your %s Version
|
||||
install.installer.depend=Requires %s
|
||||
install.installer.do_not_install=Do not install
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=Warning: Fabric API is a mod and will be installed into the mod directory of the game instance. Please do not change the working directory of the game, or the Fabric API will not function. If you do want to change the directory, you should reinstall it.
|
||||
@@ -694,7 +696,7 @@ install.installer.install_offline.tooltip=We support using the local (Neo)Forge/
|
||||
install.installer.install_online=Online Install
|
||||
install.installer.install_online.tooltip=We currently support Forge, NeoForge, OptiFine, Fabric, Quilt, and LiteLoader.
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=Do not install
|
||||
install.installer.not_installed=Not installed
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.quilt=Quilt
|
||||
install.installer.quilt-api=QSL/QFAPI
|
||||
@@ -1047,11 +1049,13 @@ web.view_in_browser=View in browser
|
||||
|
||||
world=Worlds
|
||||
world.add=Add World
|
||||
world.backup=Backup World
|
||||
world.backup=World Backup
|
||||
world.backup.create.new_one=Create New Backup
|
||||
world.backup.create.failed=Failed to create backup.\n%s
|
||||
world.backup.create.locked=The world is currently in use. Please close the game and try again.
|
||||
world.backup.create.success=Successfully created a new backup: %s
|
||||
world.backup.delete=Delete this backup
|
||||
world.backup.processing=Backing up ...
|
||||
world.backup.reveal=Show in folder
|
||||
world.backup.title=World [%s] - Backups
|
||||
world.datapack=Manage Datapacks
|
||||
|
||||
@@ -88,6 +88,7 @@ account.login.skip=Iniciar sesión sin conexión
|
||||
account.login.retry=Reintentar
|
||||
account.login.refresh=Iniciar sesión de nuevo
|
||||
account.login.refresh.microsoft.hint=Debe volver a iniciar sesión en su cuenta Microsoft porque la autorización de la cuenta no es válida.
|
||||
account.login.restricted=Iniciar sesión en su cuenta Microsoft para activar esta función
|
||||
account.logout=Salir
|
||||
account.register=Registrarse
|
||||
account.manage=Lista de cuentas
|
||||
@@ -328,7 +329,7 @@ download=Descargar
|
||||
download.hint=Instalar juegos y modpacks o descargar mods, paquetes de recursos y mundos.
|
||||
download.code.404=Archivo no encontrado en el servidor remoto: %s
|
||||
download.content=Complementos
|
||||
download.curseforge.unavailable=La versión Nightly de HMCL no admite el acceso a CurseForge. Utilice las versiones Release o Beta para descargarlas.
|
||||
download.curseforge.unavailable=Esta versión de HMCL no permite acceder a CurseForge. Utilice la versión oficial para acceder a CurseForge.
|
||||
download.existing=El archivo no se puede guardar porque ya existe. Puedes hacer clic en «Guardar como» para guardar el archivo en otro lugar.
|
||||
download.external_link=Abrir sitio web
|
||||
download.failed=Falló la descarga de %1$s, código de respuesta: %2$d.
|
||||
@@ -684,6 +685,7 @@ install.failed.version_mismatch=Este cargador requiere la versión del juego %s,
|
||||
install.installer.change_version=%s Incompatible
|
||||
install.installer.choose=Elija su versión %s
|
||||
install.installer.depend=Requiere %s
|
||||
install.installer.do_not_install=No instalar
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=Atención: Fabric API es un mod, y se instalará en el directorio de mods de la instancia del juego. Por favor, no cambies el directorio de trabajo del juego, o el Fabric API no funcionará. Si quieres cambiar esta configuración, debes reinstalarlo.
|
||||
@@ -698,7 +700,7 @@ install.installer.install_offline.tooltip=Apoyamos el uso del instalador local d
|
||||
install.installer.install_online=Instalación en línea
|
||||
install.installer.install_online.tooltip=Actualmente soportamos Forge, NeoForge, OptiFine, Fabric, Quilt y LiteLoader.
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=No instalar
|
||||
install.installer.not_installed=No está instalado
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.quilt=Quilt
|
||||
install.installer.quilt-api=QSL/QFAPI
|
||||
@@ -1052,10 +1054,12 @@ web.view_in_browser=Ver en navegador
|
||||
world=Mundos
|
||||
world.add=Añadir mundo
|
||||
world.backup=Copia de seguridad
|
||||
world.backup.create.new_one=Crear uno nuevo
|
||||
world.backup.create.failed=No se ha podido crear la copia de seguridad.\n%s
|
||||
world.backup.create.locked=El mundo está actualmente en uso. Por favor, cierra el juego e inténtalo de nuevo.
|
||||
world.backup.create.success=Creada con éxito una nueva copia de seguridad: %s
|
||||
world.backup.delete=Eliminar esta copia de seguridad
|
||||
world.backup.processing=Creando nueva copia de seguridad ...
|
||||
world.backup.reveal=Abrir el directorio
|
||||
world.backup.title=Mundo [%s] - Copias de seguridad
|
||||
world.datapack=Gestionar paquetes de datos
|
||||
|
||||
@@ -91,6 +91,7 @@ account.login.skip=Войти в режиме офлайн
|
||||
account.login.retry=Повторить снова
|
||||
account.login.refresh=Войти снова
|
||||
account.login.refresh.microsoft.hint=Вам необходимо снова войти в аккаунт Microsoft, поскольку авторизация аккаунта недействительна.
|
||||
account.login.restricted=Войти в свою аккаунт Microsoft, чтобы включить эту функцию
|
||||
account.logout=Выйти
|
||||
account.register=Зарегестрироваться
|
||||
account.manage=Список аккаунтов
|
||||
@@ -329,7 +330,7 @@ download=Скачать
|
||||
download.hint=Установить игры и модпаки или скачать моды, пакеты ресурсов и миры.
|
||||
download.code.404=Файл «%s» не найден на удаленном сервере.
|
||||
download.content=Аддоны
|
||||
download.curseforge.unavailable=Альфа-версия HMCL не поддерживает доступ к CurseForge. Для скачивания используйте версии релиза или бета.
|
||||
download.curseforge.unavailable=Эта сборка HMCL не поддерживает доступ к CurseForge. Пожалуйста, используйте официальную сборку для доступа к CurseForge.
|
||||
download.existing=Файл существует и по этому не может быть сохранён. Можно использовать «Сохранить как», чтобы сохранить файл в другом месте.
|
||||
download.external_link=Открыть сайт
|
||||
download.failed=Не удалось скачать «%1$s», код ответа: %2$d.
|
||||
@@ -680,6 +681,7 @@ install.failed.version_mismatch=Этот загрузчик требует ве
|
||||
install.installer.change_version=%s (Несовместимо)
|
||||
install.installer.choose=Выберите версию %s
|
||||
install.installer.depend=Требуется %s
|
||||
install.installer.do_not_install=Не устанавливать
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=Предупреждение: Fabric API является модом и будет установлен в папку mod в сборке игры. Пожалуйста, не меняйте рабочую папку сборки, иначе Fabric API не будет работать. Если вы все же хотите изменить папку, вам следует переустановить сборку.
|
||||
@@ -694,7 +696,7 @@ install.installer.install_offline.tooltip=Поддерживаются уста
|
||||
install.installer.install_online=Установить с интернета
|
||||
install.installer.install_online.tooltip=Поддерживаются Forge, NeoForge, OptiFine, Fabric, Quilt и LiteLoader.
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=Не устанавливать
|
||||
install.installer.not_installed=Не установлено
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.quilt=Quilt
|
||||
install.installer.quilt-api=QSL/QFAPI
|
||||
@@ -1051,10 +1053,12 @@ web.view_in_browser=Смотреть в браузере
|
||||
world=Миры
|
||||
world.add=Добавить мир
|
||||
world.backup=Резервный мир
|
||||
world.backup.create.new_one=Создать новый
|
||||
world.backup.create.failed=Не удалось создать резервную копию.\n%s
|
||||
world.backup.create.locked=В настоящее время мир находится в эксплуатации. Закройте игру и попробуйте снова.
|
||||
world.backup.create.success=Успешно создано новое резервное копирование: %s
|
||||
world.backup.delete=Удалить эту резервную копию
|
||||
world.backup.processing=Создание новой резервной копии ...
|
||||
world.backup.reveal=Показать в диспетчере файлов
|
||||
world.backup.title=Мир [%s] - Резервные копии
|
||||
world.datapack=Управлять наборами данных
|
||||
|
||||
@@ -91,6 +91,7 @@ account.login.skip=跳過重新整理帳戶
|
||||
account.login.retry=再次重新整理帳戶
|
||||
account.login.refresh=重新登入
|
||||
account.login.refresh.microsoft.hint=由於帳戶授權失效,你需要重新加入 Microsoft 帳戶
|
||||
account.login.restricted=登入微軟帳戶以啟用此功能
|
||||
account.logout=登出
|
||||
account.register=註冊
|
||||
account.manage=帳戶清單
|
||||
@@ -129,13 +130,12 @@ account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,請
|
||||
account.methods.microsoft.snapshot.website=官方網站
|
||||
account.methods.offline=離線模式
|
||||
account.methods.offline.name.special_characters=建議使用英文字母、數字以及底線命名
|
||||
account.methods.offline.name.invalid.tip=遊戲使用者名稱建議僅使用英文字母、數字及底線,且長度不超過 16 個字元。
|
||||
account.methods.offline.name.invalid=遊戲使用者名稱建議僅使用英文字母、數字及底線,且長度不超過 16 個字元。\n\
|
||||
\n\
|
||||
\ · 一些有效的使用者名稱:HuangYu、huang_Yu、Huang_Yu_123;\n\
|
||||
\ · 一些無效的使用者名稱:黃魚,Huang Yu、Huang-Yu_%%%、Huang_Yu_hello_world_hello_world。\n\
|
||||
\n\
|
||||
如果你相信伺服器端有相應的模組或插件來解除此限制,你可以忽略本警告。
|
||||
使用非法使用者名稱會導致你無法加入大部分伺服器,並可能與部分模組衝突而使遊戲崩潰。
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一標識符,每個啟動器生成 UUID 的方式可能不同。\n透過修改 UUID 選項至原啟動器所生成的 UUID,你可以保證在切換啟動器後,遊戲還能將你的遊戲角色識別為給定 UUID 所對應的角色,從而保留原來角色的背包物品。\nUUID 選項為進階選項,除非你知道你在做什麼,否則你不需要調整該選項。
|
||||
account.methods.offline.uuid.malformed=格式錯誤
|
||||
@@ -183,6 +183,7 @@ button.install=安裝
|
||||
button.export=匯出
|
||||
button.no=否
|
||||
button.ok=確定
|
||||
button.ok.countdown=確定 (%d)
|
||||
button.refresh=重新整理
|
||||
button.remove=刪除
|
||||
button.remove.confirm=你確認要刪除嗎?該操作無法復原!
|
||||
@@ -330,7 +331,7 @@ download=下載
|
||||
download.hint=安裝遊戲和模組包或下載模組、資源包和地圖
|
||||
download.code.404=遠端伺服器沒有需要下載的檔案:%s
|
||||
download.content=遊戲內容
|
||||
download.curseforge.unavailable=HMCL 預覽版暫不支援訪問 CurseForge,請使用穩定版或開發版進行下載。
|
||||
download.curseforge.unavailable=這個 HMCL 版本不支援訪問 CurseForge。請使用官方版本進行下載。
|
||||
download.existing=檔案已存在,無法儲存。你可以將檔案儲存至其他地方。
|
||||
download.external_link=打開下載網站
|
||||
download.failed=下載失敗:%1$s\n錯誤碼:%2$d
|
||||
@@ -496,6 +497,7 @@ install.failed.version_mismatch=該載入器需要的遊戲版本為 %s,但實
|
||||
install.installer.change_version=%s 與目前遊戲不相容,請更換版本
|
||||
install.installer.choose=選取 %s 版本
|
||||
install.installer.depend=需要先安裝 %s
|
||||
install.installer.do_not_install=不安裝
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是模組,將會被安裝到新遊戲的模組目錄,請你在安裝遊戲後不要修改目前遊戲的「執行路徑」設定,如果你在之後修改了相關設定,則需要重新安裝 Fabric API。
|
||||
@@ -510,7 +512,7 @@ install.installer.install_offline.tooltip=支援匯入已經下載好的 (Neo)Fo
|
||||
install.installer.install_online=線上安裝
|
||||
install.installer.install_online.tooltip=支援安裝 Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=不安裝
|
||||
install.installer.not_installed=未安裝
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.quilt=Quilt
|
||||
install.installer.quilt-api=QSL/QFAPI
|
||||
@@ -670,7 +672,7 @@ modpack.download=下載模組包
|
||||
modpack.download.title=模組包下載 - %1s
|
||||
modpack.enter_name=給遊戲取個你喜歡的名稱
|
||||
modpack.export=匯出模組包
|
||||
modpack.export.as=請選取模組包類型。若你無法決定,請選取 MCBBS 類型。
|
||||
modpack.export.as=請選取模組包類型
|
||||
modpack.file_api=模組包下載連結前綴
|
||||
modpack.files.blueprints=BuildCraft 藍圖
|
||||
modpack.files.config=模組設定檔案
|
||||
@@ -703,7 +705,7 @@ modpack.type.curse=Curse
|
||||
modpack.type.curse.error=無法完成該模組包所需的相依元件下載,請多次重試或設定代理……
|
||||
modpack.type.curse.not_found=部分必需檔案已經從網路中被刪除並且再也無法下載,請嘗試該模組包的最新版本或者安裝其他模組包。
|
||||
modpack.type.manual.warning=該模組包由發佈者手動打包,其中可能已經包含啟動器,建議嘗試解壓後使用其內建的啟動器執行遊戲。\nHMCL 可以嘗試匯入該模組包,但不保證可用性,是否繼續?
|
||||
modpack.type.mcbbs=MCBBS
|
||||
modpack.type.mcbbs=MCBBS 模組包
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 匯入
|
||||
modpack.type.modrinth=Modrinth
|
||||
modpack.type.multimc=MultiMC
|
||||
@@ -853,11 +855,13 @@ web.view_in_browser=在瀏覽器中查看
|
||||
|
||||
world=世界
|
||||
world.add=加入世界
|
||||
world.backup=備份世界
|
||||
world.backup=世界備份
|
||||
world.backup.create.new_one=建立新備份
|
||||
world.backup.create.failed=創建備份失敗。\n%s
|
||||
world.backup.create.locked=該世界正在使用中,請關閉遊戲後重試。
|
||||
world.backup.create.success=成功創建新備份:%s
|
||||
world.backup.delete=删除此備份
|
||||
world.backup.processing=正在備份中……
|
||||
world.backup.title=世界 [%s] - 備份
|
||||
world.datapack=管理資料包
|
||||
world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支援資料包
|
||||
|
||||
@@ -92,6 +92,7 @@ account.login.skip=跳过账户刷新
|
||||
account.login.retry=再次刷新账户
|
||||
account.login.refresh=重新登录
|
||||
account.login.refresh.microsoft.hint=由于账户授权失效,你需要重新添加微软账户。
|
||||
account.login.restricted=登录微软账户以启用此功能
|
||||
account.logout=登出
|
||||
account.register=注册
|
||||
account.manage=账户列表
|
||||
@@ -137,14 +138,12 @@ account.methods.microsoft.snapshot=你正在使用第三方提供的 HMCL,请
|
||||
account.methods.microsoft.snapshot.website=官方网站
|
||||
account.methods.offline=离线模式
|
||||
account.methods.offline.name.special_characters=建议使用英文字符、数字以及下划线命名
|
||||
account.methods.offline.name.invalid.tip=游戏用户名建议仅使用英文字母、数字及下划线,且长度不超过 16 个字符。
|
||||
account.methods.offline.name.invalid=游戏用户名建议仅使用英文字母、数字及下划线,且长度不超过 16 个字符。\n\
|
||||
\n\
|
||||
\ · 一些合法用户名:HuangYu、huang_Yu、Huang_Yu_123;\n\
|
||||
\ · 一些非法用户名:黄鱼、Huang Yu、Huang-Yu_%%%、Huang_Yu_hello_world_hello_world。\n\
|
||||
\n\
|
||||
如果你相信服务端有相应模组或插件解除此限制,可以忽略本警告。\n\
|
||||
如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
使用非法用户名会导致你无法加入大部分服务器,并可能与部分模组冲突而使游戏崩溃。
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 玩家的唯一标识符,每个启动器生成 UUID 的方式可能不同。通过将 UUID 修改为原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原角色的背包物品。UUID 选项为高级选项,除非你知道你在做什么,否则你不需要调整该选项。
|
||||
account.methods.offline.uuid.malformed=格式错误
|
||||
@@ -192,6 +191,7 @@ button.install=安装
|
||||
button.export=导出
|
||||
button.no=否
|
||||
button.ok=确定
|
||||
button.ok.countdown=确定 (%d)
|
||||
button.refresh=刷新
|
||||
button.remove=删除
|
||||
button.remove.confirm=你确定要删除吗?此操作无法撤销!
|
||||
@@ -339,7 +339,7 @@ download=下载
|
||||
download.hint=安装游戏和整合包或下载模组、资源包和地图
|
||||
download.code.404=远程服务器不包含需要下载的文件: %s\n你可以点击右上角帮助按钮进行求助。
|
||||
download.content=游戏内容
|
||||
download.curseforge.unavailable=HMCL 预览版暂不支持访问 CurseForge,请使用稳定版或开发版进行下载。
|
||||
download.curseforge.unavailable=此 HMCL 版本不支持访问 CurseForge。请使用官方版本进行下载。
|
||||
download.existing=文件已存在,无法保存。你可以将文件保存至其他地方。
|
||||
download.external_link=打开下载网站
|
||||
download.failed=下载失败: %1$s,\n错误码:%2$d\n你可以点击右上角帮助按钮进行求助。
|
||||
@@ -507,6 +507,7 @@ install.failed.version_mismatch=该组件需要的游戏版本为 %s,但实际
|
||||
install.installer.change_version=%s 与当前游戏不兼容,请更换版本
|
||||
install.installer.choose=选择 %s 版本
|
||||
install.installer.depend=需要先安装 %s
|
||||
install.installer.do_not_install=不安装
|
||||
install.installer.fabric=Fabric
|
||||
install.installer.fabric-api=Fabric API
|
||||
install.installer.fabric-api.warning=警告:Fabric API 是一个模组,将会被安装到新游戏的模组文件夹,请你在安装游戏后不要修改当前游戏的“运行路径”设置,如果你在之后修改了相关设置,则需要重新安装 Fabric API。
|
||||
@@ -521,7 +522,7 @@ install.installer.install_offline.tooltip=支持导入已经下载好的 (Neo)Fo
|
||||
install.installer.install_online=在线安装
|
||||
install.installer.install_online.tooltip=支持安装 Forge、NeoForge、Fabric、Quilt、LiteLoader 和 OptiFine
|
||||
install.installer.liteloader=LiteLoader
|
||||
install.installer.not_installed=不安装
|
||||
install.installer.not_installed=未安装
|
||||
install.installer.optifine=OptiFine
|
||||
install.installer.quilt=Quilt
|
||||
install.installer.quilt-api=QSL/QFAPI
|
||||
@@ -681,7 +682,7 @@ modpack.download=下载整合包
|
||||
modpack.download.title=整合包下载 - %1s
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.export=导出整合包
|
||||
modpack.export.as=请选择整合包类型 (若无法决定,请选择“我的世界中文论坛整合包标准”)
|
||||
modpack.export.as=请选择整合包类型
|
||||
modpack.file_api=整合包下载链接前缀
|
||||
modpack.files.blueprints=BuildCraft 蓝图
|
||||
modpack.files.config=模组配置文件
|
||||
@@ -714,7 +715,7 @@ modpack.type.curse=Curse
|
||||
modpack.type.curse.error=未能完成该整合包所需的依赖下载,请再次尝试或设置代理。\n你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。\n如遇到问题,你可以点击右上角帮助按钮进行求助。
|
||||
modpack.type.manual.warning=该整合包由发布者手动打包,其中可能已经包含启动器,建议尝试解压后使用其自带的启动器运行游戏。\n如遇到问题,你可以点击右上角帮助按钮进行求助。\nHMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
|
||||
modpack.type.mcbbs=我的世界中文论坛整合包标准
|
||||
modpack.type.mcbbs=MCBBS 整合包 (推荐)
|
||||
modpack.type.mcbbs.export=可以被 Hello Minecraft! Launcher 导入
|
||||
modpack.type.modrinth=Modrinth
|
||||
modpack.type.multimc=MultiMC
|
||||
@@ -864,11 +865,13 @@ web.view_in_browser=在浏览器中查看
|
||||
|
||||
world=世界
|
||||
world.add=添加世界
|
||||
world.backup=备份世界
|
||||
world.backup=世界备份
|
||||
world.backup.create.new_one=创建新备份
|
||||
world.backup.create.failed=创建备份失败。\n%s
|
||||
world.backup.create.locked=该世界正在使用中,请关闭游戏后重试。
|
||||
world.backup.create.success=成功创建新备份:%s
|
||||
world.backup.delete=删除此备份
|
||||
world.backup.processing=正在备份中……
|
||||
world.backup.title=世界 [%s] - 备份
|
||||
world.datapack=管理数据包
|
||||
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
|
||||
|
||||
@@ -3979,6 +3979,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.oshi:oshi-core:6.6.5": {
|
||||
"name": "com.github.oshi:oshi-core:6.8.0",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "com/github/oshi/oshi-core/6.8.0/oshi-core-6.8.0.jar",
|
||||
"url": "https://repo1.maven.org/maven2/com/github/oshi/oshi-core/6.8.0/oshi-core-6.8.0.jar",
|
||||
"sha1": "004d12fac84286063e6842688610b8b2d43924a4",
|
||||
"size": 1011309
|
||||
}
|
||||
}
|
||||
},
|
||||
"net.java.jinput:jinput-platform:2.0.5:natives": null,
|
||||
"com.mojang:text2speech:1.10.3:natives": null,
|
||||
"com.mojang:text2speech:1.11.3:natives": null,
|
||||
@@ -3987,26 +3998,26 @@
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:x64",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:x64",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x64.jar",
|
||||
"sha1": "629fca32417d6ec489cef8b2cbd0827131ec6801",
|
||||
"size": 27174940
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar",
|
||||
"sha1": "b2552fcc8c98e95b4559c39f2ff87cdb3aaaa513",
|
||||
"size": 27971214
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"windows-x86": {
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:x86",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:x86",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x86.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-x86.jar",
|
||||
"sha1": "d25e0cdf5c5eb182acc9b93f700e0e6d8de36283",
|
||||
"size": 22528549
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar",
|
||||
"sha1": "6fd0fd7da88ca6636f735e1e0f6feb7f62f59715",
|
||||
"size": 23311299
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4389,13 +4400,13 @@
|
||||
"com.mojang:text2speech:1.12.4:natives": null,
|
||||
"com.mojang:text2speech:1.13.9:natives-windows": null,
|
||||
"mesa-loader": {
|
||||
"name": "org.glavo:mesa-loader-windows:0.3.0:arm64",
|
||||
"name": "org.glavo:mesa-loader-windows:25.0.3:arm64",
|
||||
"downloads": {
|
||||
"artifact": {
|
||||
"path": "org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-arm64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/0.3.0/mesa-loader-windows-0.3.0-arm64.jar",
|
||||
"sha1": "1986490c6fbe950e64018c2fb62c8ecf77a247ed",
|
||||
"size": 24082103
|
||||
"path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar",
|
||||
"url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar",
|
||||
"sha1": "40ca5de3351bf48b7b9efa5d2598d5b6b4d1ab8e",
|
||||
"size": 24905270
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,20 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val kalaCompressVersion = "1.27.1-1"
|
||||
|
||||
api("org.glavo.kala:kala-compress-archivers-zip:$kalaCompressVersion")
|
||||
api("org.glavo.kala:kala-compress-archivers-tar:$kalaCompressVersion")
|
||||
api("org.glavo:simple-png-javafx:0.3.0")
|
||||
api("com.google.code.gson:gson:2.12.1")
|
||||
api("com.google.code.gson:gson:2.13.0")
|
||||
api("com.moandjiezana.toml:toml4j:0.7.2")
|
||||
api("org.tukaani:xz:1.10")
|
||||
api("org.hildan.fxgson:fx-gson:5.0.0")
|
||||
api("org.jenkins-ci:constant-pool-scanner:1.2")
|
||||
api("com.github.steveice10:opennbt:1.5")
|
||||
api("org.nanohttpd:nanohttpd:2.3.1")
|
||||
api("org.apache.commons:commons-compress:1.25.0")
|
||||
api("org.jsoup:jsoup:1.18.3")
|
||||
api("org.jsoup:jsoup:1.19.1")
|
||||
api("org.glavo:chardet:2.5.0")
|
||||
compileOnlyApi("org.jetbrains:annotations:26.0.1")
|
||||
|
||||
if (JavaVersion.current().isJava8) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jackhuang.hmcl.task.GetTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.download.UnsupportedInstallationException.FABRIC_NOT_COMPATIBLE_WITH_FORGE;
|
||||
@@ -83,8 +84,12 @@ public final class FabricInstallTask extends Task<Version> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion()));
|
||||
public void execute() throws IOException {
|
||||
FabricInfo fabricInfo = JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class);
|
||||
if (fabricInfo == null)
|
||||
throw new IOException("Fabric metadata is invalid");
|
||||
|
||||
setResult(getPatch(fabricInfo, remote.getGameVersion(), remote.getSelfVersion()));
|
||||
|
||||
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true));
|
||||
}
|
||||
|
||||
+27
-54
@@ -18,21 +18,16 @@
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
@@ -47,66 +42,44 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
private static final class LiteLoaderBMCLVersion {
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
private final LiteLoaderVersion build;
|
||||
private final String version;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = "https://bmclapi2.bangbang93.com/liteloader/download?version=" + version;
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
public LiteLoaderBMCLVersion(LiteLoaderVersion build, String version) {
|
||||
this.build = build;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
lock.writeLock().lock();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return HttpRequest.GET(
|
||||
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/list"), Pair.pair("mcversion", gameVersion)
|
||||
)
|
||||
.getJsonAsync(LiteLoaderBMCLVersion.class)
|
||||
.thenAccept(v -> {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
}
|
||||
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
||||
gameVersion, v.version, RemoteVersion.Type.UNCATEGORIZED,
|
||||
Collections.singletonList(NetworkUtils.withQuery(
|
||||
downloadProvider.injectURL("https://bmclapi2.bangbang93.com/liteloader/download"),
|
||||
Collections.singletonMap("version", v.version)
|
||||
)),
|
||||
v.build.getTweakClass(), v.build.getLibraries()
|
||||
));
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
private static String getLatestSnapshotVersion(String repo) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(repo + "maven-metadata.xml");
|
||||
Element r = doc.getDocumentElement();
|
||||
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
|
||||
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
|
||||
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
|
||||
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -30,6 +30,7 @@ import java.util.List;
|
||||
public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
private final String tweakClass;
|
||||
private final Collection<Library> libraries;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -37,8 +38,8 @@ public class LiteLoaderRemoteVersion extends RemoteVersion {
|
||||
* @param selfVersion the version string of the remote version.
|
||||
* @param urls the installer or universal jar original URL.
|
||||
*/
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, List<String> urls, String tweakClass, Collection<Library> libraries) {
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, urls);
|
||||
LiteLoaderRemoteVersion(String gameVersion, String selfVersion, Type type, List<String> urls, String tweakClass, Collection<Library> libraries) {
|
||||
super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, type, urls);
|
||||
|
||||
this.tweakClass = tweakClass;
|
||||
this.libraries = libraries;
|
||||
|
||||
+63
-53
@@ -18,21 +18,20 @@
|
||||
package org.jackhuang.hmcl.download.liteloader;
|
||||
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVersion> {
|
||||
@@ -45,52 +44,39 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
|
||||
@Override
|
||||
public boolean hasType() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) {
|
||||
if (branch == null || repository == null)
|
||||
return;
|
||||
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
String version = v.getVersion();
|
||||
String url = repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile();
|
||||
if (snapshot) {
|
||||
try {
|
||||
version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/"));
|
||||
url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar";
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
versions.put(key, new LiteLoaderRemoteVersion(gameVersion,
|
||||
version, Collections.singletonList(url),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
public static final String LITELOADER_LIST = "https://dl.liteloader.com/versions/versions.json";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||
return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class)
|
||||
.thenAcceptAsync(root -> {
|
||||
LiteLoaderGameVersions versions = root.getVersions().get(gameVersion);
|
||||
if (versions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LiteLoaderRemoteVersion snapshot = null;
|
||||
if (versions.getSnapshots() != null) {
|
||||
try {
|
||||
snapshot = loadSnapshotVersion(gameVersion, versions.getSnapshots().getLiteLoader().get("latest"));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
versions.clear();
|
||||
this.versions.clear();
|
||||
|
||||
for (Map.Entry<String, LiteLoaderGameVersions> entry : root.getVersions().entrySet()) {
|
||||
String gameVersion = entry.getKey();
|
||||
LiteLoaderGameVersions liteLoader = entry.getValue();
|
||||
if (versions.getRepoitory() != null && versions.getArtifacts() != null) {
|
||||
loadArtifactVersion(gameVersion, versions.getRepoitory(), versions.getArtifacts());
|
||||
}
|
||||
|
||||
String gg = VersionNumber.normalize(gameVersion);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false);
|
||||
doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true);
|
||||
if (snapshot != null) {
|
||||
this.versions.put(gameVersion, snapshot);
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
@@ -98,16 +84,40 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
|
||||
});
|
||||
}
|
||||
|
||||
public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json";
|
||||
@Override
|
||||
public CompletableFuture<?> refreshAsync() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static String getLatestSnapshotVersion(String repo) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(repo + "maven-metadata.xml");
|
||||
Element r = doc.getDocumentElement();
|
||||
Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0);
|
||||
Node timestamp = snapshot.getElementsByTagName("timestamp").item(0);
|
||||
Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0);
|
||||
return timestamp.getTextContent() + "-" + buildNumber.getTextContent();
|
||||
private void loadArtifactVersion(String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch) {
|
||||
for (Map.Entry<String, LiteLoaderVersion> entry : branch.getLiteLoader().entrySet()) {
|
||||
String branchName = entry.getKey();
|
||||
LiteLoaderVersion v = entry.getValue();
|
||||
if ("latest".equals(branchName))
|
||||
continue;
|
||||
|
||||
versions.put(gameVersion, new LiteLoaderRemoteVersion(
|
||||
gameVersion, v.getVersion(), RemoteVersion.Type.RELEASE,
|
||||
Collections.singletonList(repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile()),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/HMCL-dev/HMCL/issues/3147: Some LiteLoader artifacts aren't published on http://dl.liteloader.com/repo
|
||||
private static final String SNAPSHOT_METADATA = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/maven-metadata.xml";
|
||||
private static final String SNAPSHOT_FILE = "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/%s-SNAPSHOT/liteloader-%s-%s-%s-release.jar";
|
||||
|
||||
private LiteLoaderRemoteVersion loadSnapshotVersion(String gameVersion, LiteLoaderVersion v) throws IOException {
|
||||
String root = HttpRequest.GET(String.format(SNAPSHOT_METADATA, gameVersion)).getString();
|
||||
Document document = Jsoup.parseBodyFragment(root);
|
||||
String timestamp = Objects.requireNonNull(document.select("timestamp"), "timestamp").text();
|
||||
String buildNumber = Objects.requireNonNull(document.select("buildNumber"), "buildNumber").text();
|
||||
|
||||
return new LiteLoaderRemoteVersion(
|
||||
gameVersion, timestamp + "-" + buildNumber, RemoteVersion.Type.SNAPSHOT,
|
||||
Collections.singletonList(String.format(SNAPSHOT_FILE, gameVersion, gameVersion, timestamp, buildNumber)),
|
||||
v.getTweakClass(), v.getLibraries()
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -10,6 +10,7 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.wrap;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemoteVersion> {
|
||||
private final DownloadProvider downloadProvider;
|
||||
@@ -56,8 +57,20 @@ public final class NeoForgeOfficialVersionList extends VersionList<NeoForgeRemot
|
||||
}
|
||||
|
||||
for (String version : results[1].versions) {
|
||||
int si1 = version.indexOf('.'), si2 = version.indexOf('.', version.indexOf('.') + 1);
|
||||
String mcVersion = "1." + version.substring(0, Integer.parseInt(version.substring(si1 + 1, si2)) == 0 ? si1 : si2);
|
||||
String mcVersion;
|
||||
|
||||
try {
|
||||
int si1 = version.indexOf('.'), si2 = version.indexOf('.', version.indexOf('.') + 1);
|
||||
int majorVersion = Integer.parseInt(version.substring(0, si1));
|
||||
if (majorVersion == 0) { // Snapshot version.
|
||||
mcVersion = version.substring(si1 + 1, si2);
|
||||
} else {
|
||||
mcVersion = "1." + version.substring(0, Integer.parseInt(version.substring(si1 + 1, si2)) == 0 ? si1 : si2);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
LOG.warning(String.format("Cannot parse NeoForge version %s for cracking its mc version.", version), e);
|
||||
continue;
|
||||
}
|
||||
|
||||
versions.put(mcVersion, new NeoForgeRemoteVersion(
|
||||
mcVersion, NeoForgeRemoteVersion.normalize(version),
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.java;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import org.jackhuang.hmcl.util.KeyValuePairProperties;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
|
||||
@@ -189,30 +189,57 @@ public class DefaultLauncher extends Launcher {
|
||||
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS)
|
||||
res.addDefault("-Duser.home=", options.getGameDir().getParent());
|
||||
|
||||
final int javaVersion = options.getJava().getParsedVersion();
|
||||
final boolean is64bit = options.getJava().getBits() == Bits.BIT_64;
|
||||
|
||||
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
|
||||
res.addUnstableDefault("UnlockDiagnosticVMOptions", true);
|
||||
|
||||
// Using G1GC with its settings by default
|
||||
if (options.getJava().getParsedVersion() >= 8
|
||||
if (javaVersion >= 8
|
||||
&& res.noneMatch(arg -> "-XX:-UseG1GC".equals(arg) || (arg.startsWith("-XX:+Use") && arg.endsWith("GC")))) {
|
||||
res.addUnstableDefault("UnlockExperimentalVMOptions", true);
|
||||
res.addUnstableDefault("UseG1GC", true);
|
||||
res.addUnstableDefault("G1MixedGCCountTarget", "5");
|
||||
res.addUnstableDefault("G1NewSizePercent", "20");
|
||||
res.addUnstableDefault("G1ReservePercent", "20");
|
||||
res.addUnstableDefault("MaxGCPauseMillis", "50");
|
||||
res.addUnstableDefault("G1HeapRegionSize", "32m");
|
||||
}
|
||||
|
||||
res.addUnstableDefault("UseAdaptiveSizePolicy", false);
|
||||
res.addUnstableDefault("OmitStackTraceInFastThrow", false);
|
||||
res.addUnstableDefault("DontCompileHugeMethods", false);
|
||||
|
||||
// JIT Options
|
||||
if (javaVersion <= 8) {
|
||||
res.addUnstableDefault("MaxInlineLevel", "15");
|
||||
}
|
||||
if (is64bit && OperatingSystem.TOTAL_MEMORY > 4 * 1024) {
|
||||
res.addUnstableDefault("DontCompileHugeMethods", false);
|
||||
res.addUnstableDefault("MaxNodeLimit", "240000");
|
||||
res.addUnstableDefault("NodeLimitFudgeFactor", "8000");
|
||||
res.addUnstableDefault("TieredCompileTaskTimeout", "10000");
|
||||
res.addUnstableDefault("ReservedCodeCacheSize", "400M");
|
||||
if (javaVersion >= 9) {
|
||||
res.addUnstableDefault("NonNMethodCodeHeapSize", "12M");
|
||||
res.addUnstableDefault("ProfiledCodeHeapSize", "194M");
|
||||
}
|
||||
|
||||
if (javaVersion >= 8) {
|
||||
res.addUnstableDefault("NmethodSweepActivity", "1");
|
||||
}
|
||||
}
|
||||
|
||||
// As 32-bit JVM allocate 320KB for stack by default rather than 64-bit version allocating 1MB,
|
||||
// causing Minecraft 1.13 crashed accounting for java.lang.StackOverflowError.
|
||||
if (options.getJava().getBits() == Bits.BIT_32) {
|
||||
if (!is64bit) {
|
||||
res.addDefault("-Xss", "1m");
|
||||
}
|
||||
|
||||
if (options.getJava().getParsedVersion() == 16)
|
||||
if (javaVersion == 16)
|
||||
res.addDefault("--illegal-access=", "permit");
|
||||
|
||||
if (javaVersion == 24 || javaVersion == 25)
|
||||
res.addDefault("--sun-misc-unsafe-memory-access=", "allow");
|
||||
|
||||
res.addDefault("-Dfml.ignoreInvalidMinecraftCertificates=", "true");
|
||||
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -44,7 +44,7 @@ public interface ModpackProvider {
|
||||
* @throws JsonParseException if the manifest.json is missing or malformed.
|
||||
* @return the manifest.
|
||||
*/
|
||||
Modpack readManifest(ZipFile zipFile, Path file, Charset encoding) throws IOException, JsonParseException;
|
||||
Modpack readManifest(ZipArchiveReader zipFile, Path file, Charset encoding) throws IOException, JsonParseException;
|
||||
|
||||
default void injectLaunchOptions(String modpackConfigurationJson, LaunchOptions.Builder builder) {
|
||||
}
|
||||
|
||||
+3
-1
@@ -102,7 +102,9 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
|
||||
@Override
|
||||
public SearchResult search(String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException {
|
||||
int categoryId = 0;
|
||||
if (category != null) categoryId = ((CurseAddon.Category) category.getSelf()).getId();
|
||||
if (category != null && category.getSelf() instanceof CurseAddon.Category) {
|
||||
categoryId = ((CurseAddon.Category) category.getSelf()).getId();
|
||||
}
|
||||
Response<List<CurseAddon>> response = HttpRequest.GET(PREFIX + "/v1/mods/search",
|
||||
pair("gameId", "432"),
|
||||
pair("classId", Integer.toString(section)),
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.curse;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -57,7 +57,7 @@ public final class CurseModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
CurseManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "manifest.json"), CurseManifest.class);
|
||||
String description = "No description";
|
||||
try {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.mcbbs;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.LaunchOptions;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
@@ -70,7 +70,7 @@ public final class McbbsModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
ZipArchiveEntry mcbbsPackMeta = zip.getEntry("mcbbs.packmeta");
|
||||
if (mcbbsPackMeta != null) {
|
||||
return fromManifestFile(zip.getInputStream(mcbbsPackMeta), encoding);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod.modrinth;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -55,7 +55,7 @@ public final class ModrinthModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
ModrinthManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "modrinth.index.json"), ModrinthManifest.class);
|
||||
return new Modpack(manifest.getName(), "", manifest.getVersionId(), manifest.getGameVersion(), manifest.getSummary(), encoding, manifest) {
|
||||
@Override
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class MultiMCManifest {
|
||||
* @throws IOException if zip file is malformed
|
||||
* @throws com.google.gson.JsonParseException if manifest is malformed.
|
||||
*/
|
||||
public static MultiMCManifest readMultiMCModpackManifest(ZipFile zipFile, String rootEntryName) throws IOException {
|
||||
public static MultiMCManifest readMultiMCModpackManifest(ZipArchiveReader zipFile, String rootEntryName) throws IOException {
|
||||
ZipArchiveEntry mmcPack = zipFile.getEntry(rootEntryName + "mmc-pack.json");
|
||||
if (mmcPack == null)
|
||||
return null;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.mod.multimc;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -33,7 +33,6 @@ import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
@@ -71,14 +70,12 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getRootEntryName(ZipFile file) throws IOException {
|
||||
private static String getRootEntryName(ZipArchiveReader file) throws IOException {
|
||||
final String instanceFileName = "instance.cfg";
|
||||
|
||||
if (file.getEntry(instanceFileName) != null) return "";
|
||||
|
||||
Enumeration<ZipArchiveEntry> entries = file.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipArchiveEntry entry = entries.nextElement();
|
||||
for (ZipArchiveEntry entry : file.getEntries()) {
|
||||
String entryName = entry.getName();
|
||||
|
||||
int idx = entryName.indexOf('/');
|
||||
@@ -92,7 +89,7 @@ public final class MultiMCModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile modpackFile, Path modpackPath, Charset encoding) throws IOException {
|
||||
public Modpack readManifest(ZipArchiveReader modpackFile, Path modpackPath, Charset encoding) throws IOException {
|
||||
String rootEntryName = getRootEntryName(modpackFile);
|
||||
MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile, rootEntryName);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package org.jackhuang.hmcl.mod.server;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
@@ -55,7 +55,7 @@ public final class ServerModpackProvider implements ModpackProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) throws IOException, JsonParseException {
|
||||
String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json");
|
||||
ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class);
|
||||
return manifest.toModpack(encoding);
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
@@ -54,19 +54,16 @@ public final class CompressingUtils {
|
||||
}
|
||||
|
||||
public static boolean testEncoding(Path zipFile, Charset encoding) throws IOException {
|
||||
try (ZipFile zf = openZipFile(zipFile, encoding)) {
|
||||
try (ZipArchiveReader zf = openZipFile(zipFile, encoding)) {
|
||||
return testEncoding(zf, encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean testEncoding(ZipFile zipFile, Charset encoding) throws IOException {
|
||||
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
|
||||
public static boolean testEncoding(ZipArchiveReader zipFile, Charset encoding) throws IOException {
|
||||
CharsetDecoder cd = newCharsetDecoder(encoding);
|
||||
CharBuffer cb = CharBuffer.allocate(32);
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipArchiveEntry entry = entries.nextElement();
|
||||
|
||||
for (ZipArchiveEntry entry : zipFile.getEntries()) {
|
||||
if (entry.getGeneralPurposeBit().usesUTF8ForNames()) continue;
|
||||
|
||||
cd.reset();
|
||||
@@ -88,12 +85,12 @@ public final class CompressingUtils {
|
||||
}
|
||||
|
||||
public static Charset findSuitableEncoding(Path zipFile) throws IOException {
|
||||
try (ZipFile zf = openZipFile(zipFile, StandardCharsets.UTF_8)) {
|
||||
try (ZipArchiveReader zf = openZipFile(zipFile, StandardCharsets.UTF_8)) {
|
||||
return findSuitableEncoding(zf);
|
||||
}
|
||||
}
|
||||
|
||||
public static Charset findSuitableEncoding(ZipFile zipFile) throws IOException {
|
||||
public static Charset findSuitableEncoding(ZipArchiveReader zipFile) throws IOException {
|
||||
if (testEncoding(zipFile, StandardCharsets.UTF_8)) return StandardCharsets.UTF_8;
|
||||
if (OperatingSystem.NATIVE_CHARSET != StandardCharsets.UTF_8 && testEncoding(zipFile, OperatingSystem.NATIVE_CHARSET))
|
||||
return OperatingSystem.NATIVE_CHARSET;
|
||||
@@ -133,12 +130,12 @@ public final class CompressingUtils {
|
||||
throw new IOException("Cannot find suitable encoding for the zip.");
|
||||
}
|
||||
|
||||
public static ZipFile openZipFile(Path zipFile) throws IOException {
|
||||
return new ZipFile(Files.newByteChannel(zipFile));
|
||||
public static ZipArchiveReader openZipFile(Path zipFile) throws IOException {
|
||||
return new ZipArchiveReader(Files.newByteChannel(zipFile));
|
||||
}
|
||||
|
||||
public static ZipFile openZipFile(Path zipFile, Charset charset) throws IOException {
|
||||
return new ZipFile(Files.newByteChannel(zipFile), charset.name());
|
||||
public static ZipArchiveReader openZipFile(Path zipFile, Charset charset) throws IOException {
|
||||
return new ZipArchiveReader(zipFile, charset);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
@@ -234,7 +231,7 @@ public final class CompressingUtils {
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(File zipFile, String name) throws IOException {
|
||||
try (ZipFile s = new ZipFile(zipFile)) {
|
||||
try (ZipArchiveReader s = new ZipArchiveReader(zipFile.toPath())) {
|
||||
return readTextZipEntry(s, name);
|
||||
}
|
||||
}
|
||||
@@ -247,7 +244,7 @@ public final class CompressingUtils {
|
||||
* @throws IOException if the file is not a valid zip file.
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(ZipFile zipFile, String name) throws IOException {
|
||||
public static String readTextZipEntry(ZipArchiveReader zipFile, String name) throws IOException {
|
||||
return IOUtils.readFullyAsString(zipFile.getInputStream(zipFile.getEntry(name)));
|
||||
}
|
||||
|
||||
@@ -260,7 +257,7 @@ public final class CompressingUtils {
|
||||
* @return the plain text content of given file.
|
||||
*/
|
||||
public static String readTextZipEntry(Path zipFile, String name, Charset encoding) throws IOException {
|
||||
try (ZipFile s = openZipFile(zipFile, encoding)) {
|
||||
try (ZipArchiveReader s = openZipFile(zipFile, encoding)) {
|
||||
return IOUtils.readFullyAsString(s.getInputStream(s.getEntry(name)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.glavo.chardet.DetectedCharset;
|
||||
import org.glavo.chardet.UniversalDetector;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
||||
@@ -128,6 +130,24 @@ public final class FileUtils {
|
||||
return new String(Files.readAllBytes(file), charset);
|
||||
}
|
||||
|
||||
public static String readTextMaybeNativeEncoding(Path file) throws IOException {
|
||||
byte[] bytes = Files.readAllBytes(file);
|
||||
|
||||
if (OperatingSystem.NATIVE_CHARSET == UTF_8)
|
||||
return new String(bytes, UTF_8);
|
||||
|
||||
UniversalDetector detector = new UniversalDetector();
|
||||
detector.handleData(bytes);
|
||||
detector.dataEnd();
|
||||
|
||||
DetectedCharset detectedCharset = detector.getDetectedCharset();
|
||||
if (detectedCharset != null && detectedCharset.isSupported()
|
||||
&& (detectedCharset == DetectedCharset.UTF_8 || detectedCharset == DetectedCharset.US_ASCII))
|
||||
return new String(bytes, UTF_8);
|
||||
else
|
||||
return new String(bytes, OperatingSystem.NATIVE_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write plain text to file. Characters are encoded into bytes using UTF-8.
|
||||
* <p>
|
||||
|
||||
@@ -17,10 +17,20 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.glavo.chardet.DetectedCharset;
|
||||
import org.glavo.chardet.UniversalDetector;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
import static org.jackhuang.hmcl.util.platform.OperatingSystem.NATIVE_CHARSET;
|
||||
|
||||
/**
|
||||
* This utility class consists of some util methods operating on InputStream/OutputStream.
|
||||
*
|
||||
@@ -33,6 +43,25 @@ public final class IOUtils {
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
|
||||
|
||||
public static BufferedReader newBufferedReaderMaybeNativeEncoding(Path file) throws IOException {
|
||||
if (NATIVE_CHARSET == UTF_8)
|
||||
return Files.newBufferedReader(file);
|
||||
|
||||
FileChannel channel = FileChannel.open(file);
|
||||
try {
|
||||
long oldPosition = channel.position();
|
||||
DetectedCharset detectedCharset = UniversalDetector.detectCharset(channel);
|
||||
Charset charset = detectedCharset != null && detectedCharset.isSupported()
|
||||
&& (detectedCharset.getCharset() == UTF_8 || detectedCharset.getCharset() == US_ASCII)
|
||||
? UTF_8 : NATIVE_CHARSET;
|
||||
channel.position(oldPosition);
|
||||
return new BufferedReader(new InputStreamReader(Channels.newInputStream(channel), charset));
|
||||
} catch (Throwable e) {
|
||||
closeQuietly(channel, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all bytes to a buffer from given input stream. The stream will not be closed.
|
||||
*
|
||||
|
||||
@@ -19,14 +19,12 @@ package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalPredicate;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@@ -155,6 +153,27 @@ public final class Zipper implements Closeable {
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
public void putLines(Stream<String> lines, String path) throws IOException {
|
||||
zos.putNextEntry(new ZipEntry(normalize(path)));
|
||||
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
|
||||
lines.forEachOrdered(line -> {
|
||||
try {
|
||||
writer.write(line);
|
||||
writer.write('\n');
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
writer.flush();
|
||||
} catch (UncheckedIOException e) {
|
||||
throw e.getCause();
|
||||
} finally {
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public void putTextFile(String text, String path) throws IOException {
|
||||
putTextFile(text, StandardCharsets.UTF_8, path);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.ArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -39,7 +39,7 @@ public abstract class ArchiveFileTree<F, E extends ArchiveEntry> implements Clos
|
||||
|
||||
String name = namePath.toString();
|
||||
if (name.endsWith(".jar") || name.endsWith(".zip")) {
|
||||
return new ZipFileTree(new ZipFile(file));
|
||||
return new ZipFileTree(new ZipArchiveReader(file));
|
||||
} else if (name.endsWith(".tar") || name.endsWith(".tar.gz") || name.endsWith(".tgz")) {
|
||||
return TarFileTree.open(file);
|
||||
} else {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarFile;
|
||||
import kala.compress.archivers.tar.TarArchiveEntry;
|
||||
import kala.compress.archivers.tar.TarArchiveReader;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
@@ -31,19 +31,19 @@ import java.util.zip.GZIPInputStream;
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class TarFileTree extends ArchiveFileTree<TarFile, TarArchiveEntry> {
|
||||
public final class TarFileTree extends ArchiveFileTree<TarArchiveReader, TarArchiveEntry> {
|
||||
|
||||
public static TarFileTree open(Path file) throws IOException {
|
||||
String fileName = file.getFileName().toString();
|
||||
|
||||
if (fileName.endsWith(".tar.gz") || fileName.endsWith(".tgz")) {
|
||||
Path tempFile = Files.createTempFile("hmcl-", ".tar");
|
||||
TarFile tarFile;
|
||||
TarArchiveReader tarFile;
|
||||
try (GZIPInputStream input = new GZIPInputStream(Files.newInputStream(file));
|
||||
OutputStream output = Files.newOutputStream(tempFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)
|
||||
) {
|
||||
IOUtils.copyTo(input, output);
|
||||
tarFile = new TarFile(tempFile.toFile());
|
||||
tarFile = new TarArchiveReader(tempFile);
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
Files.deleteIfExists(tempFile);
|
||||
@@ -55,14 +55,14 @@ public final class TarFileTree extends ArchiveFileTree<TarFile, TarArchiveEntry>
|
||||
|
||||
return new TarFileTree(tarFile, tempFile);
|
||||
} else {
|
||||
return new TarFileTree(new TarFile(file), null);
|
||||
return new TarFileTree(new TarArchiveReader(file), null);
|
||||
}
|
||||
}
|
||||
|
||||
private final Path tempFile;
|
||||
private final Thread shutdownHook;
|
||||
|
||||
public TarFileTree(TarFile file, Path tempFile) throws IOException {
|
||||
public TarFileTree(TarArchiveReader file, Path tempFile) throws IOException {
|
||||
super(file);
|
||||
this.tempFile = tempFile;
|
||||
try {
|
||||
|
||||
@@ -17,23 +17,21 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.tree;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import kala.compress.archivers.zip.ZipArchiveEntry;
|
||||
import kala.compress.archivers.zip.ZipArchiveReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
public final class ZipFileTree extends ArchiveFileTree<ZipFile, ZipArchiveEntry> {
|
||||
public ZipFileTree(ZipFile file) throws IOException {
|
||||
public final class ZipFileTree extends ArchiveFileTree<ZipArchiveReader, ZipArchiveEntry> {
|
||||
public ZipFileTree(ZipArchiveReader file) throws IOException {
|
||||
super(file);
|
||||
try {
|
||||
Enumeration<ZipArchiveEntry> entries = file.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
addEntry(entries.nextElement());
|
||||
for (ZipArchiveEntry zipArchiveEntry : file.getEntries()) {
|
||||
addEntry(zipArchiveEntry);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"id": "1_19_deep_dark_experimental_snapshot-1",
|
||||
"id": "1.19_deep_dark_experimental_snapshot-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_19_deep_dark_experimental_snapshot-1/1_19_deep_dark_experimental_snapshot-1.json",
|
||||
"time": "2022-02-17T13:55:59+00:00",
|
||||
"releaseTime": "2022-02-17T13:55:59+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-7",
|
||||
"id": "1.18_experimental-snapshot-7",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-7/1_18_experimental-snapshot-7.json",
|
||||
"time": "2021-09-08T12:33:23+00:00",
|
||||
"releaseTime": "2021-09-08T12:33:23+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-6",
|
||||
"id": "1.18_experimental-snapshot-6",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-6/1_18_experimental-snapshot-6.json",
|
||||
"time": "2021-09-01T12:53:14+00:00",
|
||||
"releaseTime": "2021-09-01T12:53:14+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-5",
|
||||
"id": "1.18_experimental-snapshot-5",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-5/1_18_experimental-snapshot-5.json",
|
||||
"time": "2021-08-25T14:41:57+00:00",
|
||||
"releaseTime": "2021-08-25T14:41:57+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-4",
|
||||
"id": "1.18_experimental-snapshot-4",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-4/1_18_experimental-snapshot-4.json",
|
||||
"time": "2021-08-17T16:10:25+00:00",
|
||||
"releaseTime": "2021-08-17T16:10:25+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-3",
|
||||
"id": "1.18_experimental-snapshot-3",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-3/1_18_experimental-snapshot-3.json",
|
||||
"time": "2021-08-10T12:42:45+00:00",
|
||||
"releaseTime": "2021-08-10T12:42:45+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-2",
|
||||
"id": "1.18_experimental-snapshot-2",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-2/1_18_experimental-snapshot-2.json",
|
||||
"time": "2021-07-20T13:35:08+00:00",
|
||||
"releaseTime": "2021-07-20T13:35:08+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_18_experimental-snapshot-1",
|
||||
"id": "1.18_experimental-snapshot-1",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_18_experimental-snapshot-1/1_18_experimental-snapshot-1.json",
|
||||
"time": "2021-07-13T12:54:19+00:00",
|
||||
@@ -120,43 +120,43 @@
|
||||
"releaseTime": "2019-11-29T15:41:39+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-3",
|
||||
"id": "1.14_combat-3",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-3/1_14_combat-3.json",
|
||||
"time": "2019-10-31T14:31:38+00:00",
|
||||
"releaseTime": "2019-10-31T14:31:38+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-0",
|
||||
"id": "1.14_combat-0",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-0/1_14_combat-0.json",
|
||||
"time": "2019-08-13T07:33:42+00:00",
|
||||
"releaseTime": "2019-08-13T07:33:42+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1_14_combat-212796",
|
||||
"id": "1.14_combat-212796",
|
||||
"type": "pending",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1_14_combat-212796/1_14_combat-212796.json",
|
||||
"time": "2019-06-20T13:23:44+00:00",
|
||||
"releaseTime": "2019-06-20T13:23:44+00:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_blue",
|
||||
"type": "release",
|
||||
"id": "2.0_blue",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_blue/2point0_blue.json",
|
||||
"time": "2013-08-06T06:00:02-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:02-05:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_red",
|
||||
"type": "release",
|
||||
"id": "2.0_red",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_red/2point0_red.json",
|
||||
"time": "2013-08-06T06:00:01-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:01-05:00"
|
||||
},
|
||||
{
|
||||
"id": "2point0_purple",
|
||||
"type": "release",
|
||||
"id": "2.0_purple",
|
||||
"type": "snapshot",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/2point0_purple/2point0_purple.json",
|
||||
"time": "2013-08-06T06:00:00-05:00",
|
||||
"releaseTime": "2013-03-20T05:00:00-05:00"
|
||||
@@ -632,70 +632,70 @@
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-3",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-3/1.0.0-rc2-3.json",
|
||||
"time": "2013-08-06T07:33:30-07:00",
|
||||
"releaseTime": "2011-11-14T15:00:00-07:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-2",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-2/1.0.0-rc2-2.json",
|
||||
"time": "2019-04-21T21:04:24+00:00",
|
||||
"releaseTime": "2011-11-14T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc2-1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc2-1/1.0.0-rc2-1.json",
|
||||
"time": "2019-11-21T22:37:45+00:00",
|
||||
"releaseTime": "2011-11-13T17:33:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "1.0.0-rc1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/1.0.0-rc1/1.0.0-rc1.json",
|
||||
"time": "2013-08-06T07:33:30-07:00",
|
||||
"releaseTime": "2011-11-13T15:00:00-07:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre6",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre6/b1_9-pre6.json",
|
||||
"time": "2011-11-11T00:00:00+00:00",
|
||||
"releaseTime": "2011-11-11T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre5",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre5/b1_9-pre5.json",
|
||||
"time": "2011-10-27T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-27T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre4",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre4/b1_9-pre4.json",
|
||||
"time": "2011-10-13T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-13T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre3",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre3/b1_9-pre3.json",
|
||||
"time": "2011-10-06T00:00:00+00:00",
|
||||
"releaseTime": "2011-10-06T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre2",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre2/b1_9-pre2.json",
|
||||
"time": "2011-09-29T00:00:00+00:00",
|
||||
"releaseTime": "2011-09-29T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"id": "b1.9-pre1",
|
||||
"type": "snapshot",
|
||||
"type": "old_beta",
|
||||
"url": "https://zkitefly.github.io/unlisted-versions-of-minecraft/files/b1_9-pre1/b1_9-pre1.json",
|
||||
"time": "2011-09-22T00:00:00+00:00",
|
||||
"releaseTime": "2011-09-22T00:00:00+00:00"
|
||||
@@ -1359,4 +1359,4 @@
|
||||
"releaseTime": "2009-05-20T00:00:00-00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# Use the Google style in this project.
|
||||
BasedOnStyle: Google
|
||||
|
||||
SortIncludes: false
|
||||
@@ -1,5 +0,0 @@
|
||||
Debug
|
||||
Release
|
||||
.vs
|
||||
*.APS
|
||||
cmake-build-*/
|
||||
@@ -1,18 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(HMCLauncher)
|
||||
if(MSVC)
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
add_compile_options(/utf-8 /W4)
|
||||
add_link_options(/ENTRY:wWinMainCRTStartup)
|
||||
else()
|
||||
add_compile_options(-municode -Wall -Wextra -Wpedantic)
|
||||
add_link_options(-municode)
|
||||
endif()
|
||||
OPTION(ENABLE_MINGW_STATIC_LINK_LIBSTDCXX "Link the C++ standard library statically to the executable file(mingw only)." ON)
|
||||
if(ENABLE_MINGW_STATIC_LINK_LIBSTDCXX AND MINGW)
|
||||
add_link_options(-static)
|
||||
endif()
|
||||
set(CMAKE_WIN32_EXECUTABLE ON)
|
||||
add_executable(HMCLauncher WIN32 HMCL/HMCL.rc HMCL/java.cpp HMCL/main.cpp HMCL/os.cpp HMCL/stdafx.cpp HMCL/Version.cpp)
|
||||
target_link_libraries(HMCLauncher Version)
|
||||
install(TARGETS HMCLauncher)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
@@ -1,143 +0,0 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
#include "targetver.h"
|
||||
#endif
|
||||
#define APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "windows.h"
|
||||
#undef APSTUDIO_HIDDEN_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// 中文(简体,中国) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
|
||||
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_HMCL ICON "HMCL.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Accelerator
|
||||
//
|
||||
|
||||
IDC_HMCL ACCELERATORS
|
||||
BEGIN
|
||||
"?", IDM_ABOUT, ASCII, ALT
|
||||
"/", IDM_ABOUT, ASCII, ALT
|
||||
END
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#ifndef APSTUDIO_INVOKED\r\n"
|
||||
"#include ""targetver.h""\r\n"
|
||||
"#endif\r\n"
|
||||
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""windows.h""\r\n"
|
||||
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,5,0,0
|
||||
PRODUCTVERSION 3,5,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "080404b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "huanghongxun"
|
||||
VALUE "FileDescription", "Hello Minecraft! Launcher For Windows"
|
||||
VALUE "FileVersion", "3.5.0.0"
|
||||
VALUE "InternalName", "HMCL.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2021 huangyuhui"
|
||||
VALUE "OriginalFilename", "HMCL.exe"
|
||||
VALUE "ProductName", "Hello Minecraft! Launcher"
|
||||
VALUE "ProductVersion", "3.5.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x804, 1200
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_APP_TITLE "HMCL"
|
||||
IDC_HMCL "HMCL"
|
||||
END
|
||||
|
||||
#endif // 中文(简体,中国) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{672B1019-E741-4C0D-A986-627E2ACE157B}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>HMCL</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>HMCLauncher</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="java.h" />
|
||||
<ClInclude Include="lang.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="os.h" />
|
||||
<ClInclude Include="Resource.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="version.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="java.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="os.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="version.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="HMCL.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="HMCL.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Resource.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="version.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="java.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="os.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lang.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="version.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="java.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="os.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="HMCL.rc">
|
||||
<Filter>资源文件</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="HMCL.ico">
|
||||
<Filter>资源文件</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,16 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "version.h"
|
||||
|
||||
Version::Version(const std::wstring &rawString) {
|
||||
int idx = 0;
|
||||
ver[0] = ver[1] = ver[2] = ver[3] = 0;
|
||||
for (auto &i : rawString) {
|
||||
if (idx >= 4) break;
|
||||
if (i == '.')
|
||||
++idx;
|
||||
else if (i == '_')
|
||||
++idx;
|
||||
else if (isdigit(i))
|
||||
ver[idx] = ver[idx] * 10 + (i - L'0');
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class Version {
|
||||
public:
|
||||
int ver[4];
|
||||
|
||||
Version(const std::wstring &rawString);
|
||||
|
||||
template <typename T>
|
||||
Version(std::initializer_list<T> ver_list) {
|
||||
int i = 0;
|
||||
for (const auto &data : ver_list) {
|
||||
if (i >= 4) break;
|
||||
ver[i++] = data;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator<(const Version &other) const {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (ver[i] != other.ver[i]) return ver[i] < other.ver[i];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator<=(const Version &other) const {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (ver[i] != other.ver[i]) return ver[i] < other.ver[i];
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "java.h"
|
||||
#include "os.h"
|
||||
#include "version.h"
|
||||
|
||||
const Version JAVA_8(L"1.8"), JAVA_11(L"11");
|
||||
|
||||
const LPCWSTR JDK_NEW = L"SOFTWARE\\JavaSoft\\JDK";
|
||||
const LPCWSTR JRE_NEW = L"SOFTWARE\\JavaSoft\\JRE";
|
||||
const LPCWSTR JDK_OLD = L"SOFTWARE\\JavaSoft\\Java Development Kit";
|
||||
const LPCWSTR JRE_OLD = L"SOFTWARE\\JavaSoft\\Java Runtime Environment";
|
||||
|
||||
bool oldJavaFound = false;
|
||||
|
||||
bool FindJavaByRegistryKey(HKEY rootKey, LPCWSTR subKey, std::wstring& path) {
|
||||
WCHAR javaVer[MAX_KEY_LENGTH]; // buffer for subkey name, special for
|
||||
// JavaVersion
|
||||
DWORD cbName; // size of name string
|
||||
DWORD cSubKeys = 0; // number of subkeys
|
||||
DWORD cbMaxSubKey; // longest subkey size
|
||||
DWORD cValues; // number of values for key
|
||||
DWORD cchMaxValue; // longest value name
|
||||
DWORD cbMaxValueData; // longest value data
|
||||
LSTATUS result;
|
||||
|
||||
HKEY hKey;
|
||||
if (ERROR_SUCCESS !=
|
||||
(result =
|
||||
RegOpenKeyEx(rootKey, subKey, 0, KEY_WOW64_64KEY | KEY_READ, &hKey)))
|
||||
return false;
|
||||
|
||||
RegQueryInfoKey(hKey, // key handle
|
||||
NULL, // buffer for class name
|
||||
NULL, // size of class string
|
||||
NULL, // reserved
|
||||
&cSubKeys, // number of subkeys
|
||||
&cbMaxSubKey, // longest subkey size
|
||||
NULL, // longest class string
|
||||
&cValues, // number of values for this key
|
||||
&cchMaxValue, // longest value name
|
||||
&cbMaxValueData, // longest value data
|
||||
NULL, // security descriptor
|
||||
NULL); // last write time
|
||||
|
||||
if (!cSubKeys) return false;
|
||||
|
||||
bool flag = false;
|
||||
for (DWORD i = 0; i < cSubKeys; ++i) {
|
||||
cbName = MAX_KEY_LENGTH;
|
||||
if (ERROR_SUCCESS != (result = RegEnumKeyEx(hKey, i, javaVer, &cbName, NULL,
|
||||
NULL, NULL, NULL)))
|
||||
continue;
|
||||
|
||||
HKEY javaKey;
|
||||
if (ERROR_SUCCESS != RegOpenKeyEx(hKey, javaVer, 0, KEY_READ, &javaKey))
|
||||
continue;
|
||||
|
||||
if (ERROR_SUCCESS == MyRegQueryValue(javaKey, L"JavaHome", REG_SZ, path)) {
|
||||
if (Version(javaVer) < JAVA_8)
|
||||
oldJavaFound = true;
|
||||
else
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (flag) break;
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
bool FindJavaInRegistry(std::wstring& path) {
|
||||
return FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_NEW, path) ||
|
||||
FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_NEW, path) ||
|
||||
FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JDK_OLD, path) ||
|
||||
FindJavaByRegistryKey(HKEY_LOCAL_MACHINE, JRE_OLD, path);
|
||||
}
|
||||
|
||||
bool FindJava(std::wstring& path) {
|
||||
return ERROR_SUCCESS == MyGetEnvironmentVariable(L"HMCL_JAVA_HOME", path) ||
|
||||
ERROR_SUCCESS == MyGetEnvironmentVariable(L"JAVA_HOME", path) ||
|
||||
FindJavaInRegistry(path);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
// Find Java installation in system registry
|
||||
bool FindJavaInRegistry(std::wstring &path);
|
||||
|
||||
// Find Java Installation in registry and environment variable
|
||||
bool FindJava(std::wstring &path);
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define ERROR_TITLE L"Java not found"
|
||||
|
||||
#define ERROR_TITLE_ZH L"未找到 Java"
|
||||
|
||||
#define ERROR_PROMPT L"The Java runtime environment is required to run HMCL and Minecraft,\n"\
|
||||
L"Click 'OK' to start downloading java.\n"\
|
||||
L"Please restart HMCL after installing Java."
|
||||
|
||||
#define ERROR_PROMPT_ZH L"运行 HMCL 以及 Minecraft 需要 Java 运行时环境,点击“确定”开始下载。\n"\
|
||||
L"请在安装 Java 完成后重新启动 HMCL。"
|
||||
@@ -1,155 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "main.h"
|
||||
#include "os.h"
|
||||
#include "java.h"
|
||||
#include "lang.h"
|
||||
#include <windows.h>
|
||||
|
||||
Version J8(TEXT("8"));
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001;
|
||||
}
|
||||
|
||||
LPCWSTR VENDOR_DIRS[] = {
|
||||
L"Java", L"Microsoft", L"BellSoft", L"Zulu", L"Eclipse Foundation", L"AdoptOpenJDK", L"Semeru"
|
||||
};
|
||||
|
||||
void RawLaunchJVM(const std::wstring &javaPath, const std::wstring &workdir,
|
||||
const std::wstring &jarPath, const std::wstring &jvmOptions) {
|
||||
if (MyCreateProcess(L"\"" + javaPath + L"\" " + jvmOptions + L" -jar \"" + jarPath + L"\"", workdir))
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void LaunchJVM(const std::wstring &javaPath, const std::wstring &workdir,
|
||||
const std::wstring &jarPath, const std::wstring &jvmOptions) {
|
||||
Version javaVersion(L"");
|
||||
if (!MyGetFileVersionInfo(javaPath, javaVersion)) return;
|
||||
|
||||
if (J8 <= javaVersion) {
|
||||
RawLaunchJVM(javaPath, workdir, jarPath, jvmOptions);
|
||||
}
|
||||
}
|
||||
|
||||
void FindJavaInDirAndLaunchJVM(const std::wstring &baseDir, const std::wstring &workdir,
|
||||
const std::wstring &jarPath, const std::wstring &jvmOptions) {
|
||||
std::wstring pattern = baseDir + L"*";
|
||||
|
||||
WIN32_FIND_DATA data;
|
||||
HANDLE hFind = FindFirstFile(pattern.c_str(), &data); // Search all subdirectory
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
std::wstring javaw = baseDir + data.cFileName + std::wstring(L"\\bin\\javaw.exe");
|
||||
if (FindFirstFileExists(javaw.c_str(), 0)) {
|
||||
LaunchJVM(javaw, workdir, jarPath, jvmOptions);
|
||||
}
|
||||
} while (FindNextFile(hFind, &data));
|
||||
FindClose(hFind);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenHelpPage() {
|
||||
ShellExecute(0, 0, L"https://docs.hmcl.net/help.html", 0, 0, SW_SHOW);
|
||||
}
|
||||
|
||||
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
LPWSTR lpCmdLine, int nCmdShow) {
|
||||
std::wstring path, exeName, jvmOptions;
|
||||
|
||||
// Since Jar file is appended to this executable, we should first get the
|
||||
// location of JAR file.
|
||||
if (ERROR_SUCCESS != MyGetModuleFileName(NULL, exeName)) return 1;
|
||||
|
||||
std::wstring workdir;
|
||||
size_t last_slash = exeName.find_last_of(L"/\\");
|
||||
if (last_slash != std::wstring::npos && last_slash + 1 < exeName.size()) {
|
||||
workdir = exeName.substr(0, last_slash);
|
||||
exeName = exeName.substr(last_slash + 1);
|
||||
}
|
||||
|
||||
if (ERROR_SUCCESS != MyGetEnvironmentVariable(L"HMCL_JAVA_OPTS", jvmOptions)) {
|
||||
jvmOptions = L"-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=15"; // Default Options
|
||||
}
|
||||
|
||||
bool useChinese = GetUserDefaultUILanguage() == 2052; // zh-CN
|
||||
|
||||
MyArchitecture architecture = MyGetArchitecture();
|
||||
|
||||
// First try the Java packaged together.
|
||||
bool isX64 = architecture == MyArchitecture::X86_64;
|
||||
bool isARM64 = architecture == MyArchitecture::ARM64;
|
||||
|
||||
if (isARM64) {
|
||||
RawLaunchJVM(L"jre-arm64\\bin\\javaw.exe", workdir, exeName, jvmOptions);
|
||||
}
|
||||
if (isX64) {
|
||||
RawLaunchJVM(L"jre-x64\\bin\\javaw.exe", workdir, exeName, jvmOptions);
|
||||
}
|
||||
RawLaunchJVM(L"jre-x86\\bin\\javaw.exe", workdir, exeName, jvmOptions);
|
||||
|
||||
if (FindJava(path)) LaunchJVM(path + L"\\bin\\javaw.exe", workdir, exeName, jvmOptions);
|
||||
|
||||
std::wstring programFiles;
|
||||
|
||||
// Or we try to search Java in C:\Program Files
|
||||
if (!SUCCEEDED(MySHGetFolderPath(CSIDL_PROGRAM_FILES, programFiles))) programFiles = L"C:\\Program Files\\";
|
||||
for (LPCWSTR vendorDir : VENDOR_DIRS) {
|
||||
std::wstring dir = programFiles;
|
||||
MyPathAppend(dir, vendorDir);
|
||||
MyPathAddBackslash(dir);
|
||||
|
||||
FindJavaInDirAndLaunchJVM(dir, workdir, exeName, jvmOptions);
|
||||
}
|
||||
|
||||
// Consider C:\Program Files (x86)
|
||||
if (!SUCCEEDED(MySHGetFolderPath(CSIDL_PROGRAM_FILESX86, programFiles))) programFiles = L"C:\\Program Files (x86)\\";
|
||||
for (LPCWSTR vendorDir : VENDOR_DIRS) {
|
||||
std::wstring dir = programFiles;
|
||||
MyPathAppend(dir, vendorDir);
|
||||
MyPathAddBackslash(dir);
|
||||
|
||||
FindJavaInDirAndLaunchJVM(dir, workdir, exeName, jvmOptions);
|
||||
}
|
||||
|
||||
// Try java in PATH
|
||||
RawLaunchJVM(L"javaw", workdir, exeName, jvmOptions);
|
||||
|
||||
std::wstring hmclJavaDir;
|
||||
{
|
||||
std::wstring buffer;
|
||||
if (SUCCEEDED(MySHGetFolderPath(CSIDL_APPDATA, buffer)) || SUCCEEDED(MySHGetFolderPath(CSIDL_PROFILE, buffer))) {
|
||||
MyPathAppend(buffer, L".hmcl");
|
||||
MyPathAppend(buffer, L"java");
|
||||
if (isARM64) {
|
||||
MyPathAppend(buffer, L"windows-arm64");
|
||||
} else if (isX64) {
|
||||
MyPathAppend(buffer, L"windows-x86_64");
|
||||
} else {
|
||||
MyPathAppend(buffer, L"windows-x86");
|
||||
}
|
||||
MyPathAddBackslash(buffer);
|
||||
hmclJavaDir = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hmclJavaDir.empty()) {
|
||||
FindJavaInDirAndLaunchJVM(hmclJavaDir, workdir, exeName, jvmOptions);
|
||||
}
|
||||
|
||||
LPCWSTR downloadLink;
|
||||
|
||||
if (isARM64) {
|
||||
downloadLink = L"https://docs.hmcl.net/downloads/windows/arm64.html";
|
||||
} else if (isX64) {
|
||||
downloadLink = L"https://docs.hmcl.net/downloads/windows/x86_64.html";
|
||||
} else {
|
||||
downloadLink = L"https://docs.hmcl.net/downloads/windows/x86.html";
|
||||
}
|
||||
|
||||
if (IDOK == MessageBox(NULL, useChinese ? ERROR_PROMPT_ZH : ERROR_PROMPT, useChinese ? ERROR_TITLE_ZH : ERROR_TITLE, MB_ICONWARNING | MB_OKCANCEL)) {
|
||||
ShellExecute(0, 0, downloadLink, 0, 0, SW_SHOW);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "resource.h"
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "os.h"
|
||||
|
||||
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS2) (HANDLE, PUSHORT, PUSHORT);
|
||||
|
||||
MyArchitecture MyGetArchitecture() {
|
||||
LPFN_ISWOW64PROCESS2 fnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)GetProcAddress(
|
||||
GetModuleHandle(L"Kernel32.dll"), "IsWow64Process2");
|
||||
if (NULL != fnIsWow64Process2) {
|
||||
USHORT uProcessMachine = 0;
|
||||
USHORT uNativeMachine = 0;
|
||||
if (fnIsWow64Process2(GetCurrentProcess(), &uProcessMachine, &uNativeMachine)) {
|
||||
if (uNativeMachine == 0xAA64) {
|
||||
return MyArchitecture::ARM64;
|
||||
}
|
||||
|
||||
if (uNativeMachine == 0x8664) {
|
||||
return MyArchitecture::X86_64;
|
||||
}
|
||||
|
||||
return MyArchitecture::X86;
|
||||
}
|
||||
}
|
||||
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetNativeSystemInfo(&systemInfo);
|
||||
|
||||
if (systemInfo.wProcessorArchitecture == 12) { // PROCESSOR_ARCHITECTURE_ARM64
|
||||
return MyArchitecture::ARM64;
|
||||
}
|
||||
|
||||
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
|
||||
return MyArchitecture::X86_64;
|
||||
}
|
||||
|
||||
return MyArchitecture::X86;
|
||||
}
|
||||
|
||||
LSTATUS MyRegQueryValue(HKEY hKey, LPCWSTR subKey, DWORD dwType,
|
||||
std::wstring &out) {
|
||||
DWORD dwSize = 0;
|
||||
LSTATUS ret = RegQueryValueEx(hKey, subKey, 0, &dwType, NULL, &dwSize);
|
||||
if (ret != ERROR_SUCCESS) return ret;
|
||||
WCHAR *buffer = new WCHAR[dwSize];
|
||||
ret = RegQueryValueEx(hKey, subKey, 0, &dwType, (LPBYTE)buffer, &dwSize);
|
||||
if (ret != ERROR_SUCCESS) return ret;
|
||||
out = buffer;
|
||||
delete[] buffer;
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out) {
|
||||
DWORD res, size = MAX_PATH;
|
||||
out = std::wstring();
|
||||
out.resize(size);
|
||||
while ((res = GetModuleFileName(hModule, &out[0], size)) == size) {
|
||||
out.resize(size += MAX_PATH);
|
||||
}
|
||||
if (res == 0)
|
||||
return GetLastError();
|
||||
else {
|
||||
out.resize(size - MAX_PATH + res);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring &out) {
|
||||
DWORD res, size = MAX_PATH;
|
||||
out = std::wstring();
|
||||
out.resize(size);
|
||||
while ((res = GetEnvironmentVariable(name, &out[0], size)) == size) {
|
||||
out.resize(size += MAX_PATH);
|
||||
}
|
||||
if (res == 0)
|
||||
return GetLastError();
|
||||
else {
|
||||
out.resize(size - MAX_PATH + res);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
bool MyCreateProcess(const std::wstring &command, const std::wstring &workdir) {
|
||||
std::wstring writable_command = command;
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (workdir.empty()) {
|
||||
return CreateProcess(NULL, &writable_command[0], NULL, NULL, false,
|
||||
NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
|
||||
} else {
|
||||
return CreateProcess(NULL, &writable_command[0], NULL, NULL, false,
|
||||
NORMAL_PRIORITY_CLASS, NULL, workdir.c_str(), &si,
|
||||
&pi);
|
||||
}
|
||||
}
|
||||
|
||||
bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter) {
|
||||
WIN32_FIND_DATA fd;
|
||||
HANDLE hFind = FindFirstFile(lpPath, &fd);
|
||||
bool bFilter = (false == dwFilter) ? true : fd.dwFileAttributes & dwFilter;
|
||||
bool ret = ((hFind != INVALID_HANDLE_VALUE) && bFilter) ? true : false;
|
||||
FindClose(hFind);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MyGetFileVersionInfo(const std::wstring &filePath, Version &version) {
|
||||
DWORD verHandle = 0;
|
||||
UINT size = 0;
|
||||
LPBYTE lpBuffer = NULL;
|
||||
VS_FIXEDFILEINFO *pFileInfo;
|
||||
DWORD dwSize = GetFileVersionInfoSize(filePath.c_str(), NULL);
|
||||
|
||||
if (!dwSize) return false;
|
||||
|
||||
LPBYTE data = new BYTE[dwSize];
|
||||
if (!GetFileVersionInfo(filePath.c_str(), 0, dwSize, data)) {
|
||||
delete[] data;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!VerQueryValue(data, TEXT("\\"), (LPVOID *)&pFileInfo, &size)) {
|
||||
delete[] data;
|
||||
return false;
|
||||
}
|
||||
|
||||
version = Version{(pFileInfo->dwFileVersionMS >> 16) & 0xFFFF,
|
||||
(pFileInfo->dwFileVersionMS >> 0) & 0xFFFF,
|
||||
(pFileInfo->dwFileVersionLS >> 16) & 0xFFFF,
|
||||
(pFileInfo->dwFileVersionLS >> 0) & 0xFFFF};
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT MySHGetFolderPath(int csidl, std::wstring &out) {
|
||||
out = std::wstring();
|
||||
out.resize(MAX_PATH);
|
||||
|
||||
HRESULT res = SHGetFolderPath(NULL, csidl, NULL, 0, &out[0]);
|
||||
if (SUCCEEDED(res)) {
|
||||
out.resize(wcslen(&out[0]));
|
||||
} else {
|
||||
out.resize(0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void MyPathAppend(std::wstring &filePath, const std::wstring &more) {
|
||||
if (filePath.back() != L'\\') {
|
||||
filePath += L'\\';
|
||||
}
|
||||
|
||||
filePath += more;
|
||||
}
|
||||
|
||||
void MyPathAddBackslash(std::wstring &filePath) {
|
||||
if (filePath.back() != L'\\') {
|
||||
filePath += L'\\';
|
||||
}
|
||||
}
|
||||
|
||||
LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext, std::wstring &out) {
|
||||
out.resize(MAX_PATH);
|
||||
DWORD res = GetTempPath(MAX_PATH, &out[0]);
|
||||
if (res == 0) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
out.resize(res);
|
||||
|
||||
GUID guid;
|
||||
CoCreateGuid(&guid);
|
||||
|
||||
WCHAR buffer[MAX_PATH];
|
||||
int n = StringFromGUID2(guid, buffer, MAX_PATH);
|
||||
if (n == 0) {
|
||||
return CO_E_PATHTOOLONG;
|
||||
}
|
||||
|
||||
MyPathAddBackslash(out);
|
||||
out += prefixString;
|
||||
out += buffer;
|
||||
out += L'.';
|
||||
out += ext;
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void MyAppendPathToCommandLine(std::wstring &commandLine, const std::wstring &path) {
|
||||
commandLine += L'"';
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
WCHAR ch = path[i];
|
||||
if (ch == L'\\' && (i + 1 == path.size() || path[i + 1] == L'"')) {
|
||||
commandLine += L"\\\\";
|
||||
} else if (ch == L'"') {
|
||||
commandLine += L"\\\"";
|
||||
} else {
|
||||
commandLine += ch;
|
||||
}
|
||||
}
|
||||
commandLine += L'"';
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <Objbase.h>
|
||||
#include "Version.h"
|
||||
|
||||
const int MAX_KEY_LENGTH = 255;
|
||||
const int MAX_VALUE_NAME = 16383;
|
||||
|
||||
enum MyArchitecture {
|
||||
X86,
|
||||
X86_64,
|
||||
ARM64
|
||||
};
|
||||
|
||||
MyArchitecture MyGetArchitecture();
|
||||
|
||||
// Query registry value of class root hKey, key path subKey, stores result in
|
||||
// parameter out.
|
||||
LSTATUS MyRegQueryValue(HKEY hKey, LPCWSTR subKey, DWORD dwType,
|
||||
std::wstring &out);
|
||||
|
||||
// Get module file name, stores result in parameter out.
|
||||
LSTATUS MyGetModuleFileName(HMODULE hModule, std::wstring &out);
|
||||
|
||||
// Get environment variable by name, C++ style, stores the value in parameter
|
||||
// out.
|
||||
LSTATUS MyGetEnvironmentVariable(LPCWSTR name, std::wstring &out);
|
||||
|
||||
// Create process by invoking CreateProcess, only pass command.
|
||||
bool MyCreateProcess(const std::wstring &command, const std::wstring &workdir);
|
||||
|
||||
// Check if file lpPath exists.
|
||||
bool FindFirstFileExists(LPCWSTR lpPath, DWORD dwFilter);
|
||||
|
||||
bool MyGetFileVersionInfo(const std::wstring &filePath, Version &version);
|
||||
|
||||
HRESULT MySHGetFolderPath(int csidl, std::wstring &out);
|
||||
|
||||
void MyPathAppend(std::wstring &filePath, const std::wstring &more);
|
||||
|
||||
void MyPathAddBackslash(std::wstring &filePath);
|
||||
|
||||
LSTATUS MyGetTempFile(const std::wstring &prefixString, const std::wstring &ext, std::wstring &out);
|
||||
|
||||
void MyAppendPathToCommandLine(std::wstring &commandLine, const std::wstring &path);
|
||||
@@ -1,25 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ 生成的包含文件。
|
||||
// 供 HMCL.rc 使用
|
||||
//
|
||||
#define IDC_MYICON 2
|
||||
#define IDD_HMCL_DIALOG 102
|
||||
#define IDS_APP_TITLE 103
|
||||
#define IDM_ABOUT 104
|
||||
#define IDI_HMCL 107
|
||||
#define IDC_HMCL 109
|
||||
#define IDR_MAINFRAME 128
|
||||
#define ID_SCRIPT_DOWNLOAD_JAVA 160
|
||||
#define IDC_STATIC -1
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NO_MFC 1
|
||||
#define _APS_NEXT_RESOURCE_VALUE 129
|
||||
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 110
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
// stdafx.cpp : 只包括标准包含文件的源文件
|
||||
// HMCL.pch 将作为预编译标头
|
||||
// stdafx.obj 将包含预编译类型信息
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: 在 STDAFX.H 中引用任何所需的附加头文件,
|
||||
//而不是在此文件中引用
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Windows 7
|
||||
|
||||
#define WINVER 0x0601
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27428.2005
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HMCL", "HMCL\HMCL.vcxproj", "{672B1019-E741-4C0D-A986-627E2ACE157B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{672B1019-E741-4C0D-A986-627E2ACE157B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{672B1019-E741-4C0D-A986-627E2ACE157B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{672B1019-E741-4C0D-A986-627E2ACE157B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{672B1019-E741-4C0D-A986-627E2ACE157B}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FE40055B-673D-42F5-8AE4-6DF2C87EB659}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+1
-1
@@ -55,7 +55,7 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.create("checkTranslations") {
|
||||
tasks.register("checkTranslations") {
|
||||
doLast {
|
||||
val hmclLangDir = file("HMCL/src/main/resources/assets/lang")
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
networkTimeout=120000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ if (!jfxInClasspath && JavaVersion.current() >= JavaVersion.VERSION_11) {
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.tasks.create("generateOpenJFXDependencies") {
|
||||
rootProject.tasks.register("generateOpenJFXDependencies") {
|
||||
doLast {
|
||||
val jfxDependencies = jfxPlatforms.associate { platform ->
|
||||
platform.name to jfxModules.map { module ->
|
||||
@@ -99,7 +99,7 @@ rootProject.tasks.create("generateOpenJFXDependencies") {
|
||||
}
|
||||
|
||||
// Ensure that the mirror repository caches files
|
||||
rootProject.tasks.create("preTouchOpenJFXDependencies") {
|
||||
rootProject.tasks.register("preTouchOpenJFXDependencies") {
|
||||
doLast {
|
||||
for (repo in jfxMirrorRepos) {
|
||||
for (platform in jfxPlatforms) {
|
||||
|
||||
Reference in new issue
Block a user