Compare commits

...
55 Commits
Author SHA1 Message Date
huanghongxun d28723916d fix(multiplayer): incorrect cato arguments. 2022-01-24 01:45:23 +08:00
Glavo 65d4c9c8ee Fix build on JDK without JavaFX 2022-01-23 19:47:26 +08:00
Glavo 3a1f7a13a1 Add a new GameJavaVersion 'java-runtime-beta' 2022-01-23 19:47:07 +08:00
Glavo 69d97a508c Add JavaVersionConstraint rules 2022-01-23 19:47:07 +08:00
Glavo e7dc23ae26 fix: Cato exit code is not displayed normally 2022-01-23 19:36:31 +08:00
huanghongxun 4cffe30910 fix: test build 2022-01-23 15:06:22 +08:00
NekoCaffeine 6f0dfba4aa fix updateSkin 2022-01-23 10:01:54 +08:00
NekoCaffeine 6d617216c7 fix #1130 2022-01-23 10:01:54 +08:00
kitefly 13d5e5e6af Update MultiplayerManager.java
更新cato版本;将String[] commands = new String[]{exe.toString(), "-auth.token", StringUtils.isBlank(token) ? "new" : token};改成String[] commands = new String[]{exe.toString(), "-auth.token", token};原因是新版cato已经取消这个now传值了
2022-01-23 09:59:19 +08:00
kitefly c697413554 Update I18N_zh_CN.properties 2022-01-23 09:55:42 +08:00
kitefly 994ae3e2f5 Update I18N_zh_CN.properties 2022-01-23 09:55:42 +08:00
kitefly 69ab292394 Update I18N_zh_CN.properties 2022-01-23 09:55:42 +08:00
kitefly 1985d31a07 Update I18N_zh_CN.properties
根据@huanghongxun 的说法修改
2022-01-23 09:55:42 +08:00
kitefly b813185dc3 Update I18N_zh_CN.properties 2022-01-23 09:55:42 +08:00
kitefly 6daf04c9fc Update I18N_zh_CN.properties
决定删除:<b>登录后请勿将同HMCL目录下的hmcl.json文件开放给他人,否则可能存在文件里的token(不是明文密码)会泄露账号(仅限微软账号)</b>
并添加:若发现账号头像是默认头像、进入游戏看到是默认皮肤且无法进入开启正版认证的服务器,则可能需要重新登录微软账号
2022-01-23 09:55:42 +08:00
kitefly fd49ec4f97 Update I18N_zh_CN.properties
添加
<b>你需要在新打开的网页窗口中按照提示完成登录</b> 设备代码为:<b>%1$s</b>,已拷贝至剪切板中,若无法粘贴,可点击此蓝方框再次拷贝。若网页未能打开,请手动在网页浏览器中打开:%2$s <b>登录后请勿将同HMCL目录下的hmcl.json文件开放给他人,否则可能存在文件里的token(不是明文密码)会泄露账号(仅限微软账号)、

讨论:是否添加?:登录后请勿将同HMCL目录下的hmcl.json文件开放给他人,否则可能存在文件里的token(不是明文密码)会泄露账号(仅限微软账号)
2022-01-23 09:55:42 +08:00
kitefly 84a4921629 Update I18N_zh_CN.properties 2022-01-23 09:55:42 +08:00
Glavo b4aeaff783 fix preTouchOpenJFXDependencies 2022-01-23 09:55:03 +08:00
Glavo caf73b89ea Fix #1196: Use OpenJFX 17.0.2 2022-01-23 09:55:03 +08:00
Glavo 1c406557a4 Allow different platforms to use different versions of OpenJFX 2022-01-23 09:55:03 +08:00
Glavo d39eac9512 Replace static fields with instance fields 2022-01-23 09:55:03 +08:00
Glavo 2530d31d58 Clean up build scripts 2022-01-23 09:55:03 +08:00
Glavo c6894a1e95 Modify the path where openjfx is placed 2022-01-23 09:55:03 +08:00
Glavo 05fdea3267 Improve the format of openjfx-dependencies.json 2022-01-23 09:55:03 +08:00
Glavo e0f7d9cd32 Generate 'openjfx-dependencies.json' at build time 2022-01-23 09:55:03 +08:00
asdqp233 91b9fe23cc Update Japanese translation
Update and fix Japanese translation
2022-01-23 09:47:35 +08:00
kitefly 4e4e2907d9 Update I18N_zh_CN.properties
添加一些小提示
multiplayer.exit.timeout.static_token=无法连接多人联机服务,你的凭证可能无法正常工作,你可以使用空凭证再试。\n你可以联系在线管理员询问鉴权服务器是否正常工作,或者耐心等待十分钟。
multiplayer.exit.timeout.dynamic_token=无法连接多人联机服务,你可以在多人联机页面的反馈中反馈问题。\n可能原因:鉴权服务器被人占满,请等待每个半点,鉴权服务器会自动刷新一次。\n没有耐心且有闲钱的可以考虑购买联机凭证,不受鉴权服务器影响。
2022-01-23 09:44:44 +08:00
Glavo b94f54f56d Delete unused submodule 'JSTUN' 2022-01-15 01:48:30 +08:00
Glavo 12932a1d33 Adjust the policy of automatic memory allocation 2022-01-15 01:44:41 +08:00
Glavo 7beef30d20 Set an upper limit of 8G for the suggested memory 2022-01-15 01:44:41 +08:00
Glavo f1e4e3c753 Prevent HMCL from crashing when exiting 2022-01-15 01:43:28 +08:00
Glavo d1ba758c6f fix #1297: export logs without 'Schedulers.io()' 2022-01-15 01:43:11 +08:00
Glavo c0472056ac fix #1287, fix #1284: sort type is null 2022-01-10 19:08:55 +08:00
kitefly 1780c479d5 Update I18N_zh_CN.properties 2022-01-07 21:38:24 +08:00
kitefly 59f8465ece 修复多人联机界面过大的问题
通过修改I18N_zh_CN.properties中的
2022-01-03 14:24:53 +08:00
Glavo 256f72dd08 Fix 'DefaultGameRepository::getVersionJar' 2022-01-02 14:28:32 +08:00
huanghongxun 836c7769f6 fix(skin): when texture file is lost, continue launching game instead of failing. 2022-01-02 01:40:37 +08:00
huanghongxun ebe880bd39 fix(multiplayer): ConcurrentModificationException. Closes #1278. 2022-01-02 01:19:14 +08:00
huanghongxun 10fea79a35 fix: cannot update Forge for Minecraft 1.18 when "patches" lost in game version json. Closes #1262. 2022-01-02 01:08:22 +08:00
huanghongxun 451600448e fix: do not show install failure if it is cancelled by user. 2022-01-02 01:08:22 +08:00
huanghongxun 7fccd6261e fix: not on FX thread. 2022-01-02 01:08:21 +08:00
huanghongxun 2436dab797 fix: load fabric api version list by incorrect game version. Closes #1273. 2022-01-02 01:08:21 +08:00
huanghongxun 56538623a5 fix: sort not work in modrinth. Closes #1274. 2022-01-02 01:08:20 +08:00
YuanYuanOwO 42a667c0ef 修正句子 2022-01-02 00:10:58 +08:00
Glavo 5df95ad0fa Use Gradle Kotlin DSL 2022-01-01 20:59:36 +08:00
Glavo 345598016a Use more hash algorithms to generate checksum files 2021-12-30 23:58:57 +08:00
kitefly 6da38e537b 修改错字
将"室"改成"是"
2021-12-25 14:16:21 +08:00
Glavo e65f664821 Check for illegal characters of the HMCL directory path 2021-12-24 23:02:53 +08:00
Glavo cfa1eedae7 Extract the log4j patch to the HMCL directory 2021-12-24 23:02:53 +08:00
Glavo 130c8d0e89 Add 'javaAgents' to LaunchOptions 2021-12-24 23:02:53 +08:00
Hcat c4a2695b69 Update MultiplayerPage.java
Replace the stun server to solve the problem of slow and inaccurate detection
2021-12-24 17:34:31 +08:00
Glavo 246069cc48 Use ArrayList instead of LinkedList 2021-12-21 22:36:42 +08:00
Glavo 36a7d4a510 Stop syncing to gitee on fork 2021-12-21 22:31:21 +08:00
Glavo 1b4a53f572 Update version string to 3.5 2021-12-21 22:26:02 +08:00
huanghongxun 61a9d80e2c fix: checkstyle. 2021-12-20 01:46:25 +08:00
89 changed files with 1256 additions and 1152 deletions

No files matched your search

+1
View File
@@ -5,6 +5,7 @@ on:
jobs:
run:
if: ${{ github.repository_owner == 'huanghongxun' }}
runs-on: ubuntu-latest
steps:
- name: Mirror Github to Gitee
-3
View File
@@ -1,3 +0,0 @@
[submodule "JSTUN"]
path = JSTUN
url = https://github.com/huanghongxun/JSTUN
-235
View File
@@ -1,235 +0,0 @@
buildscript {
repositories {
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'org.tukaani:xz:1.8'
classpath 'org.glavo:pack200:0.3.0'
}
}
plugins {
id 'application'
id 'com.github.johnrengelman.shadow' version '7.0.0'
}
import java.nio.file.FileSystems
import java.security.KeyFactory
import java.security.MessageDigest
import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.GZIPOutputStream
import java.util.zip.ZipFile
import java.nio.file.Files
import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.XZOutputStream
import org.glavo.pack200.Pack200
def dev = null
def shortcommit = System.getenv("GITHUB_SHA")?.toLowerCase()?.substring(0, 7) ?: null
if (shortcommit != null && !shortcommit.isEmpty()) dev = "dev-" + shortcommit
def buildnumber = System.getenv("BUILD_NUMBER") ?: dev ?: "SNAPSHOT"
if (System.getenv("BUILD_NUMBER") != null && System.getenv("BUILD_NUMBER_OFFSET") != null)
buildnumber = (Integer.parseInt(System.getenv("BUILD_NUMBER")) - Integer.parseInt(System.getenv("BUILD_NUMBER_OFFSET"))).toString()
def versionroot = System.getenv("VERSION_ROOT") ?: "3.4"
def microsoftAuthId = System.getenv("MICROSOFT_AUTH_ID") ?: ""
def microsoftAuthSecret = System.getenv("MICROSOFT_AUTH_SECRET") ?: ""
def versionType = System.getenv("VERSION_TYPE") ?: "nightly"
version = versionroot + '.' + buildnumber
mainClassName = 'org.jackhuang.hmcl.Main'
dependencies {
implementation project(":HMCLCore")
implementation 'libs:JFoenix'
implementation group: 'de.javawi.jstun', name: 'jstun', version: '0.7.4'
}
def digest(String algorithm, byte[] bytes) {
return MessageDigest.getInstance(algorithm).digest(bytes)
}
def createChecksum(File file) {
def algorithm = "SHA-1"
def suffix = "sha1"
new File(file.parentFile, file.name + "." + suffix).text = digest(algorithm, file.bytes).encodeHex().toString() + "\n"
}
def attachSignature(File jar) {
def keyLocation = System.getenv("HMCL_SIGNATURE_KEY");
if (keyLocation == null)
return
def privatekey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(new File(keyLocation).bytes))
def signer = Signature.getInstance("SHA512withRSA")
signer.initSign(privatekey)
new ZipFile(jar).withCloseable { zip ->
zip.stream()
.sorted(Comparator.comparing { it.name })
.filter { it.name != "META-INF/hmcl_signature" }
.forEach {
signer.update(digest("SHA-512", it.name.getBytes("UTF-8")))
signer.update(digest("SHA-512", zip.getInputStream(it).bytes))
}
}
def signature = signer.sign()
FileSystems.newFileSystem(URI.create("jar:" + jar.toURI()), [:]).withCloseable { zipfs ->
Files.newOutputStream(zipfs.getPath("META-INF/hmcl_signature")).withCloseable { it << signature }
}
}
ext.packer = Pack200.newPacker()
packer.properties()["pack.effort"] = "9"
ext.unpacker = Pack200.newUnpacker()
// Pack200 does not guarantee that unpacked .class file is bit-wise same as the .class file before packing
// because of shrinking. So we should pack .class files and unpack it to make sure that after unpacking
// .class files remain the same.
def repack(File file) {
def packed = new ByteArrayOutputStream()
new JarFile(file).withCloseable { packer.pack(it, packed) }
new JarOutputStream(file.newOutputStream()).withCloseable { unpacker.unpack(new ByteArrayInputStream(packed.toByteArray()), it) }
}
sourceSets {
java11 {
java {
srcDirs = ['src/main/java11']
}
}
}
compileJava11Java {
if(JavaVersion.current() < JavaVersion.VERSION_11) {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(11)
}
}
options.compilerArgs.add('--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED')
sourceCompatibility = 11
targetCompatibility = 11
}
jar {
enabled = false
dependsOn shadowJar
}
shadowJar {
classifier = null
minimize {
exclude(dependency('com.google.code.gson:.*:.*'))
exclude(dependency('com.github.steveice10:.*:.*'))
exclude(dependency('libs:JFoenix:.*'))
}
manifest {
attributes 'Created-By': 'Copyright(c) 2013-2021 huangyuhui.',
'Main-Class': mainClassName,
'Multi-Release': 'true',
'Implementation-Version': project.version,
'Microsoft-Auth-Id': microsoftAuthId,
'Microsoft-Auth-Secret': microsoftAuthSecret,
'Build-Channel': versionType,
'Class-Path': 'pack200.jar',
'Add-Opens': [
'java.base/java.lang',
'java.base/java.lang.reflect',
'javafx.graphics/javafx.css',
'javafx.base/com.sun.javafx.runtime',
'javafx.controls/com.sun.javafx.scene.control.behavior',
'javafx.controls/javafx.scene.control.skin',
'javafx.controls/com.sun.javafx.scene.control',
'javafx.base/com.sun.javafx.binding',
'javafx.base/com.sun.javafx.event',
'javafx.graphics/com.sun.javafx.stage'
].join(" "),
'Add-Exports': [
'java.base/jdk.internal.loader',
'javafx.controls/com.sun.javafx.scene.control.behavior',
'javafx.controls/javafx.scene.control.skin',
'javafx.controls/com.sun.javafx.scene.control',
'javafx.base/com.sun.javafx.binding',
'javafx.graphics/com.sun.javafx.stage',
'javafx.base/com.sun.javafx.event'
].join(" ")
}
doLast {
repack(jar.archivePath) // see repack()
attachSignature(jar.archivePath)
createChecksum(jar.archivePath)
}
}
def createExecutable(String suffix, String header) {
def output = new File(jar.archivePath.parentFile, jar.archivePath.name[0..-4] + suffix)
output.bytes = new File(project.projectDir, header).bytes
output << jar.archivePath.bytes
createChecksum(output)
}
processResources {
ext.convertToBSS = { String resource ->
// exclude resource
doFirst {
def cssFile = new File(this.projectDir, "src/main/resources/" + resource)
def bssFile = new File(this.projectDir, "build/compiled-resources/" + resource[0..-4] + "bss")
bssFile.parentFile.mkdirs()
javaexec {
classpath = sourceSets.main.compileClasspath
mainClass = "com.sun.javafx.css.parser.Css2Bin"
args = [cssFile, bssFile]
}
}
}
from "build/compiled-resources"
convertToBSS "assets/css/root.css"
convertToBSS "assets/css/blue.css"
into('META-INF/versions/11') {
from sourceSets.java11.output
}
dependsOn java11Classes
}
task makePack(dependsOn: jar) {
ext.outputPath = new File(jar.archivePath.parentFile, jar.archivePath.name[0..-4] + "pack")
doLast {
outputPath.newOutputStream().withCloseable { out ->
new JarFile(jar.archivePath).withCloseable { jarFile -> packer.pack(jarFile, out) }
}
createChecksum(outputPath)
}
}
task makePackXz(dependsOn: makePack) doLast {
def packXz = new File(makePack.outputPath.parentFile, makePack.outputPath.name + ".xz")
// Our CI server does not have enough memory space to compress file at highest level.
new XZOutputStream(packXz.newOutputStream(), new LZMA2Options(5)).withCloseable { it << makePack.outputPath.bytes }
createChecksum(packXz)
}
task makePackGz(dependsOn: makePack) doLast {
def packGz = new File(makePack.outputPath.parentFile, makePack.outputPath.name + ".gz")
new GZIPOutputStream(packGz.newOutputStream()).withCloseable { it << makePack.outputPath.bytes }
createChecksum(packGz)
}
task makeExecutables(dependsOn: jar) doLast {
createExecutable("exe", "src/main/resources/assets/HMCLauncher.exe")
}
build.dependsOn makePackXz
build.dependsOn makePackGz
build.dependsOn makeExecutables
+278
View File
@@ -0,0 +1,278 @@
import java.nio.file.FileSystems
import java.nio.file.Files
import java.security.KeyFactory
import java.security.MessageDigest
import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.ZipFile
import java.util.zip.GZIPOutputStream
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.net.URI
import org.glavo.pack200.Pack200
import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.XZOutputStream
buildscript {
repositories {
gradlePluginPortal()
maven(url = "https://jitpack.io")
}
dependencies {
classpath("org.tukaani:xz:1.8")
classpath("org.glavo:pack200:0.3.0")
}
}
plugins {
application
id("com.github.johnrengelman.shadow") version "7.0.0"
}
val buildNumber = System.getenv("BUILD_NUMBER")?.toInt().let { number ->
val offset = System.getenv("BUILD_NUMBER_OFFSET")?.toInt() ?: 0
if (number != null) {
(number - offset).toString()
} else {
val shortCommit = System.getenv("GITHUB_SHA")?.toLowerCase()?.substring(0, 7)
if (!shortCommit.isNullOrEmpty()) "dev-$shortCommit" else "SNAPSHOT"
}
}
val versionRoot = System.getenv("VERSION_ROOT") ?: "3.5"
val microsoftAuthId = System.getenv("MICROSOFT_AUTH_ID") ?: ""
val microsoftAuthSecret = System.getenv("MICROSOFT_AUTH_SECRET") ?: ""
val versionType = System.getenv("VERSION_TYPE") ?: "nightly"
version = "$versionRoot.$buildNumber"
application {
mainClass.set("org.jackhuang.hmcl.Main")
}
dependencies {
implementation(project(":HMCLCore"))
implementation("libs:JFoenix")
implementation("de.javawi.jstun:jstun:0.7.4")
}
fun digest(algorithm: String, bytes: ByteArray) = MessageDigest.getInstance(algorithm).digest(bytes)
fun createChecksum(file: File) {
val algorithms = linkedMapOf(
"MD5" to "md5",
"SHA-1" to "sha1",
"SHA-256" to "sha256",
"SHA-512" to "sha512"
)
algorithms.forEach { (algorithm, ext) ->
File(file.parentFile, "${file.name}.$ext").writeText(
digest(algorithm, file.readBytes()).joinToString(separator = "", postfix = "\n") { "%02x".format(it) }
)
}
}
fun attachSignature(jar: File) {
val keyLocation = System.getenv("HMCL_SIGNATURE_KEY") ?: return
val privatekey = KeyFactory.getInstance("RSA").generatePrivate(PKCS8EncodedKeySpec(File(keyLocation).readBytes()))
val signer = Signature.getInstance("SHA512withRSA")
signer.initSign(privatekey)
ZipFile(jar).use { zip ->
zip.stream()
.sorted(Comparator.comparing { it.name })
.filter { it.name != "META-INF/hmcl_signature" }
.forEach {
signer.update(digest("SHA-512", it.name.toByteArray()))
signer.update(digest("SHA-512", zip.getInputStream(it).readBytes()))
}
}
val signature = signer.sign()
FileSystems.newFileSystem(URI.create("jar:" + jar.toURI()), emptyMap<String, Any>()).use { zipfs ->
Files.newOutputStream(zipfs.getPath("META-INF/hmcl_signature")).use { it.write(signature) }
}
}
val packer = Pack200.newPacker().apply {
properties()["pack.effort"] = "9"
}
val unpacker = Pack200.newUnpacker()
// Pack200 does not guarantee that unpacked .class file is bit-wise same as the .class file before packing
// because of shrinking. So we should pack .class files and unpack it to make sure that after unpacking
// .class files remain the same.
fun repack(file: File) {
val packed = ByteArrayOutputStream()
JarFile(file).use { packer.pack(it, packed) }
JarOutputStream(file.outputStream()).use { unpacker.unpack(ByteArrayInputStream(packed.toByteArray()), it) }
}
val java11 = sourceSets.create("java11") {
java {
srcDir("src/main/java11")
}
}
tasks.getByName<JavaCompile>(java11.compileJavaTaskName) {
if (JavaVersion.current() < JavaVersion.VERSION_11) {
javaCompiler.set(javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(11))
})
}
options.compilerArgs.add("--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED")
sourceCompatibility = "11"
targetCompatibility = "11"
}
tasks.jar {
enabled = false
dependsOn(tasks["shadowJar"])
}
val jarPath = tasks.jar.get().archiveFile.get().asFile
tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
archiveClassifier.set(null as String?)
minimize {
exclude(dependency("com.google.code.gson:.*:.*"))
exclude(dependency("com.github.steveice10:.*:.*"))
exclude(dependency("libs:JFoenix:.*"))
}
manifest {
attributes(
"Created-By" to "Copyright(c) 2013-2021 huangyuhui.",
"Main-Class" to application.mainClass.get(),
"Multi-Release" to "true",
"Implementation-Version" to project.version,
"Microsoft-Auth-Id" to microsoftAuthId,
"Microsoft-Auth-Secret" to microsoftAuthSecret,
"Build-Channel" to versionType,
"Class-Path" to "pack200.jar",
"Add-Opens" to listOf(
"java.base/java.lang",
"java.base/java.lang.reflect",
"javafx.graphics/javafx.css",
"javafx.base/com.sun.javafx.runtime",
"javafx.controls/com.sun.javafx.scene.control.behavior",
"javafx.controls/javafx.scene.control.skin",
"javafx.controls/com.sun.javafx.scene.control",
"javafx.base/com.sun.javafx.binding",
"javafx.base/com.sun.javafx.event",
"javafx.graphics/com.sun.javafx.stage"
).joinToString(" "),
"Add-Exports" to listOf(
"java.base/jdk.internal.loader",
"javafx.controls/com.sun.javafx.scene.control.behavior",
"javafx.controls/javafx.scene.control.skin",
"javafx.controls/com.sun.javafx.scene.control",
"javafx.base/com.sun.javafx.binding",
"javafx.graphics/com.sun.javafx.stage",
"javafx.base/com.sun.javafx.event"
).joinToString(" ")
)
}
doLast {
repack(jarPath) // see repack()
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 {
fun convertToBSS(resource: String) {
doFirst {
val cssFile = File(projectDir, "src/main/resources/$resource")
val bssFile = File(projectDir, "build/compiled-resources/${resource.substring(0, resource.length - 4)}.bss")
bssFile.parentFile.mkdirs()
javaexec {
classpath = sourceSets["main"].compileClasspath
mainClass.set("com.sun.javafx.css.parser.Css2Bin")
args(cssFile, bssFile)
}
}
}
from("build/compiled-resources")
convertToBSS("assets/css/root.css")
convertToBSS("assets/css/blue.css")
into("META-INF/versions/11") {
from(sourceSets["java11"].output)
}
dependsOn(tasks["java11Classes"])
into("assets") {
from(project.buildDir.resolve("openjfx-dependencies.json"))
}
dependsOn(rootProject.tasks["generateOpenJFXDependencies"])
}
val packFile = File(jarPath.parentFile, jarPath.nameWithoutExtension + ".pack")
val makePack = tasks.create("makePack") {
dependsOn(tasks.jar)
doLast {
packFile.outputStream().use { out ->
JarFile(jarPath).use { jarFile -> packer.pack(jarFile, out) }
}
createChecksum(packFile)
}
}
val makePackXz = tasks.create("makePackXz") {
dependsOn(makePack)
val packXz = File(packFile.parentFile, packFile.name + ".xz")
doLast {
// Our CI server does not have enough memory space to compress file at highest level.
XZOutputStream(packXz.outputStream(), LZMA2Options(5))
.use { it.write(packFile.readBytes()) }
createChecksum(packXz)
}
}
val makePackGz = tasks.create("makePackGz") {
dependsOn(makePack)
val packGz = File(packFile.parentFile, packFile.name + ".gz")
doLast {
GZIPOutputStream(packGz.outputStream()).use { it.write(packFile.readBytes()) }
createChecksum(packGz)
}
}
val makeExecutables = tasks.create("makeExecutables") {
dependsOn(makePack)
doLast {
createExecutable("exe", "src/main/resources/assets/HMCLauncher.exe")
}
}
tasks.build {
dependsOn(makePackXz, makePackGz, makeExecutables)
}
@@ -3,8 +3,8 @@ package moe.mickey.minecraft.skin.fx;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class SkinAnimation {
@@ -12,9 +12,8 @@ public class SkinAnimation {
protected int weight, left;
protected List<SkinTransition> transitions;
@Deprecated
public SkinAnimation() {
this.transitions = new LinkedList<>();
this.transitions = new ArrayList<>();
}
public SkinAnimation(int weight, SkinTransition... transitions) {
@@ -2,14 +2,15 @@ package moe.mickey.minecraft.skin.fx;
import javafx.animation.AnimationTimer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class SkinAnimationPlayer {
protected final Random random = new Random();
protected LinkedList<SkinAnimation> animations = new LinkedList<>();
protected List<SkinAnimation> animations = new ArrayList<>();
protected SkinAnimation playing;
protected boolean running;
protected int weightedSum = 0;
@@ -28,7 +29,7 @@ public class SkinAnimationPlayer {
}
playing = tmp;
if (playing == null && animations.size() > 0)
playing = animations.getLast();
playing = animations.get(animations.size() - 1);
if (playing != null) {
playing.playFromStart();
lastPlayTime = now;
@@ -4,6 +4,7 @@ import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Shape3D;
@@ -11,6 +12,8 @@ import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import org.jetbrains.annotations.Nullable;
public class SkinCanvas extends Group {
public static final Image ALEX = new Image(SkinCanvas.class.getResourceAsStream("/assets/img/alex.png"));
@@ -22,7 +25,7 @@ public class SkinCanvas extends Group {
public static final SkinCube STEVEN_LARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false);
public static final SkinCube STEVEN_RARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false);
protected Image srcSkin, skin;
protected Image srcSkin, skin, srcCape, cape;
protected boolean isSlim;
protected double preW, preH;
@@ -45,6 +48,8 @@ public class SkinCanvas extends Group {
public final SkinCube llegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 16F / 64F, 48F / 64F, 0F, false);
public final SkinCube rlegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 0F, 16F / 64F, 0F, false);
public final SkinCube capeCube = new SkinCube(10, 16, 1, 22F / 64F, 17F / 32F, 0F, 0F, 0F, false);
public final SkinGroup head = new SkinGroup(
new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS),
@@ -82,6 +87,13 @@ public class SkinCanvas extends Group {
rlegOuter, rlegInside
);
public final SkinGroup capeGroup = new SkinGroup(
new Rotate(0, 0, -capeCube.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, Rotate.Z_AXIS),
capeCube
);
protected PerspectiveCamera camera = new PerspectiveCamera(true);
protected Rotate xRotate = new Rotate(0, Rotate.X_AXIS);
@@ -90,10 +102,10 @@ public class SkinCanvas extends Group {
protected Translate translate = new Translate(0, 0, -80);
protected Scale scale = new Scale(1, 1);
protected SkinAnimationPlayer animationplayer = new SkinAnimationPlayer();
protected SkinAnimationPlayer animationPlayer = new SkinAnimationPlayer();
public SkinAnimationPlayer getAnimationplayer() {
return animationplayer;
public SkinAnimationPlayer getAnimationPlayer() {
return animationPlayer;
}
public Image getSrcSkin() {
@@ -104,20 +116,21 @@ public class SkinCanvas extends Group {
return skin;
}
public void updateSkin(Image skin, boolean isSlim) {
public void updateSkin(Image skin, boolean isSlim, final @Nullable Image cape) {
if (SkinHelper.isNoRequest(skin) && SkinHelper.isSkin(skin)) {
this.srcSkin = skin;
this.skin = SkinHelper.x32Tox64(skin);
this.srcCape = cape;
this.cape = cape == null ? null : cape.getWidth() < 256 ? SkinHelper.enlarge(cape, 4, 8) : cape;
int multiple = Math.max((int) (1024 / skin.getWidth()), 1);
if (multiple > 1)
this.skin = SkinHelper.enlarge(this.skin, multiple);
if (this.isSlim != isSlim)
updateSkinModel(isSlim);
this.skin = SkinHelper.enlarge(this.skin, multiple, multiple);
updateSkinModel(isSlim, cape != null);
bindMaterial(root);
}
}
protected void updateSkinModel(boolean isSlim) {
protected void updateSkinModel(boolean isSlim, boolean hasCape) {
this.isSlim = isSlim;
FunctionHelper.alwaysB(SkinMultipleCubes::setWidth, isSlim ? 3 : 4, larmOuter, rarmOuter);
FunctionHelper.alwaysB(SkinCube::setWidth, isSlim ? 3D : 4D, larmInside, rarmInside);
@@ -134,6 +147,8 @@ public class SkinCanvas extends Group {
larm.getZRotate().setPivotX(-larmInside.getWidth() / 2);
rarm.getZRotate().setPivotX(+rarmInside.getWidth() / 2);
capeGroup.setVisible(hasCape);
}
public SkinCanvas(double preW, double preH) {
@@ -149,17 +164,17 @@ public class SkinCanvas extends Group {
init();
}
protected Material createMaterial() {
protected Material createMaterial(final Image image) {
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(skin);
material.setDiffuseMap(image);
return material;
}
protected void bindMaterial(Group group) {
Material material = createMaterial();
Material material = createMaterial(skin);
for (Node node : group.getChildren())
if (node instanceof Shape3D)
((Shape3D) node).setMaterial(material);
((Shape3D) node).setMaterial(node == capeCube ? createMaterial(cape) : material);
else if (node instanceof SkinMultipleCubes)
((SkinMultipleCubes) node).updateSkin(skin);
else if (node instanceof Group)
@@ -178,6 +193,11 @@ public class SkinCanvas extends Group {
lleg.setTranslateY(+(bodyInside.getHeight() + llegInside.getHeight()) / 2);
rleg.setTranslateY(+(bodyInside.getHeight() + rlegInside.getHeight()) / 2);
capeGroup.setTranslateY(+(capeCube.getHeight() - bodyOuter.getHeight()) / 2);
capeGroup.setTranslateZ(-(bodyInside.getDepth() + bodyOuter.getDepth()) / 2);
capeGroup.getTransforms().addAll(new Rotate(180, Rotate.Y_AXIS), new Rotate(10, Rotate.X_AXIS));
root.getTransforms().addAll(xRotate);
root.getChildren().addAll(
@@ -186,15 +206,20 @@ public class SkinCanvas extends Group {
larm,
rarm,
lleg,
rleg
rleg,
capeGroup
);
updateSkin(skin, false);
updateSkin(skin, false, null);
return root;
}
protected SubScene createSubScene() {
Group group = new Group();
AmbientLight light = new AmbientLight(Color.WHITE);
group.getChildren().add(light);
group.getChildren().add(createPlayerModel());
group.getTransforms().add(zRotate);
@@ -1,9 +1,9 @@
package moe.mickey.minecraft.skin.fx;
import javafx.scene.image.Image;
import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import org.apache.commons.lang3.ArrayUtils;
public class SkinCube extends MeshView {
@@ -20,61 +20,60 @@ public class SkinCube extends MeshView {
width /= 2F;
height /= 2F;
depth /= 2F;
return new float[]{
-width, -height, depth, // P0
width, -height, depth, // P1
-width, height, depth, // P2
width, height, depth, // P3
-width, -height, -depth, // P4
width, -height, -depth, // P5
-width, height, -depth, // P6
width, height, -depth // P7
-width, -height, +depth, // P0
+width, -height, +depth, // P1
-width, +height, +depth, // P2
+width, +height, +depth, // P3
-width, -height, -depth, // P4
+width, -height, -depth, // P5
-width, +height, -depth, // P6
+width, +height, -depth // P7
};
}
public static float[] createTexCoords(float width, float height, float depth, float scaleX, float scaleY,
float startX, float startY, boolean isSlim) {
float startX, float startY, boolean isSlim) {
float x = (width + depth) * 2, y = height + depth, half_width = width / x * scaleX, half_depth = depth / x * scaleX,
top_x = depth / x * scaleX + startX, top_y = startY, arm4 = isSlim ? half_depth : half_width,
bottom_x = startX, middle_y = depth / y * scaleY + top_y, bottom_y = scaleY + top_y;
return new float[]{
top_x, top_y, // T0 ---
top_x + half_width, top_y, // T1 |
top_x + half_width * 2, top_y, // T2 ---
bottom_x, middle_y, // T3 ---
bottom_x + half_depth, middle_y, // T4 |
top_x + half_width, top_y, // T1 |
top_x + half_width * 2, top_y, // T2 ---
bottom_x, middle_y, // T3 ---
bottom_x + half_depth, middle_y, // T4 |
bottom_x + half_depth + half_width, middle_y, // T5 |
bottom_x + scaleX - arm4, middle_y, // T6 |
bottom_x + scaleX, middle_y, // T7 ---
bottom_x, bottom_y, // T8 ---
bottom_x + half_depth, bottom_y, // T9 |
bottom_x + scaleX - arm4, middle_y, // T6 |
bottom_x + scaleX, middle_y, // T7 ---
bottom_x, bottom_y, // T8 ---
bottom_x + half_depth, bottom_y, // T9 |
bottom_x + half_depth + half_width, bottom_y, // T10 |
bottom_x + scaleX - arm4, bottom_y, // T11 |
bottom_x + scaleX, bottom_y // T12 ---
bottom_x + scaleX - arm4, bottom_y, // T11 |
bottom_x + scaleX, bottom_y // T12 ---
};
}
public static int[] createFaces() {
int[] faces = new int[]{
int[] faces = {
// TOP
5, 0, 4, 1, 0, 5, //P5,T0, P4,T1, P0,T5
5, 0, 0, 5, 1, 4, //P5,T0, P0,T5, P1,T4
// RIGHT
0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8
0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9
0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8
0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9
// FRONT
1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9
1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10
1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9
1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10
// LEFT
5, 3, 1, 4, 3, 9, //P5,T6, P1,T5, P3,T10
5, 3, 3, 9, 7, 8, //P5,T6, P3,T10,P7,T11
// BACK
4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12
4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11
4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12
4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11
// BOTTOM
3, 5, 2, 6, 6, 2, //P3,T2, P2,T1, P6,T5
3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6
3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6
};
int[] copy = faces.clone();
@@ -91,7 +90,6 @@ public class SkinCube extends MeshView {
private double width, height, depth;
private boolean isSlim;
private Image skin;
private Mesh model;
public SkinCube(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, float enlarge, boolean isSlim) {
@@ -10,12 +10,12 @@ public final class SkinHelper {
private SkinHelper() {
}
public static class PixelCopyer {
public static class PixelCopier {
protected Image srcImage;
protected WritableImage newImage;
public PixelCopyer(Image srcImage, WritableImage newImage) {
public PixelCopier(Image srcImage, WritableImage newImage) {
this.srcImage = srcImage;
this.newImage = newImage;
}
@@ -70,53 +70,53 @@ public final class SkinHelper {
}
public static Image x32Tox64(Image srcSkin) {
if (srcSkin.getHeight() == 64)
if (srcSkin.getHeight() == srcSkin.getWidth())
return srcSkin;
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth(), (int) srcSkin.getHeight() * 2);
PixelCopyer copyer = new PixelCopyer(srcSkin, newSkin);
PixelCopier copier = new PixelCopier(srcSkin, newSkin);
// HEAD & HAT
copyer.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F);
copier.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F);
// LEFT-LEG
x32Tox64(copyer, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
x32Tox64(copier, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-LEG
copyer.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
copier.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
// BODY
copyer.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
copier.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
// LEFT-ARM
x32Tox64(copyer, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
x32Tox64(copier, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-ARM
copyer.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
copier.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
return newSkin;
}
static void x32Tox64(PixelCopyer copyer, float srcX, float srcY, float toX, float toY, float width, float height, float depth) {
static void x32Tox64(PixelCopier copier, float srcX, float srcY, float toX, float toY, float width, float height, float depth) {
// TOP
copyer.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false);
copier.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false);
// BOTTOM
copyer.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false);
copier.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false);
// INS
copyer.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false);
copier.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false);
// OUTS
copyer.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false);
copier.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false);
// FRONT
copyer.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false);
copier.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false);
// BACK
copyer.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false);
copier.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false);
}
public static Image enlarge(Image srcSkin, int multiple) {
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multiple, (int) srcSkin.getHeight() * multiple);
public static Image enlarge(Image srcSkin, int multipleX, int multipleY) {
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multipleX, (int) srcSkin.getHeight() * multipleY);
PixelReader reader = srcSkin.getPixelReader();
PixelWriter writer = newSkin.getPixelWriter();
for (int x = 0, lenX = (int) srcSkin.getWidth(); x < lenX; x++)
for (int y = 0, lenY = (int) srcSkin.getHeight(); y < lenY; y++)
for (int mx = 0; mx < multiple; mx++)
for (int my = 0; my < multiple; my++) {
for (int mx = 0; mx < multipleX; mx++)
for (int my = 0; my < multipleY; my++) {
int argb = reader.getArgb(x, y);
writer.setArgb(x * multiple + mx, y * multiple + my, argb);
writer.setArgb(x * multipleX + mx, y * multipleY + my, argb);
}
return newSkin;
@@ -70,6 +70,10 @@ public final class Launcher extends Application {
Main.showErrorAndExit(i18n("fatal.config_loading_failure", Paths.get("").toAbsolutePath().normalize()));
}
if (Metadata.HMCL_DIRECTORY.toAbsolutePath().toString().indexOf('=') >= 0) {
Main.showWarningAndContinue(i18n("fatal.illegal_char"));
}
// runLater to ensure ConfigHolder.init() finished initialization
Platform.runLater(() -> {
// When launcher visibility is set to "hide and reopen" without Platform.implicitExit = false,
@@ -328,7 +328,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
vs.setUsesGlobal(true);
}
public LaunchOptions getLaunchOptions(String version, JavaVersion javaVersion, File gameDir, boolean makeLaunchScript) {
public LaunchOptions getLaunchOptions(String version, JavaVersion javaVersion, File gameDir, List<String> javaAgents, boolean makeLaunchScript) {
VersionSetting vs = getVersionSetting(version);
LaunchOptions.Builder builder = new LaunchOptions.Builder()
@@ -359,7 +359,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
.setProcessPriority(vs.getProcessPriority())
.setUseNativeGLFW(vs.isUseNativeGLFW())
.setUseNativeOpenAL(vs.isUseNativeOpenAL())
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon());
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon())
.setJavaAgents(javaAgents);
if (config().hasProxy()) {
builder.setProxy(ProxyManager.getProxy());
if (config().hasProxyAuth()) {
@@ -453,6 +454,20 @@ public class HMCLGameRepository extends DefaultGameRepository {
}
public static long getAllocatedMemory(long minimum, long available, boolean auto) {
return auto ? Math.max(minimum, (long) (available * 0.8)) : minimum;
if (auto) {
available -= 256 * 1024 * 1024; // Reserve 256MB memory for off-heap memory and HMCL itself
if (available <= 0) {
return minimum;
}
final long threshold = 8L * 1024 * 1024 * 1024;
final long suggested = Math.min(available <= threshold
? (long) (available * 0.8)
: (long) (threshold * 0.8 + (available - threshold) * 0.2),
32736L * 1024 * 1024); // Limit the maximum suggested memory to ensure that compressed oops are available
return Math.max(minimum, suggested);
} else {
return minimum;
}
}
}
@@ -33,7 +33,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
public final class HMCLModpackInstallTask extends Task<Void> {
@@ -42,8 +42,8 @@ public final class HMCLModpackInstallTask extends Task<Void> {
private final HMCLGameRepository repository;
private final DefaultDependencyManager dependency;
private final Modpack modpack;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
private final List<Task<?>> dependents = new ArrayList<>(4);
public HMCLModpackInstallTask(Profile profile, File zipFile, Modpack modpack, String name) {
dependency = profile.getDependency();
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.game;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
@@ -55,9 +56,13 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -124,9 +129,12 @@ public final class LauncherHelper {
Optional<String> gameVersion = repository.getGameVersion(version);
boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion);
CountDownLatch launchingLatch = new CountDownLatch(1);
List<String> javaAgents = new ArrayList<>(0);
AtomicReference<JavaVersion> javaVersionRef = new AtomicReference<>();
getLog4jPatch(version).ifPresent(javaAgents::add);
TaskExecutor executor = checkGameState(profile, setting, version)
.thenComposeAsync(javaVersion -> {
javaVersionRef.set(Objects.requireNonNull(javaVersion));
@@ -172,7 +180,7 @@ public final class LauncherHelper {
}
}).withStage("launch.state.logging_in"))
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir(), scriptFile != null);
LaunchOptions launchOptions = repository.getLaunchOptions(selectedVersion, javaVersionRef.get(), profile.getGameDir(), javaAgents, scriptFile != null);
return new HMCLGameLauncher(
repository,
version,
@@ -339,12 +347,27 @@ public final class LauncherHelper {
JavaVersionConstraint.VersionRanges range = JavaVersionConstraint.findSuitableJavaVersionRange(gameVersion, version);
GameJavaVersion targetJavaVersion;
if (range.getMandatory().contains(VersionNumber.asVersion("16"))) {
if (range.getMandatory().contains(VersionNumber.asVersion("17.0.1"))) {
targetJavaVersion = GameJavaVersion.JAVA_17;
} else if (range.getMandatory().contains(VersionNumber.asVersion("16.0.1"))) {
targetJavaVersion = GameJavaVersion.JAVA_16;
} else if (range.getMandatory().contains(VersionNumber.asVersion("1.8.0_51"))) {
targetJavaVersion = GameJavaVersion.JAVA_8;
} else {
targetJavaVersion = null;
String java8Version;
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
java8Version = "1.8.0_51";
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
java8Version = "1.8.0_202";
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
java8Version = "1.8.0_74";
} else {
java8Version = null;
}
if (java8Version != null && range.getMandatory().contains(VersionNumber.asVersion(java8Version)))
targetJavaVersion = GameJavaVersion.JAVA_8;
else
targetJavaVersion = null;
}
if (targetJavaVersion != null) {
@@ -413,12 +436,11 @@ public final class LauncherHelper {
.thenAcceptAsync(downloadedJavaVersion -> {
setting.setJavaVersion(downloadedJavaVersion);
future.complete(downloadedJavaVersion);
})
.exceptionally(throwable -> {
}, Schedulers.javafx())
.whenCompleteAsync((result, throwable) -> {
LOG.log(Level.WARNING, "Failed to download java", throwable);
breakAction.run();
return null;
});
}, Schedulers.javafx());
return Task.fromCompletableFuture(future);
case VANILLA_JAVA_16:
Controllers.confirm(i18n("launch.advice.require_newer_java_version", gameVersion.toString(), 16), i18n("message.warning"), () -> {
@@ -486,9 +508,15 @@ public final class LauncherHelper {
case MODDED_JAVA_8:
Controllers.dialog(i18n("launch.advice.newer_java"), i18n("message.warning"), MessageType.WARNING, continueAction);
break;
case MODDED_JAVA_16:
Controllers.dialog(i18n("launch.advice.forge37_0_60"), i18n("message.warning"), MessageType.WARNING, continueAction);
break;
case VANILLA_JAVA_8_51:
Controllers.dialog(i18n("launch.advice.java8_51_1_13"), i18n("message.warning"), MessageType.WARNING, continueAction);
break;
case MODLAUNCHER_8:
Controllers.dialog(i18n("launch.advice.modlauncher8"), i18n("message.warning"), MessageType.WARNING, continueAction);
break;
}
}
@@ -598,6 +626,47 @@ public final class LauncherHelper {
return future;
}
private static Optional<String> getLog4jPatch(Version version) {
Optional<String> log4jVersion = version.getLibraries().stream()
.filter(it -> it.is("org.apache.logging.log4j", "log4j-core")
&& (VersionNumber.VERSION_COMPARATOR.compare(it.getVersion(), "2.17") < 0 || "2.0-beta9".equals(it.getVersion())))
.map(Library::getVersion)
.findFirst();
if (log4jVersion.isPresent()) {
final String agentFileName = "log4j-patch-agent-1.0.jar";
Path agentFile = Metadata.HMCL_DIRECTORY.resolve(agentFileName).toAbsolutePath();
String agentFilePath = agentFile.toString();
if (agentFilePath.indexOf('=') >= 0) {
LOG.warning("Invalid character '=' in the HMCL directory path, unable to attach log4j-patch");
return Optional.empty();
}
if (Files.notExists(agentFile)) {
try (InputStream input = DefaultLauncher.class.getResourceAsStream("/assets/game/" + agentFileName)) {
LOG.info("Extract log4j patch to " + agentFilePath);
Files.createDirectories(agentFile.getParent());
Files.copy(input, agentFile, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to extract log4j patch");
try {
Files.deleteIfExists(agentFile);
} catch (IOException ex) {
LOG.log(Level.WARNING, "Failed to delete incomplete log4j patch", ex);
}
return Optional.empty();
}
}
boolean isBeta = log4jVersion.get().startsWith("2.0-beta");
return Optional.of(agentFilePath + "=" + isBeta);
} else {
LOG.info("No log4j with security vulnerabilities found");
return Optional.empty();
}
}
private void checkExit() {
switch (launcherVisibility) {
case HIDE_AND_REOPEN:
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.Zipper;
@@ -77,6 +76,6 @@ public final class LogExporter {
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, Schedulers.io());
});
}
}
@@ -26,8 +26,8 @@ import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -204,7 +204,7 @@ public final class Profiles {
return selectedVersion.get();
}
private static final List<Consumer<Profile>> versionsListeners = new LinkedList<>();
private static final List<Consumer<Profile>> versionsListeners = new ArrayList<>(4);
public static void registerVersionsListener(Consumer<Profile> listener) {
Profile profile = getSelectedProfile();
@@ -76,7 +76,7 @@ public class OfflineAccountSkinPane extends StackPane {
canvasPane.setPrefWidth(300);
canvasPane.setPrefHeight(300);
pane.setCenter(canvas);
canvas.getAnimationplayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
canvas.getAnimationPlayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
canvas.enableRotation(.5);
canvas.addEventHandler(DragEvent.DRAG_OVER, e -> {
@@ -136,11 +136,14 @@ public class OfflineAccountSkinPane extends StackPane {
LOG.log(Level.WARNING, "Failed to load skin", exception);
Controllers.showToast(i18n("message.failed"));
} else {
if (result == null || result.getSkin() == null) {
canvas.updateSkin(getDefaultTexture(), isDefaultSlim());
if (result == null || result.getSkin() == null && result.getCape() == null) {
canvas.updateSkin(getDefaultTexture(), isDefaultSlim(), null);
return;
}
canvas.updateSkin(new Image(result.getSkin().getInputStream()), result.getModel() == TextureModel.ALEX);
canvas.updateSkin(
result.getSkin() != null ? new Image(result.getSkin().getInputStream()) : getDefaultTexture(),
result.getModel() == TextureModel.ALEX,
result.getCape() != null ? new Image(result.getCape().getInputStream()) : null);
}
}).start();
}, skinItem.selectedDataProperty(), cslApiField.textProperty(), skinSelector.valueProperty(), capeSelector.valueProperty());
@@ -41,6 +41,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.zip.ZipException;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -173,6 +174,8 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
} else if (exception instanceof VersionMismatchException) {
VersionMismatchException e = ((VersionMismatchException) exception);
Controllers.dialog(i18n("install.failed.version_mismatch", e.getExpect(), e.getActual()), i18n("install.failed"), MessageDialogPane.MessageType.ERROR, next);
} else if (exception instanceof CancellationException) {
// Ignore cancel
} else {
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageDialogPane.MessageType.ERROR, next);
}
@@ -35,7 +35,7 @@ import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -147,7 +147,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
@FXML
private void onNext() {
LinkedList<String> list = new LinkedList<>();
ArrayList<String> list = new ArrayList<>();
getFilesNeeded(rootNode, "minecraft", list);
controller.getSettings().put(MODPACK_FILE_SELECTION, list);
controller.onFinish();
@@ -54,11 +54,11 @@ public class MultiplayerClient extends Thread {
setDaemon(true);
}
public void setGamePort(int gamePort) {
public synchronized void setGamePort(int gamePort) {
this.gamePort = gamePort;
}
public int getGamePort() {
public synchronized int getGamePort() {
return gamePort;
}
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.multiplayer;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import javafx.application.Platform;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventManager;
@@ -52,17 +53,15 @@ import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Lang.wrap;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
/**
* Cato Management.
*/
public final class MultiplayerManager {
static final String CATO_VERSION = "1.2.0-202112171527";
private static final String CATO_DOWNLOAD_URL = "https://gitcode.net/huanghongxun1/ioi_bin/-/raw/3.5.3/client/";
static final String CATO_VERSION = "1.2.1-1642413526";
private static final String CATO_DOWNLOAD_URL = "https://gitcode.net/to/cato/-/raw/master/client/";
private static final String CATO_HASH_URL = CATO_DOWNLOAD_URL + "cato-all-files.sha1";
private static final String CATO_PATH = getCatoPath();
public static final int CATO_AGREEMENT_VERSION = 2;
@@ -134,7 +133,9 @@ public final class MultiplayerManager {
throw e;
}
String[] commands = new String[]{exe.toString(), "-client.token", StringUtils.isBlank(token) ? "new" : token};
String[] commands = StringUtils.isBlank(token)
? new String[]{exe.toString()}
: new String[]{exe.toString(), "-auth.token", token};
Process process = new ProcessBuilder()
.command(commands)
.start();
@@ -177,7 +178,7 @@ public final class MultiplayerManager {
session.setClient(client);
TimerTask task = Lang.setTimeout(() -> {
future.completeExceptionally(new JoinRequestTimeoutException());
Platform.runLater(() -> future.completeExceptionally(new JoinRequestTimeoutException()));
session.stop();
}, 30 * 1000);
@@ -188,24 +189,30 @@ public final class MultiplayerManager {
session.addRelatedThread(Lang.thread(new LocalServerBroadcaster(port, session), "LocalServerBroadcaster", true));
session.setName(connectedEvent.getSessionName());
client.setGamePort(port);
session.onExit.unregister(onExit);
future.complete(session);
Platform.runLater(() -> {
session.onExit.unregister(onExit);
future.complete(session);
});
} catch (IOException e) {
future.completeExceptionally(e);
session.stop();
Platform.runLater(() -> future.completeExceptionally(e));
}
task.cancel();
});
client.onKicked().register(kickedEvent -> {
future.completeExceptionally(new KickedException(kickedEvent.getReason()));
session.stop();
task.cancel();
Platform.runLater(() -> {
future.completeExceptionally(new KickedException(kickedEvent.getReason()));
});
});
client.onDisconnected().register(disconnectedEvent -> {
if (!client.isConnected()) {
// We fail to establish connection with server
future.completeExceptionally(new ConnectionErrorException());
}
Platform.runLater(() -> {
if (!client.isConnected()) {
// We fail to establish connection with server
future.completeExceptionally(new ConnectionErrorException());
}
});
});
client.onHandshake().register(handshakeEvent -> {
if (handler != null) {
@@ -255,9 +262,11 @@ public final class MultiplayerManager {
}, 15 * 1000);
session.onPeerConnected.register(event -> {
session.onExit.unregister(onExit);
future.complete(session);
peerConnectionTimeoutTask.cancel();
Platform.runLater(() -> {
session.onExit.unregister(onExit);
future.complete(session);
});
});
return future;
@@ -355,11 +364,11 @@ public final class MultiplayerManager {
writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8));
}
public MultiplayerClient getClient() {
public synchronized MultiplayerClient getClient() {
return client;
}
public CatoSession setClient(MultiplayerClient client) {
public synchronized CatoSession setClient(MultiplayerClient client) {
this.client = client;
return this;
}
@@ -414,11 +423,11 @@ public final class MultiplayerManager {
return id != null;
}
public String getName() {
public synchronized String getName() {
return name;
}
public void setName(String name) {
public synchronized void setName(String name) {
this.name = name;
}
@@ -134,7 +134,7 @@ public class MultiplayerPage extends DecoratorAnimatedPage implements DecoratorP
private void testNAT() {
Task.supplyAsync(() -> {
DiscoveryTest tester = new DiscoveryTest(null, 0, "stun.stunprotocol.org", 3478);
DiscoveryTest tester = new DiscoveryTest(null, 0, "stun.miwifi.com", 3478);
return tester.test();
}).whenComplete(Schedulers.javafx(), (info, exception) -> {
if (exception == null) {
@@ -373,10 +373,11 @@ public class MultiplayerPage extends DecoratorAnimatedPage implements DecoratorP
}
} else if (e instanceof MultiplayerManager.CatoExitException) {
LOG.info("Cato exited accidentally");
int exitCode = ((MultiplayerManager.CatoExitException) e).getExitCode();
if (!((MultiplayerManager.CatoExitException) e).isReady()) {
return i18n("multiplayer.exit.before_ready");
return i18n("multiplayer.exit.before_ready", exitCode);
} else {
return i18n("multiplayer.exit.after_ready");
return i18n("multiplayer.exit.after_ready", exitCode);
}
} else if (e instanceof ChecksumMismatchException) {
return i18n("exception.artifact_malformed");
@@ -62,6 +62,7 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -117,7 +118,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
if (!searchInitialized) {
searchInitialized = true;
search("", null, 0, "", 0);
search("", null, 0, "", RemoteModRepository.SortType.DATE_CREATED);
}
if (versionSelection) {
@@ -152,7 +153,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
this.loading.set(loading);
}
public void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, int sort) {
public void search(String userGameVersion, RemoteModRepository.Category category, int pageOffset, String searchFilter, RemoteModRepository.SortType sort) {
retrySearch = null;
setLoading(true);
setFailed(false);
@@ -311,17 +312,12 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
}).start();
StackPane sortStackPane = new StackPane();
JFXComboBox<String> sortComboBox = new JFXComboBox<>();
JFXComboBox<RemoteModRepository.SortType> sortComboBox = new JFXComboBox<>();
sortStackPane.getChildren().setAll(sortComboBox);
sortComboBox.prefWidthProperty().bind(sortStackPane.widthProperty());
sortComboBox.getStyleClass().add("fit-width");
sortComboBox.getItems().setAll(
i18n("curse.sort.date_created"),
i18n("curse.sort.popularity"),
i18n("curse.sort.last_updated"),
i18n("curse.sort.name"),
i18n("curse.sort.author"),
i18n("curse.sort.total_downloads"));
sortComboBox.setConverter(stringConverter(sortType -> i18n("curse.sort." + sortType.name().toLowerCase(Locale.ROOT))));
sortComboBox.getItems().setAll(RemoteModRepository.SortType.values());
sortComboBox.getSelectionModel().select(0);
searchPane.addRow(rowIndex++, new Label(i18n("mods.category")), categoryStackPane, new Label(i18n("search.sort")), sortStackPane);
@@ -345,7 +341,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
.orElse(null),
0,
nameField.getText(),
sortComboBox.getSelectionModel().getSelectedIndex());
sortComboBox.getSelectionModel().getSelectedItem());
searchButton.setOnAction(searchAction);
nameField.setOnAction(searchAction);
gameVersionField.setOnAction(searchAction);
@@ -48,7 +48,7 @@ public class ModDownloadListPage extends DownloadListPage {
private class Repository implements RemoteModRepository {
@Override
public Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort) throws IOException {
public Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, SortType sort) throws IOException {
String newSearchFilter;
if (StringUtils.CHINESE_PATTERN.matcher(searchFilter).find()) {
List<ModTranslations.Mod> mods = ModTranslations.searchMod(searchFilter);
@@ -95,7 +95,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
synchronized (ModListPage.this) {
runInFX(() -> loadingProperty().set(true));
modManager.refreshMods();
return new LinkedList<>(modManager.getMods());
return new ArrayList<>(modManager.getMods());
}
} catch (IOException e) {
throw new UncheckedIOException(e);
@@ -118,10 +118,12 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
List<File> res = chooser.showOpenMultipleDialog(Controllers.getStage());
// It's guaranteed that succeeded and failed are thread safe here.
List<String> succeeded = new LinkedList<>();
List<String> failed = new LinkedList<>();
if (res == null) return;
// It's guaranteed that succeeded and failed are thread safe here.
List<String> succeeded = new ArrayList<>(res.size());
List<String> failed = new ArrayList<>();
Task.runAsync(() -> {
for (File file : res) {
try {
@@ -135,7 +137,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
}
}
}).withRunAsync(Schedulers.javafx(), () -> {
List<String> prompt = new LinkedList<>();
List<String> prompt = new ArrayList<>(1);
if (!succeeded.isEmpty())
prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
if (!failed.isEmpty())
@@ -45,17 +45,13 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.Platform;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -68,117 +64,74 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toSet;
import static org.jackhuang.hmcl.Metadata.HMCL_DIRECTORY;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.SelfDependencyPatcher.DependencyDescriptor.JFX_DEPENDENCIES;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.platform.JavaVersion.CURRENT_JAVA;
// From: https://github.com/Col-E/Recaf/blob/7378b397cee664ae81b7963b0355ef8ff013c3a7/src/main/java/me/coley/recaf/util/self/SelfDependencyPatcher.java
public final class SelfDependencyPatcher {
private SelfDependencyPatcher() {
private final List<DependencyDescriptor> dependencies = DependencyDescriptor.readDependencies();
private final List<Repository> repositories;
private final Repository defaultRepository;
private SelfDependencyPatcher() throws IncompatibleVersionException {
// We can only self-patch JavaFX on specific platform.
if (dependencies == null) {
throw new IncompatibleVersionException();
}
final String customUrl = System.getProperty("hmcl.openjfx.repo");
if (customUrl == null) {
if (System.getProperty("user.country", "").equalsIgnoreCase("CN")) {
defaultRepository = Repository.ALIYUN_MIRROR;
} else {
defaultRepository = Repository.MAVEN_CENTRAL;
}
repositories = Collections.unmodifiableList(Arrays.asList(Repository.MAVEN_CENTRAL, Repository.ALIYUN_MIRROR));
} else {
defaultRepository = new Repository(String.format(i18n("repositories.custom"), customUrl), customUrl);
repositories = Collections.unmodifiableList(Arrays.asList(Repository.MAVEN_CENTRAL, Repository.ALIYUN_MIRROR, defaultRepository));
}
}
static class DependencyDescriptor {
private static final class DependencyDescriptor {
private static final String DEPENDENCIES_LIST_FILE = "/assets/openjfx-dependencies.json";
private static final Path DEPENDENCIES_DIR_PATH = HMCL_DIRECTORY.resolve("dependencies").resolve(Platform.getPlatform().toString()).resolve("openjfx");
private static final Path DEPENDENCIES_DIR_PATH = HMCL_DIRECTORY.resolve("dependencies");
public static final String CURRENT_ARCH_CLASSIFIER = currentArchClassifier();
public static final List<DependencyDescriptor> JFX_DEPENDENCIES = readDependencies();
private static List<DependencyDescriptor> readDependencies() {
String content;
try (InputStream in = SelfDependencyPatcher.class.getResourceAsStream(DEPENDENCIES_LIST_FILE)) {
content = IOUtils.readFullyAsString(in, UTF_8);
static List<DependencyDescriptor> readDependencies() {
//noinspection ConstantConditions
try (Reader reader = new InputStreamReader(SelfDependencyPatcher.class.getResourceAsStream(DEPENDENCIES_LIST_FILE), UTF_8)) {
Map<String, List<DependencyDescriptor>> allDependencies =
new Gson().fromJson(reader, new TypeToken<Map<String, List<DependencyDescriptor>>>(){}.getType());
return allDependencies.get(Platform.getPlatform().toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return new Gson().fromJson(content, TypeToken.getParameterized(List.class, DependencyDescriptor.class).getType());
}
private static String currentArchClassifier() {
if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
switch (Architecture.CURRENT_ARCH) {
case X86_64:
return "linux";
case ARM32:
return "linux-arm32-monocle";
case ARM64:
return "linux-aarch64";
}
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
switch (Architecture.CURRENT_ARCH) {
case X86_64:
return "mac";
case ARM64:
return "mac-aarch64";
}
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
switch (Architecture.CURRENT_ARCH) {
case X86_64:
return "win";
case X86:
return "win-x86";
}
}
return null;
}
public String module;
public String groupId;
public String artifactId;
public String version;
public Map<String, String> sha1;
public String classifier;
public String sha1;
public String filename() {
if (CURRENT_ARCH_CLASSIFIER == null) {
return null;
}
return artifactId + "-" + version + "-" + CURRENT_ARCH_CLASSIFIER + ".jar";
return artifactId + "-" + version + "-" + classifier + ".jar";
}
public String sha1() {
if (CURRENT_ARCH_CLASSIFIER == null) {
return null;
}
return sha1.get(CURRENT_ARCH_CLASSIFIER);
return sha1;
}
public Path localPath() {
if (CURRENT_ARCH_CLASSIFIER == null) {
return null;
}
return DEPENDENCIES_DIR_PATH.resolve(filename());
}
public boolean isSupported() {
return CURRENT_ARCH_CLASSIFIER != null && sha1.containsKey(CURRENT_ARCH_CLASSIFIER);
}
}
static final class Repository {
public static final List<Repository> REPOSITORIES;
public static final Repository CUSTOM;
private static final class Repository {
public static final Repository MAVEN_CENTRAL = new Repository(i18n("repositories.maven_central"), "https://repo1.maven.org/maven2");
public static final Repository ALIYUN_MIRROR = new Repository(i18n("repositories.aliyun_mirror"), "https://maven.aliyun.com/repository/central");
public static final Repository DEFAULT;
static {
final String customUrl = System.getProperty("hmcl.openjfx.repo");
if (customUrl == null) {
CUSTOM = null;
if (System.getProperty("user.country", "").equalsIgnoreCase("CN")) {
DEFAULT = Repository.ALIYUN_MIRROR;
} else {
DEFAULT = Repository.MAVEN_CENTRAL;
}
REPOSITORIES = Collections.unmodifiableList(Arrays.asList(MAVEN_CENTRAL, ALIYUN_MIRROR));
} else {
CUSTOM = new Repository(String.format(i18n("repositories.custom"), customUrl), customUrl);
DEFAULT = CUSTOM;
REPOSITORIES = Collections.unmodifiableList(Arrays.asList(MAVEN_CENTRAL, ALIYUN_MIRROR, CUSTOM));
}
}
private final String name;
private final String url;
@@ -196,8 +149,6 @@ public final class SelfDependencyPatcher {
}
}
private static final String DEPENDENCIES_LIST_FILE = "/assets/openjfx-dependencies.json";
/**
* Patch in any missing dependencies, if any.
*/
@@ -221,19 +172,16 @@ public final class SelfDependencyPatcher {
throw new IncompatibleVersionException();
}
// We can only self-patch JavaFX on specific platform.
if (DependencyDescriptor.CURRENT_ARCH_CLASSIFIER == null) {
throw new IncompatibleVersionException();
}
SelfDependencyPatcher patcher = new SelfDependencyPatcher();
// Otherwise we're free to download in Java 11+
LOG.info("Missing JavaFX dependencies, attempting to patch in missing classes");
// Download missing dependencies
List<DependencyDescriptor> missingDependencies = checkMissingDependencies();
List<DependencyDescriptor> missingDependencies = patcher.checkMissingDependencies();
if (!missingDependencies.isEmpty()) {
try {
fetchDependencies(missingDependencies);
patcher.fetchDependencies(missingDependencies);
} catch (IOException e) {
throw new PatchException("Failed to download dependencies", e);
}
@@ -241,7 +189,7 @@ public final class SelfDependencyPatcher {
// Add the dependencies
try {
loadFromCache();
patcher.loadFromCache();
} catch (IOException ex) {
throw new PatchException("Failed to load JavaFX cache", ex);
} catch (ReflectiveOperationException | NoClassDefFoundError ex) {
@@ -250,7 +198,7 @@ public final class SelfDependencyPatcher {
LOG.info(" - Done!");
}
private static Repository showChooseRepositoryDialog() {
private Repository showChooseRepositoryDialog() {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
@@ -260,12 +208,12 @@ public final class SelfDependencyPatcher {
final ButtonGroup buttonGroup = new ButtonGroup();
for (Repository repository : Repository.REPOSITORIES) {
for (Repository repository : repositories) {
final JRadioButton button = new JRadioButton(repository.name);
button.putClientProperty("repository", repository);
buttonGroup.add(button);
panel.add(button);
if (repository == Repository.DEFAULT) {
if (repository == defaultRepository) {
button.setSelected(true);
}
}
@@ -293,16 +241,14 @@ public final class SelfDependencyPatcher {
* @throws IOException When the locally cached dependency urls cannot be resolved.
* @throws ReflectiveOperationException When the call to add these urls to the system classpath failed.
*/
private static void loadFromCache() throws IOException, ReflectiveOperationException {
private void loadFromCache() throws IOException, ReflectiveOperationException {
LOG.info(" - Loading dependencies...");
Set<String> modules = JFX_DEPENDENCIES.stream()
.filter(DependencyDescriptor::isSupported)
Set<String> modules = dependencies.stream()
.map(it -> it.module)
.collect(toSet());
Path[] jars = JFX_DEPENDENCIES.stream()
.filter(DependencyDescriptor::isSupported)
Path[] jars = dependencies.stream()
.map(DependencyDescriptor::localPath)
.toArray(Path[]::new);
@@ -314,11 +260,11 @@ public final class SelfDependencyPatcher {
*
* @throws IOException When the files cannot be fetched or saved.
*/
private static void fetchDependencies(List<DependencyDescriptor> dependencies) throws IOException {
private void fetchDependencies(List<DependencyDescriptor> dependencies) throws IOException {
boolean isFirstTime = true;
byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
Repository repository = Repository.DEFAULT;
Repository repository = defaultRepository;
int count = 0;
while (true) {
@@ -392,13 +338,10 @@ public final class SelfDependencyPatcher {
}
}
private static List<DependencyDescriptor> checkMissingDependencies() {
private List<DependencyDescriptor> checkMissingDependencies() {
List<DependencyDescriptor> missing = new ArrayList<>();
for (DependencyDescriptor dependency : JFX_DEPENDENCIES) {
if (!dependency.isSupported()) {
continue;
}
for (DependencyDescriptor dependency : dependencies) {
if (!Files.exists(dependency.localPath())) {
missing.add(dependency);
continue;
@@ -314,6 +314,7 @@ fatal.config_loading_failure=The configuration is not accessible.\nPlease ensure
fatal.migration_requires_manual_reboot=The update is complete. Please reopen Hello Minecraft! Launcher.
fatal.apply_update_failure=We're sorry, Hello Minecraft! Launcher couldn't finish the update because something went wrong.\nBut you can still manually finish the update by downloading Hello Minecraft! Launcher from %s.\nPlease consider reporting this issue to us.
fatal.samba=If you are trying to run HMCL in a shared folder by Samba, HMCL may not working, please try updating your Java or running HMCL in a local folder.
fatal.illegal_char=Illegal character '=' in your user folder path. HMCL can work, but some features don't work.\nYou will not be able to use the authlib-injector account or changing skin of offline account, and there may be greater security risks in multiplayer games.
feedback=Feedback
feedback.add=Add Feedback
@@ -458,6 +459,7 @@ launch.advice.uncorrected=If you are sure that the game can be started normally,
launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
launch.advice.forge2760_liteloader=Forge 2760 and higher are not compatible with LiteLoader, please consider upgrading Forge to 2773 or later. Shall we continue launching?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 and later versions are not compatible with OptiFine. Please consider downgrading Forge to 28.2.1 or earlier versions. Shall we continue launching?
launch.advice.forge37_0_60=Forge versions earlier than 37.0.60 are not compatible with Java 17. Please update forge or start the game with Java 16. Shall we continue launching?
launch.advice.java8_1_13=Minecraft 1.13 and later can only run on Java 8 or later.
launch.advice.java8_51_1_13=Minecraft 1.13 may crash on Java 8 earlier than 1.8.0_51. Please install the latest version of Java 8.
launch.advice.java9=You cannot launch Minecraft 1.12 or earlier with Java 9 or later versions of Java.
@@ -466,6 +468,7 @@ launch.advice.not_enough_space=You have allocated too much memory, because the p
launch.advice.require_newer_java_version=Minecraft %1$s requires Java %2$s or later, are you willing to download one now?
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game may crash. The maximum memory capacity for 32 bit systems is is 1024MB. Shall we continue launching?
launch.advice.vanilla_linux_java_8=For Linux x86-64, Minecraft 1.12.2 and below can only run on Java 8.\nJava 9 and above versions cannot load 32-bit native libraries like liblwjgl.so.
launch.advice.modlauncher8=The Forge version you are using is not compatible with the current Java version. Please try updating Forge, or start with Java 8u312/11.0.13/17.0.1 or earlier. Shall we continue launching?
launch.advice.vanilla_x86=Minecraft currently does not provide official support for architectures other than x86 and x86-64.\nYou can try to download the corresponding native library of the platform and specify its placement path.
launch.advice.vanilla_x86.translation=Minecraft currently does not provide official support for architectures other than x86 and x86-64.\nPlease use Java for x86-64 to run minecraft through translator, or download the corresponding native library of the platform and specify its placement path.
launch.failed=Unable to launch
@@ -94,7 +94,7 @@ account.methods.microsoft.error.add_family_probably=すでに18歳であるか
account.methods.microsoft.error.country_unavailable=XBoxLiveはお住まいの国/地域ではご利用いただけません。
account.methods.microsoft.error.missing_xbox_account=MicrosoftアカウントがXboxアカウントに接続されていません。続行する前に作成してください。
account.methods.microsoft.error.no_character=アカウントにMinecraftJavaプロファイルがありません。 Microsoftアカウントは有効ですが、ゲームを所有していません。
account.methods.microsoft.error.unknown=ログインに失敗しました。Microsoftはエラーコード%dで応答します。
account.methods.microsoft.error.unknown=ログインに失敗しました。Microsoftはエラーコード %d で応答します。
account.methods.microsoft.logging_in=ログイン...
account.methods.microsoft.hint=「ログイン」ボタンをクリックして、新しく開いたブラウザウィンドウでログインプロセスを続行する必要があります。
account.methods.microsoft.manual=「ログイン」ボタンをクリックした後、新しく開いたブラウザウィンドウで認証を完了する必要があります。ブラウザウィンドウが表示されない場合は、ここをクリックしてURLをコピーし、ブラウザで手動で開くことができます。
@@ -229,6 +229,7 @@ curse.category.4843=自動化
curse.category.4906=MCreator
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5193=データパッケージ
curse.category.399=スチームパンク
curse.category.396=128x
curse.category.398=512x以上
@@ -279,7 +280,7 @@ download.content=ゲームコンテンツ
download.curseforge.customization=光と影、およびゲームのカスタマイズ
download.existing=ファイルは既に存在するため、保存できません。 「名前を付けて保存」を選択して、ファイルを別の場所に保存できます。
download.external_link=ダウンロードサイトを開く
download.failed=%1$sのダウンロードに失敗しました、応答コード:%2 $ d
download.failed=%1$s のダウンロードに失敗しました、応答コード:%2$d
download.failed.empty=候補者なし。戻るにはここをクリックしてください。
download.failed.refresh=バージョンリストをダウンロードできません。ここをクリックして再試行してください。
download.game=ゲームのダウンロード
@@ -291,14 +292,14 @@ download.provider.balanced=mcbbsのソースからロード
download.provider.mirror=ミラーソースからロード
download.javafx=必要なランタイムコンポーネントのダウンロード
download.javafx.notes=ネットワークを介したHMCLに必要なコンポーネントのダウンロード。\n [ダウンロードソースの変更]ボタンをクリックして詳細を表示し、ダウンロードソースを選択するか、[キャンセル]ボタンをクリックして停止して終了します。\n注:ダウンロード速度が遅すぎます。ダウンロードソースを変更してみてください。
download.javafx.component=モジュール%sをダウンロードしています
download.javafx.component=ダウンロード中のモジュール %s
download.javafx.prepare=ダウンロードする準備ができました
exception.access_denied=It's denied by operating system to access file %s. Maybe we don't have permission to access this file, or this file has already been opened by other program.\n\
Please check if current operating system user has permission to access that file.\n\
For Windows users, you can also try the Resource Monitor, find if some programs is holding the file, try to close related program, or restart your computer.
exception.artifact_malformed=The file cannot pass verification.
exception.ssl_handshake=Cannot establish SSL connection because of missing SSL certifcate in current Java installation. Try to start HMCL on another Java VM.
exception.access_denied=ファイル %s にアクセスできないので、HMCL がファイルにアクセスできないか、ファイルが他のプログラムによって開かれています。\n\
例えば、管理者でないユーザーは、他のアカウントの個人フォルダーにあるファイルにアクセスできない場合があります。\n\
Windowsユーザーの場合、リソースモニターでプログラムがファイルを占有しているかどうかを確認し、もしそうなら、このファイルを占有している関連プログラムを閉じるか、コンピュータを再起動して、もう一度試してみることも可能です。
exception.artifact_malformed=ダウンロードしたファイルがチェックサムを通過していない。
exception.ssl_handshake=現在のJava仮想マシンに該当するSSL証明書がないため、SSL接続を確立できませんでした。 別のJava仮想マシンでHMCLを起動して、もう一度試してみてください。
extension.bat=WindowsBatファイル
extension.mod=Modファイル
@@ -311,8 +312,9 @@ fatal.javafx.missing=JavaFXがありません。\nJava11以降を使用してい
fatal.missing_dst_root_ca_x3=現在のJavaプラットフォームにDSTルートCAX3証明書がありません。\n引き続きHelloMinecraftを使用できます。ランチャーですが、一部のサイト(Let's Encryptによって発行された証明書を使用するサイトなど)に接続できず、ランチャーが正しく機能しない可能性があります。\n問題を解決するには、Javaランタイムを8u101以降に更新してください。
fatal.config_loading_failure=構成にアクセスできません。\nHelloMinecraftを確認してください。 Launcherには、「%s」とその中のファイルへの読み取りおよび書き込みアクセス権があります。
fatal.migration_requires_manual_reboot=更新が完了しました。 Hello Minecraftを再開してください!ランチャー。
fatal.apply_update_failure=ごめんなさい、Hello Minecraft! Launcher 何か問題が発生したため、ランチャーは更新を完了できませんでした。\nただし、Hello Minecraftをダウンロードすることで、手動で更新を終了できます。 %sからのランチャー。\nこの問題を報告することを検討してください。
fatal.apply_update_failure=ごめんなさい、Hello Minecraft! Launcher 何か問題が発生したため、ランチャーは更新を完了できませんでした。\nただし、Hello Minecraftをダウンロードすることで、手動で更新を終了できます。 %s からのランチャー。\nこの問題を報告することを検討してください。
fatal.samba=If you are trying to run HMCL in a shared folder by Samba, HMCL may not working, please try updating your Java or running HMCL in a local folder.
fatal.illegal_char=ユーザーフォルダーのパスに不正な文字'='が含まれています。hmclは動作しますが、一部の機能が正しく動作しません。 \nログインアカウントやオフラインログインではスキンの変更ができなくなり、多人数参加型ゲームをプレイする際のセキュリティリスクが高くなる可能性があります。
feedback=フィードバック
Feedback.add=フィードバックを追加
@@ -355,33 +357,37 @@ game.crash.feedback=問題は非常に一般的ですが、理由がわかりま
game.crash.info=ゲームステータス
game.crash.reason=クラッシュアナライザー
game.crash.reason.analyzing=分析中..
game.crash.reason.block=ブロックが原因でゲームを実行できません。\nMCEditでこのブロックを削除するか、関連するModを直接削除できます。\nブロックタイプ:%1$s \nブロックの場所:%2 $ s
game.crash.reason.bootstrap_failed=mod%1$sがクラッシュしたため、ゲームを実行できません。\n削除または更新を試みることができます。
game.crash.reason.config=modが構成ファイルを解析できないため、ゲームを実行できません。\nMod%1$sは構成ファイル%2 $ sを解析できません。
game.crash.reason.block=ブロックが原因でゲームを実行できません。\nMCEditでこのブロックを削除するか、関連するModを直接削除できます。\nブロックタイプ:%1$s \nブロックの場所:%2$s
game.crash.reason.bootstrap_failed=mod %1$s がクラッシュしたため、ゲームを実行できません。\n削除または更新を試みることができます。
game.crash.reason.config=modが構成ファイルを解析できないため、ゲームを実行できません。\nMod %1$s は構成ファイル %2$s を解析できません。
game.crash.reason.debug_crash=クラッシュが手動でトリガーされるため、ゲームを実行できません。\n実際、ゲームは無実であり、すべてあなたの責任です。
game.crash.reason.duplicated_mod=modが重複しているためゲームを実行できません:%s。\n%s \n各Modは1つしかインストールできません。重複したmodを削除して、再試行してください。
game.crash.reason.entity=エンティティが原因でゲームを実行できません。\nMCEditでこのエンティティを削除するか、関連するModを直接削除できます。\nエンティティタイプ:%1$s \nエンティティの場所:%2 $ s
game.crash.reason.duplicated_mod=Mod %1$s が重複してインストールされているため、現在のゲームを継続して実行できません。\n%2$s\n各Modは1つしかインストールできませんので、余分なModを削除して再度お試しください。
game.crash.reason.entity=エンティティが原因でゲームを実行できません。\nMCEditでこのエンティティを削除するか、関連するModを直接削除できます。\nエンティティタイプ:%1$s \nエンティティの場所:%2$s
game.crash.reason.fabric_version_0_12=Fabric 0.12以降は、現在インストールされているmodと互換性がありません。ファブリックを0.11.7にダウングレードする必要があります。
game.crash.reason.fabric_warnings=Fabricはいくつかの警告を出します:\n%1$s
game.crash.reason.file_already_exists=ファイル%1$sが既に存在するため、ゲームを実行できません。\n削除できる場合は、バックアップして削除し、再試行してください。
game.crash.reason.file_changed=ファイルの検証に失敗したため、ゲームを実行できません。\nMinecraftプライマリjarを変更した場合は、変更をロールバックするか、ゲームを再ダウンロードする必要があります。
game.crash.reason.file_already_exists=ファイル %1$s が既に存在するので、現在のゲームは続行できません。 \このファイルを削除してもよいと思われる場合は、このファイルをバックアップして、ゲームを再起動してから削除してみてください。
game.crash.reason.file_changed=ファイルのチェックサムに失敗したため、現在のゲームを続行できません。 \nMinecraft.jar ファイルを手動で変更した場合、変更をロールバックするか、ゲームを再度ダウンロードする必要があります。
game.crash.reason.gl_operation_failure=一部のモッド、シェーダーパック、テクスチャパックが原因でゲームがクラッシュしました。\n使用しているモッド/シェーダーパック/テクスチャパックを無効にして、再試行してください。
game.crash.reason.graphics_driver=グラフィックスドライバーの問題によりゲームがクラッシュしました。\nグラフィックスドライバーを最新バージョンに更新してください。\nコンピューターにディスクリートグラフィックスカードがある場合は、ゲームでitegrated /を使用しているかどうかを確認する必要があります。コアグラフィックス。その場合は、ディスクリートグラフィックカードを使用してHMCLとゲームを実行してください。それでもこれらの問題が発生する場合は、新しいグラフィックカードまたは新しいコンピュータの入手を検討してください。
game.crash.reason.graphics_driver=現在、お使いのグラフィックカードのドライバに問題があるため、ゲームがクラッシュしています。\n\
ゲームを起動する前に、グラフィックカードのドライバーを最新バージョンにアップグレードしてみてください。\n\
ディスクリートグラフィックカードを搭載している場合、統合型/コアグラフィックカードを使用してゲームが起動するかどうかを確認する必要があります。 その場合、ディスクリートグラフィックカードを使用したゲームでHMCLを起動してみてください。 それでも問題がある場合は、新しいグラフィックカードや新しいコンピュータの購入を検討する必要があるかもしれません。 \n\
コアグラフィックカードが必要な場合は、お使いのパソコンのCPUがIntel(R) Core(TM) 3000シリーズ以上であることをご確認ください。 その場合、Minecraft 1.16.5以前の場合は、ゲームに使用しているJavaバージョンを1.8.0_51以下にダウングレードするか、ディスクリートグラフィックカードを交換するか、または、新しい コンピュータを使用します。 \n\
詳しくは、<a href="https://hmcl.huangyuhui.net/help/game/graphics-card">こちら</a>をクリックしてご覧ください。
game.crash.reason.illegal_access_error=一部のmodが原因でゲームがクラッシュしました。\n認識している場合:%1$s、modを更新または削除して、再試行してください。
game.crash.reason.jdk_9=Javaのバージョンが高すぎるため、ゲームを実行できません。\nJava 8をダウンロードしてインストールし、ゲーム設定で新しくインストールしたJavaを選択する必要があります。
game.crash.reason.jvm_32bit=メモリ割り当てが32ビットJavaVMの制限を超えたため、ゲームがクラッシュしました。\nOSが64ビットの場合は、64ビットJavaをインストールして使用してください。 OSが32ビットの場合は、64ビットOSを再インストールするか、新しいコンピュータを変更できます。\nまたは、自動メモリ割り当てを無効にして、最大メモリサイズを1024MB以下に設定できます。
game.crash.reason.loading_crashed_forge=mod%1$s(%2 $ s)がクラッシュしたため、ゲームがクラッシュしました。\n削除または更新を試みることができます。
game.crash.reason.loading_crashed_fabric=mod%1$sがクラッシュしたため、ゲームがクラッシュしました。\n削除または更新を試みることができます。
game.crash.reason.loading_crashed_forge=mod %1$s(%2$s)がクラッシュしたため、ゲームがクラッシュしました。\n削除または更新を試みることができます。
game.crash.reason.loading_crashed_fabric=mod %1$s がクラッシュしたため、ゲームがクラッシュしました。\n削除または更新を試みることができます。
game.crash.reason.memory_exceeded=JVMが割り当てるのに十分なメモリがないため、ゲームがクラッシュしました。\nこの問題は、ページサイズが小さすぎることが原因です。\nゲーム設定で自動メモリ割り当てをオフにし、メモリ割り当てを次のように調整する必要があります。システムが処理できる値。\nシステムのページサイズを十分な大きさに調整することもできます。
game.crash.reason.mod=modが原因でゲームがクラッシュしました:%1$s。\nmodを更新または削除して、再試行できます。
game.crash.reason.mod_resolution=modの解決に失敗したため、ゲームがクラッシュしました。\nFabricは次の情報を提供します:\n%s
game.crash.reason.mod_resolution_collection=必要なmodバージョンに互換性がないため、ゲームがクラッシュしました。\n%1$sには%2 $ sが必要です。\nこれは、%2 $ sをアップグレードまたはダウングレードする必要があることを意味します。 %3 $ sをダウンロードする必要があります。
game.crash.reason.mod_resolution_conflict=modが競合しているため、ゲームがクラッシュしました。\n%1$sが%2 $ sと競合しています。
game.crash.reason.mod_resolution_missing=一部のmodが依存するmodがインストールされていないため、ゲームがクラッシュしました。\n%1$sではmodをインストールする必要があります:%2 $ s。\nこれは、%2 $をダウンロードする必要があることを意味します。 ■ダウンロードページまたはオンラインWebサイトから。
game.crash.reason.mod_resolution_missing_minecraft=modが現在のMinecraftバージョンと互換性がないため、ゲームがクラッシュしました。\n%1$sにはMinecraftバージョン%2 $ sが必要です。\nインストールされているバージョンのmodを保持する場合は、変更する必要があります。ゲームバージョン。\n現在のゲームバージョンを引き続き使用する場合は、適切なmodを再インストールする必要があります。
game.crash.reason.mod_resolution_mod_version=%1$s(バージョン:%2 $ s)
game.crash.reason.mod_resolution_collection=改造前のバージョンと一致しないため、現在のゲームを続行できない。\n%1$s Mod: %2$s が必要です。をクリックすると実行を継続します。\nつまり、フロントエンドをアップデートするか、ダウングレードする必要があるのです。 ダウンロードページからMODをダウンロードするか、ウェブから %3$s をダウンロードすることができます。
game.crash.reason.mod_resolution_conflict=modが競合しているため、ゲームがクラッシュしました。\n%1$s が %2$s と競合しています。
game.crash.reason.mod_resolution_missing=Modプレフィックスがないため、現在のゲームを続けることができません。 \続行するにはMod: %2$s が必要です。 \これはMODがインストールされていないか、そのMODのバージョンが足りないことを意味します。 ダウンロードページからMODをダウンロードするか、ウェブから %3$s をダウンロードすることができます。
game.crash.reason.mod_resolution_missing_minecraft=modが現在のMinecraftバージョンと互換性がないため、ゲームがクラッシュしました。\n%1$s にはMinecraftバージョン %2$s が必要です。\nインストールされているバージョンのmodを保持する場合は、変更する必要があります。ゲームバージョン。\n現在のゲームバージョンを引き続き使用する場合は、適切なmodを再インストールする必要があります。
game.crash.reason.mod_resolution_mod_version=%1$s(バージョン:%2$s)
game.crash.reason.mod_resolution_mod_version.any=%1$s(任意のバージョン)
game.crash.reason.no_class_def_found_error=コードが不完全なためゲームを実行できません。\nゲームにmodがないか、一部のmodファイルが不完全であるか、一部のmodが現在のゲームと互換性がない可能性があります。\nゲームを再インストールする必要がある場合があります。およびmod、またはヘルプを要求します。\n%1$sがありません
game.crash.reason.no_class_def_found_error=コードが不完全なためゲームを実行できません。\nゲームにmodがないか、一部のmodファイルが不完全であるか、一部のmodが現在のゲームと互換性がない可能性があります。\nゲームを再インストールする必要がある場合があります。およびmod、またはヘルプを要求します。\n%1$s がありません
game.crash.reason.no_such_method_error=コードが不完全なためゲームを実行できません。\nゲームにmodがないか、一部のmodファイルが不完全であるか、一部のmodが現在のゲームと互換性がない可能性があります。\nゲームを再インストールする必要がある場合があります。と改造、または助けを求める。
game.crash.reason.opengl_not_supported=グラフィックスドライバに問題があるため、ゲームを実行できません。\nOpenGLがサポートされていないため、リモートデスクトップモードですか、それともストリーミングモードですか。はいの場合は、元のコンピューターを使用してゲームを開始してください。\nまたは、グラフィックスドライバーを最新バージョンに更新して、ゲームを再起動してください。コンピューターに個別のグラフィックがある場合は、ゲームが統合/コアグラフィックを使用しているかどうかを確認する必要があります。その場合は、ディスクリートグラフィックカードを使用してHMCLとゲームを実行してみてください。それでもこれらの問題が発生する場合は、新しいグラフィックカードまたは新しいコンピュータの入手を検討してください。
game.crash.reason.openj9=ゲームはOpenJ9仮想マシンでは実行できません。ゲーム設定でHotspotJava仮想マシンに切り替えて、ゲームを再起動してください。そうでない場合は、オンラインでダウンロードできます。
@@ -406,7 +412,7 @@ input.url=有効なURLである必要があります。
install=新規作成
install.change_version=バージョンの変更
install.change_version.confirm=%sをバージョン%sから%sに更新してもよろしいですか?
install.change_version.confirm=%s をバージョン %s から %s に更新してもよろしいですか?
install.failed=バージョンのインストールに失敗しました
install.failed.downloading=一部のファイルが正常にダウンロードされなかったため、インストールに失敗しました
install.failed.downloading.detail=ファイルのダウンロードに失敗しました:%s
@@ -415,17 +421,17 @@ install.failed.install_online=提供されたインストーラーファイル
install.failed.malformed=少し前にダウンロードしたファイルの形式が正しくありません。この問題を解決するには、他のダウンロードプロバイダーに切り替えることができます。
install.failed.optifine_conflict=Fabric、OptiFine、ForgeがMinecraft1.13に同時にインストールされます
install.failed.optifine_forge_1.17=Minecraft 1.17.1の場合、OptiFine H1 Pre2以降のバージョンのみがForgeと互換性があり、スナップショットバージョンでインストールできます。
install.failed.version_mismatch=ライブラリにはゲームバージョン%sが必要ですが、実際のバージョンは%sです。
install.installer.change_version=%s、このバージョンは現在のゲームバージョンと互換性がありません。別のものを選択するには、ここをクリックしてください。
install.installer.choose=%sバージョンを選択してください
install.installer.depend=%sに依存
install.failed.version_mismatch=ライブラリにはゲームバージョン %s が必要ですが、実際のバージョンは %s です。
install.installer.change_version=%s 、このバージョンは現在のゲームバージョンと互換性がありません。別のものを選択するには、ここをクリックしてください。
install.installer.choose=%s バージョンを選択してください
install.installer.depend=%s に依存
install.installer.fabric=Fabric
install.installer.fabric-api=Fabric API
install.installer.fabric-api.warning=警告:Fabric APIはmodであり、新しいゲームのmodディレクトリにインストールされます。新しいゲームの実行ディレクトリを変更しないでください。変更すると、FabricAPIのインストールが失われます。その設定を変更したい場合は、FabricAPIを再インストールする必要があります。
install.installer.forge=Forge
install.installer.game=Minecraft
install.installer.incompatible=%sと互換性がありません
install.installer.install=インストール%s
install.installer.incompatible=%s と互換性がありません
install.installer.install=インストール %s
install.installer.install_offline=ファイルからのインストール/更新
install.installer.install_offline.extension=Forge / OptiFineインストーラー
install.installer.install_offline.tooltip=Forge / OptiFineインストールファイルのインポートをサポートします
@@ -458,7 +464,7 @@ launch.advice.java8_51_1_13=Minecraft 1.13は、1.8.0_51より前のJava8でク
launch.advice.java9=Java9以降のバージョンのJavaでMinecraft1.12以前を起動することはできません。
ゲームを高速化するには、launch.advice.newer_java=Java8をお勧めします。多くのMinecraft1.12以降、およびほとんどのModには、Java8が必要です。
launch.advice.not_enough_space=割り当てたメモリが多すぎます。物理メモリサイズが%dMBであるため、ゲームがクラッシュする可能性があります。打ち上げを続けましょうか。
launch.advice.require_newer_java_version=Minecraft%1$sにはJava%2 $ s以降が必要です。今すぐダウンロードしますか?
launch.advice.require_newer_java_version=Minecraft %1$s にはJava %2$s 以降が必要です。今すぐダウンロードしますか?
launch.advice.too_large_memory_for_32bit=32ビットJavaランタイム環境が原因で、割り当てたメモリが多すぎるため、ゲームがクラッシュする可能性があります。 32ビットシステムの最大メモリ容量は1024MBです。打ち上げを続けましょうか。
launch.advice.vanilla_linux_java_8=Linux x86-64の場合、Minecraft1.12.2以下はJava8でのみ実行できます。\nJava9以降のバージョンでは、liblwjgl.soなどの32ビットネイティブライブラリをロードできません。
launch.advice.vanilla_x86=Minecraftは現在、x86およびx86-64以外のアーキテクチャの公式サポートを提供していません。\nプラットフォームの対応するネイティブライブラリをダウンロードして、その配置パスを指定してみてください。
@@ -468,7 +474,7 @@ launch.failed.cannot_create_jvm=Java仮想マシンを作成できませんで
launch.failed.creating_process=プロセスの作成に失敗しました。 Javaパスを確認してください。
launch.failed.command_too_long=コマンドの長さが制限を超えているため、batスクリプトを作成できません。 PowerShellスクリプトとしてエクスポートしてください。
launch.failed.decompressing_natives=ネイティブライブラリを解凍できません。
launch.failed.download_library=ライブラリ%sをダウンロードできません。
launch.failed.download_library=ライブラリ %s をダウンロードできません。
launch.failed.executable_permission=起動スクリプトにアクセス許可を追加できません。
launch.failed.execution_policy=実行ポリシーを設定します
launch.failed.execution_policy.failed_to_set=実行ポリシーの設定に失敗しました
@@ -491,14 +497,14 @@ launcher.agreement.hint=このソフトウェアを使用するには、EULAに
launcher.background=背景画像
launcher.background.classic=クラシック
launcher.background.choose=背景画像ファイルを選択してください
launcher.background.default=標準
launcher.background.default=標準(ランチャーと同じディレクトリにある background.png/jpg と bg フォルダから自動的に画像を取得します。)
launcher.background.network=ネットワーク
launcher.background.translucent=半透明
launcher.cache_directory=キャッシュ用のディレクトリ
launcher.cache_directory.clean=Clear
launcher.cache_directory.clean=クリアランス
launcher.cache_directory.choose=キャッシュするディレクトリを選択します
launcher.cache_directory.default=標準
launcher.cache_directory.disabled=無効
launcher.cache_directory.default=標準(%AppData%/.minecraft または ~/.minecraft)
launcher.cache_directory.disabled=無効(常にゲームパスを使用する)
launcher.cache_directory.invalid=無効なディレクトリ。デフォルト設定の復元。
launcher.contact=お問い合わせ
launcher.crash=Hello Minecraft!ランチャーがクラッシュしました!次のコンテンツをコピーして、MCBBS、Baidu Tieba、GitHub、またはMinecraftForumを介してフィードバックを送信してください。
@@ -556,11 +562,11 @@ modpack.files.resourcepacks=リソース/テクスチャパック
modpack.files.saves=保存されたマップ
modpack.files.scripts=MineTweaker構成
modpack.files.servers_dat=サーバーリスト
modpack.install=%smodpackをインストールします
modpack.install=%s modpackをインストールします
modpack.installing=modpackのインストール
modpack.introduction=Curse、MultiMC、HMCLmodpackがサポートされています。
modpack.invalid=無効なmodpackファイル。
modpack.mismatched_type=不適切なmodpackタイプ、現在のゲームは%s modpackですが、更新ファイルは%smodpackです。
modpack.mismatched_type=不適切なmodpackタイプ、現在のゲームは %s modpackですが、更新ファイルは %s modpackです。
modpack.name=Modpack名
modpack.not_a_valid_name=無効なmodpack名
modpack.origin=Origin
@@ -616,8 +622,8 @@ modrinth.category.worldgen=Worldgen
mods=Mods
mods.add=modを追加
mods.add.failed=mods%sの追加に失敗しました。
mods.add.success=mods%sが正常に追加されました。
mods.add.failed=mods %s の追加に失敗しました。
mods.add.success=mods %s が正常に追加されました。
mods.category=Category
mods.check_updates=更新を確認
mods.check_updates.current_version=Current
@@ -631,7 +637,7 @@ mods.curseforge=CurseForge
mods.dependencies=Dependencies
mods.disable=無効にする
mods.download=Modのダウンロード
mods.download.title=Modダウンロード-%1s
mods.download.title=Modダウンロード- %1s
mods.enable=有効にする
mods.manage=Mods
mods.mcbbs=MCBBS
@@ -650,8 +656,8 @@ multiplayer.download=マルチプレイヤーの依存関係をダウンロー
multiplayer.download.failed=マルチプレイヤーの初期化に失敗しました。一部のファイルはダウンロードできません
multiplayer.download.success=依存関係の初期化に成功しました
multiplayer.download.unsupported=現在のオペレーティングシステムまたはアーキテクチャはサポートされていません。
multiplayer.exit.after_ready=マルチプレイヤーセッションが壊れています。 cato exitcode%d
multiplayer.exit.before_ready=マルチプレイヤーセッションの作成に失敗しました。 cato exitcode%d
multiplayer.exit.after_ready=マルチプレイヤーセッションが壊れています。 cato exitcode %d
multiplayer.exit.before_ready=マルチプレイヤーセッションの作成に失敗しました。 cato exitcode %d
multiplayer.exit.timeout=マルチプレイヤーサーバーへの接続に失敗しました。
multiplayer.exit.timeout.dynamic_token=マルチプレイヤーサーバーへの接続に失敗しました。
multiplayer.hint=マルチプレイヤー機能は実験的なものです。フィードバックをお願いします。
@@ -676,8 +682,8 @@ multiplayer.report=レポート
multiplayer.relay=リレーモード
multiplayer.relay.hint=静的トークンを持ち、テストされたNATタイプが不良対称であるユーザーの場合
multiplayer.session=セッション
multiplayer.session.name.format=%1$sの部屋
multiplayer.session.name.motd=HMCLマルチプレイヤーセッション-%s
multiplayer.session.name.format=%1$s の部屋
multiplayer.session.name.motd=HMCLマルチプレイヤーセッション - %s
multiplayer.session.close=部屋を閉じる
multiplayer.session.close.warning=部屋を閉じた後、部屋に参加したすべてのプレイヤーは接続を失います。継続する?
multiplayer.session.copy_room_code=招待コードをコピーする
@@ -687,7 +693,7 @@ multiplayer.session.create.error.static_token=マルチプレイヤーセッシ
multiplayer.session.create.hint=マルチプレイヤーセッションを作成する前に、実行中のゲームで[LANサーバーを開く]をクリックし、ゲームに表示されているポートを下の空白に入力する必要があります。
multiplayer.session.create.join=接続リクエスト
multiplayer.session.create.join.allow=すべての参加リクエストを受け入れます(無効にした場合、攻撃を防ぐために、参加リクエストを手動で受け入れる必要があります)。
multiplayer.session.create.join.prompt=Player%sがマルチプレイヤーセッションに参加したいと考えています。承認?
multiplayer.session.create.join.prompt=Player %s がマルチプレイヤーセッションに参加したいと考えています。承認?
multiplayer.session.create.members=メンバー
multiplayer.session.create.members.kick=キック
multiplayer.session.create.members.kick.prompt=このプレーヤーをキックした後、このプレーヤーとの確立された接続は切断されます。
@@ -700,30 +706,30 @@ multiplayer.session.create.token.prompt=デフォルトはランダム化され
multiplayer.session.error.already_started=Catoサービスはすでにローカルで開始されています。他のマルチプレイヤーサービスが提供されているかどうかを確認してください。または、タスクマネージャーでcatoプロセスを強制終了できます。
multiplayer.session.error.file_not_found=cato実行可能ファイルが見つかりません。このページに入るときに、このファイルをダウンロードする必要があります。ウイルス対策ソフトウェアがファイルを分離したかどうかを確認してください。 trueの場合、ロールバックしてください。
multiplayer.session.expired=マルチプレイヤーセッションの有効期限が切れています。続行するには、部屋を再作成または再参加する必要があります。
multiplayer.session.hint=マルチプレイヤー機能を有効にするには、ゲームで[LANサーバーを開く]をクリックする必要があります。
multiplayer.session.join=セッションに参加する
multiplayer.session.join.error=マルチプレイヤーセッションへの参加に失敗しました
multiplayer.session.join.error.connection=マルチプレイヤーセッションへの参加に失敗しました。接続を確立できません。
multiplayer.session.join.hint=マルチプレイヤーセッションをすでに作成しているゲーマーから招待コードを取得する必要があります。
multiplayer.session.join.invitation_code=招待コード
multiplayer.session.join.invitation_code.error=不正な招待コード。マルチプレイヤーセッションを作成したプレイヤーから招待コードを入手してください。
multiplayer.session.join.invitation_code.version=マルチプレイヤー機能のバージョンはあなたの間で同じではありません。
multiplayer.session.join.kicked=セッションホルダーによってキックされました。マルチプレイヤーセッションとの接続が失われます。
multiplayer.session.join.kicked.join_acceptance_timeout=相手方が即時加入の要求に応じない場合
multiplayer.session.join.kicked.kicked=オーナーに部屋から追い出される。
multiplayer.session.join.kicked.version_not_matched=マルチプレイヤーオンライン機能のバージョン番号が異なっているため、接続しているマルチプレイヤーオンライン機能のバージョン番号が同じであることを確認してください。
multiplayer.session.join.lost_connection=マルチプレイヤーセッションとの接続が失われました。セッションが作成者によって破棄されたか、このセッションとの接続を確立できない可能性があります。
multiplayer.session.join.port.error=リスニングに使用できるローカルネットワークポートが見つかりません。 HMCLがポートでリッスンする権限を持っていることを確認してください。
multiplayer.session.join.rejected=接続はセッション所有者によって拒否されます。
multiplayer.session.join.wait=受け入れを待っています。
multiplayer.session.join.wait_timeout=セッション所有者は時間内にあなたの参加リクエストを受け入れません。
multiplayer.session.members=ルームメンバー
multiplayer.session.quit=Quit Room
multiplayer.session.quit=この部屋から出る
multiplayer.session.quit.warning=部屋を終了した後、サーバーとの接続が失われます。継続する?
multiplayer.session.username=ユーザー名
multiplayer.state.connecting=接続中
multiplayer.state.disconnected=マルチプレイヤーセッションが作成/開始されていません
multiplayer.state.disconnected.hint=誰かがマルチプレイヤーセッションを作成し、他の人がセッションに参加して一緒にゲームをプレイする必要があります。
multiplayer.state.master=作成された部屋:%1$s、ポート:%2 $ d
multiplayer.state.master=作成された部屋:%1$s、ポート:%2$d
multiplayer.state.master.hint=マルチプレイヤールームを作成したら、[招待コードをコピー]ボタンをクリックして、招待したいプレイヤーに招待コードを送信します。これらのプレーヤーは、会議室に参加するときに招待コードを貼り付けることができます。
multiplayer.state.slave=参加ルーム:%1$s、アドレス:%2 $ s
multiplayer.state.slave=参加ルーム:%1$s、アドレス:%2$s
multiplayer.state.slave.copy=コピーアドレス
multiplayer.state.slave.hint=マルチプレイヤールームに参加した後、Minecraftのマルチプレイヤーページにアクセスして「HMCLマルチプレイヤーセッション」サーバーに接続するか、以下に示すアドレスのサーバーを手動で追加する必要があります。
@@ -731,18 +737,18 @@ datapack=Datapacks
datapack.add=データパックをインストールします
datapack.choose_datapack=インポートするデータパックのzipを選択してください
datapack.extension=Datapack
datapack.title=World%s-データパック
datapack.title=World %s -データパック
world=マップ
world.add=マップを追加(.zip)
world.datapack=データパックの管理
world.datapack.1_13=Minecraft1.13以降のバージョンのみがデータパックをサポートします。
world.description=%s。最終ゲーム時刻:%s。ゲームバージョン:%s。
world.description=%s。 最終ゲーム時刻:%s。 ゲームバージョン:%s。
world.download=ダウンロード
world.export=このマップをエクスポートする
world.export.title=保存する場所を選択してください
world.export.location=エクスポート先
world.export.wizard=エクスポートワールド%s
world.export.wizard=エクスポートワールド: %s
world.extension=World zip
world.game_version=ゲームバージョン
world.import.already_exists=このマップはすでに存在しています。
@@ -836,10 +842,10 @@ settings.advanced.server_ip=サーバーアドレス
settings.advanced.server_ip.prompt=ゲームの起動時にサーバーに参加する
settings.advanced.use_native_glfw=[Linux]システムGLFWを使用する
settings.advanced.use_native_openal=[Linux]システムOpenALを使用する
settings.advanced.workaround=Workarounds
settings.advanced.workaround.warning=Workaround settings are only for professional users. Modifying workaround settings may result in game crash. Unless you know what you are doing, please do not modify these options.
settings.advanced.wrapper_launcher=Wrapper command
settings.advanced.wrapper_launcher.prompt=Allows launching using an extra wrapper program like 'optirun' on Linux.
settings.advanced.workaround=デバッグ用オプション
settings.advanced.workaround.warning=デバッグオプションはプロフェッショナルのみ使用可能です。 デバッグオプションにより、ゲームが起動しない場合があります。 これらのオプションは、ご自分が何をしているのかが分からない限り、変更しないでください。
settings.advanced.wrapper_launcher=パッキングオーダー
settings.advanced.wrapper_launcher.prompt=optirunが記入されている場合、起動コマンドが "java ... "から変更されます。を「optirun java ...」に変更しました。
settings.custom=カスタム
@@ -850,8 +856,10 @@ settings.game.exploration=フォルダ
settings.game.fullscreen=フルスクリーン
settings.game.java_directory=Javaディレクトリ
settings.game.java_directory.auto=自動的に選択されます
settings.game.java_directory.bit=%s-ビット
settings.game.java_directory.auto.not_found=適切なJavaがない
settings.game.java_directory.bit=%s ビット
settings.game.java_directory.choose=Javaディレクトリを選択します。
settings.game.java_directory.invalid=Java のパスが正しくありません。
settings.game.java_directory.template=%s(%s)
settings.game.management=管理
settings.game.working_directory=作業ディレクトリ
@@ -875,7 +883,7 @@ settings.launcher.general=全般的
settings.launcher.language=言語
settings.launcher.launcher_log.export=ランチャーログのエクスポート
settings.launcher.launcher_log.export.failed=ログのエクスポートに失敗しました
settings.launcher.launcher_log.export.success=ログが%sにエクスポートされました
settings.launcher.launcher_log.export.success=ログが %s にエクスポートされました
settings.launcher.log=ログ
settings.launcher.log.font=ログフォント
settings.launcher.proxy=プロキシ
@@ -893,13 +901,13 @@ settings.launcher.title_transparent=タイトルの透明性
settings.launcher.version_list_source=バージョンリストソース
settings.memory=ゲームメモリ
settings.memory.allocate.auto=最小%1 $ .1f GB /実際の%2 $ .1f GB
settings.memory.allocate.auto.exceeded=最小%1 $ .1f GB /実際の%2 $ .1f GB(%3 $ .1f GB使用可能)
settings.memory.allocate.manual=割り当て%1 $ .1f GB
settings.memory.allocate.manual.exceeded=割り当て%1 $ .1f GB(%3 $ .1f GB使用可能)
settings.memory.allocate.auto=最小 %1$.1f GB /実際の %2$.1f GB
settings.memory.allocate.auto.exceeded=最小 %1$.1f GB /実際の %2$.1f GB(%3$.1f GB使用可能)
settings.memory.allocate.manual=割り当て %1$.1f GB
settings.memory.allocate.manual.exceeded=割り当て %1$.1f GB(%3$.1f GB使用可能)
settings.memory.auto_allocate=メモリサイズの自動割り当て
settings.memory.lower_bound=最小メモリ
settings.memory.used_per_total=%1 $ .1f GB使用済み/%2 $ .1fGB合計
settings.memory.used_per_total=%1$.1f GB使用済み/%2$.1fGB合計
settings.physical_memory=物理メモリサイズ
settings.show_log=ログを表示
settings.skin=オフラインアカウントのスキンの変更がサポートされるようになりました!アカウントページに移動して、今すぐ肌を変えてください!
@@ -911,7 +919,7 @@ settings.type.global.manage=グローバルゲーム設定
settings.type.global.edit=グローバルゲーム設定を構成します
settings.type.special.enable=このゲームの特殊な設定を有効にします
settings.type.special.edit=現在のゲーム設定を構成します
settings.type.special.edit.hint=現在のゲームバージョン%sで特殊な設定が有効になっているため、現在のページのオプションはそのゲームバージョンに適用されません。リンクをクリックして、現在のゲーム設定を構成します。
settings.type.special.edit.hint=現在のゲームバージョン %s で特殊な設定が有効になっているため、現在のページのオプションはそのゲームバージョンに適用されません。リンクをクリックして、現在のゲーム設定を構成します。
sponsor=寄付
sponsor.bmclapi=ダウンロードサービスはBMCLAPIとMCBBSによって提供されます。詳細については、ここをクリックしてください。
@@ -972,12 +980,12 @@ version.manage.duplicate.duplicate_save=コピー保存
version.manage.duplicate.prompt=新しいバージョン名を入力してください
version.manage.duplicate.confirm=
version.manage.manage=バージョンの管理
version.manage.manage.title=バージョンの管理-%1s
version.manage.manage.title=バージョンの管理 - %1s
version.manage.redownload_assets_index=ゲームアセットファイルの更新
version.manage.remove=このバージョンを削除します
version.manage.remove.confirm=このバージョン%sを削除してもよろしいですか?このバージョンを再度復元することはできません。
version.manage.remove.confirm.trash=このバージョン%sを削除してもよろしいですか?このバージョンは、システムのゴミ箱に%sという名前で復元できます。
version.manage.remove.confirm.independent=このバージョンは独立モードであるため、このバージョンを削除すると、このバージョンに属するすべての保存済みワールドも削除されます。このバージョン%sを削除しますか?
version.manage.remove.confirm=このバージョン %s を削除してもよろしいですか?このバージョンを再度復元することはできません。
version.manage.remove.confirm.trash=このバージョン %s を削除してもよろしいですか?このバージョンは、システムのゴミ箱に %s という名前で復元できます。
version.manage.remove.confirm.independent=このバージョンは独立モードであるため、このバージョンを削除すると、このバージョンに属するすべての保存済みワールドも削除されます。このバージョン %s を削除しますか?
version.manage.remove_libraries=ライブラリファイルを削除します
version.manage.rename=このバージョンの名前を変更します
version.manage.rename.message=このバージョンの新しい名前を入力してください
@@ -314,6 +314,7 @@ fatal.config_loading_failure=Hello Minecraft! Launcher 無法載入設定檔案
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即將升級完成,請重新開啟 Hello Minecraft! Launcher。
fatal.apply_update_failure=我們很抱歉 Hello Minecraft! Launcher 無法自動完成升級程式,因為出現了一些問題。\n但你依然可以從 %s 處手動下載 Hello Minecraft! Launcher 來完成升級。\n請考慮向我們回報該問題。
fatal.samba=如果您正在通過 Samba 共亯的資料夾中運行 HMCL,HMCL 可能無法正常工作,請嘗試更新您的 Java 或在本地資料夾內運行 HMCL。
fatal.illegal_char=您的用戶資料夾路徑中存在非法字元‘=’。HMCL 能够運行,但一些功能無法正常使用。 \n您將無法使用外置登入帳戶以及離線登入更換皮膚功能,並且在進行多人遊戲時可能存在更大的安全隱患。
feedback=回饋
feedback.add=新增回饋
@@ -458,9 +459,11 @@ launch.advice.uncorrected=如果您確實希望使用您自訂的 Java 虛擬機
launch.advice.different_platform=您的系統為 64 位元,但是 Java 是 32 位元,建議您安裝 64 位 Java。
launch.advice.forge2760_liteloader=Forge 2760 與 LiteLoader 不相容。請刪除 LiteLoader 或者變更 Forge 至 2773 或更新的版本。是否繼續啟動?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 或更高版本與 OptiFine 不相容。請變更 Forge 至 28.2.1 或更低版本。是否繼續啟動?
launch.advice.forge37_0_60=Forge 低於 37.0.60 的版本不相容 Java 17。請更新 Forge 到 37.0.60 或更高版本,或者使用 Java 16 啟動遊戲。是否繼續啟動?
launch.advice.java8_1_13=Minecraft 1.13 只支援 Java 8 或更高版本,請使用 Java 8 或最新版本。
launch.advice.java8_51_1_13=低於 1.8.0_51 的 Java 版本可能會導致 Minecraft 1.13 崩潰。建議您到 https://java.com 安裝最新版的 Java 8。
launch.advice.java9=低於 (包含) 1.13 的有安裝 Mod 的 Minecraft 版本不支援 Java 9 或更高版本,請使用 Java 8。
launch.advice.modlauncher8=您所使用的 Forge 版本與當前使用的 Java 不相容。請嘗試更新 Forge,或使用 Java 8u312/11.0.13/17.0.1 及更早版本啟動。是否繼續啟動?
launch.advice.newer_java=偵測到您未使用 Java 8 及更新版本,Java 8 能使遊戲更順暢而且 Minecraft 1.12 及更新版本和很多 Mod 強制需要 Java 8 版本。
launch.advice.not_enough_space=您設定的記憶體大小過大,由於超過了系統記憶體大小 %dMB,所以可能影響遊戲體驗或無法啟動遊戲。是否繼續啟動?
launch.advice.require_newer_java_version=Minecraft %1$s 僅能運行在 Java %2$s 或更高版本上,是否下載?
@@ -89,15 +89,15 @@ account.methods.authlib_injector=外置登录 (authlib-injector)
account.methods.microsoft=微软帐户
account.methods.microsoft.birth=如何修改帐户出生日期
account.methods.microsoft.close_page=已完成微软帐户授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。
account.methods.microsoft.error.add_family=由于你未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以点击上方链接更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.add_family=由于你未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以点击上方【账户设置页】更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.add_family_probably=请检查你的帐户设置,如果年龄未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以点击上方链接更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 XBox Live 的支持。
account.methods.microsoft.error.missing_xbox_account=你的微软帐户尚未关联 XBox 帐户,你必须先创建 XBox 帐户,才能登录游戏。
account.methods.microsoft.error.no_character=该帐户没有包含 Minecraft Java 版购买记录
account.methods.microsoft.error.unknown=登录失败,错误码:%d
account.methods.microsoft.logging_in=登录中...
account.methods.microsoft.hint=您需要点击登录按钮,并在新打开的浏览器窗口中完成登录,并输入待会在此处显示的设备代码。
account.methods.microsoft.manual=设备代码为:<b>%1$s</b>,已经帮你拷贝到剪切板中,如果未能粘贴,你可以点击此处再复制一次。你需要在新打开的浏览器窗口中完成登录,若登录页面未能打开,您手动在浏览器中打开网页:%2$s。
account.methods.microsoft.hint=你需要点击登录按钮,并在新打开的浏览器窗口中完成登录,并输入待会在此处显示的设备代码。<b>若发现账号头像是默认头像、进入游戏发现是默认皮肤和无法进入开启了正版认证的服务器,则可能需要重新添加微软账户。</b>
account.methods.microsoft.manual=<b>你需要在新打开的网页窗口中输入应用代码并按提示完成登录</b> 应用代码为:<b>%1$s</b>,已拷贝至剪切板中,若无法粘贴,可点击此蓝方框再次拷贝。若网页未能打开,请手动在网页浏览器中打开:%2$s
account.methods.microsoft.profile=帐户设置页
account.methods.microsoft.snapshot=HMCL 快照版不支持微软登录
account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录...
@@ -137,7 +137,7 @@ archive.version=版本
assets.download=下载资源
assets.download_all=检查资源文件完整性
assets.index.malformed=资源文件的索引文件损坏,您可以在游戏设置页面右上角的设置按钮中选择更新游戏资源文件以修复该问题
assets.index.malformed=资源文件的索引文件损坏,您可以在相应的游戏管理,点击左下角的管理按钮选择【更新游戏资源文件】以修复该问题
button.cancel=取消
button.change_source=切换下载源
@@ -298,7 +298,7 @@ download.javafx.prepare=准备开始下载
exception.access_denied=因为无法访问文件 %s,HMCL 没有对该文件的访问权限,或者该文件被其他程序打开。\n\
请你检查当前操作系统帐户是否能访问该文件,比如非管理员用户可能不能访问其他帐户的个人文件夹内的文件。\n\
对于 Windows 用户,你还可以尝试通过资源监视器查看是否有程序占用了该文件,如果是,你可以关闭占用此文件相关程序,或者重启电脑再试。
exception.artifact_malformed=下载的文件正确,无法通过校验。
exception.artifact_malformed=下载的文件无法通过校验。
exception.ssl_handshake=无法建立 SSL 连接,因为当前 Java 虚拟机缺少相关的 SSL 证书。你可以尝试使用其他的 Java 虚拟机启动 HMCL 再试。
extension.bat=Windows 脚本
@@ -314,6 +314,7 @@ fatal.config_loading_failure=Hello Minecraft! Launcher 无法加载配置文件
fatal.migration_requires_manual_reboot=Hello Minecraft! Launcher 即将完成升级,请重新打开 Hello Minecraft! Launcher。
fatal.apply_update_failure=我们很抱歉 Hello Minecraft! Launcher 无法自动完成升级,因为出现了一些问题。\n但你依然可以从 %s 处手动下载 Hello Minecraft! Launcher 来完成升级。\n请考虑向我们反馈该问题。
fatal.samba=如果您正在通过 Samba 共享的文件夹中运行 HMCL,HMCL 可能无法正常工作,请尝试更新您的 Java 或在本地文件夹内运行 HMCL。
fatal.illegal_char=您的用户文件夹路径中存在非法字符‘=’。HMCL 能够运行,但一些功能无法正常使用。\n您将无法使用外置登陆账户以及离线登陆更换皮肤功能,并且在进行多人游戏时可能存在更大的安全隐患。
feedback=反馈
feedback.add=新增反馈
@@ -374,7 +375,7 @@ game.crash.reason.graphics_driver=当前游戏因为你的显卡驱动存在问
详情请点击 <a href="https://hmcl.huangyuhui.net/help/game/graphics-card">此处</a> 查看
game.crash.reason.illegal_access_error=当前游戏因为某些 Mod 的问题,无法继续运行。\n如果你认识:%1$s,你可以更新或删除对应 Mod 再试。
game.crash.reason.jdk_9=当前游戏因为 Java 版本过高,无法继续运行。\n你需要下载安装 Java 8,并在游戏设置中将 Java 设置为 1.8 的版本。
game.crash.reason.jvm_32bit=当前游戏因为内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。如果你的电脑室 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以关闭游戏内存的自动分配,并且把内存限制调节为 1024 MB 或以下。
game.crash.reason.jvm_32bit=当前游戏因为内存分配过大,超过了 32 位 Java 内存限制,无法继续运行。\n如果你的电脑是 64 位系统,请下载安装并更换 64 位 Java。如果你的电脑是 32 位系统,你或许可以重新安装 64 位系统,或换一台新电脑。\n或者,你可以关闭游戏内存的自动分配,并且把内存限制调节为 1024 MB 或以下。
game.crash.reason.loading_crashed_forge=当前游戏因为模组 %1$s (%2$s) 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
game.crash.reason.loading_crashed_fabric=当前游戏因为模组 %1$s 错误,无法继续运行。\n你可以尝试删除或更新该模组以解决问题。
game.crash.reason.memory_exceeded=当前游戏因为分配的内存过大,无法继续运行。\n该问题是由于系统页面文件太小导致的。\n你需要在游戏设置中关闭游戏内存的自动分配,并将游戏内存调低至游戏能正常启动为止。\n你还可以尝试调大系统的页面大小。
@@ -411,7 +412,7 @@ input.url=必须是合法的链接
install=添加游戏
install.change_version=更换版本
install.change_version.confirm=你确定要将 %s 从 %s 更新到 %s 吗?
install.change_version.confirm=你确定要将 %s 从 %s 更换成 %s 吗?
install.failed=安装失败
install.failed.downloading=安装失败,部分文件未能完成下载
install.failed.downloading.detail=未能下载文件:%s
@@ -426,7 +427,7 @@ install.installer.choose=选择 %s 版本
install.installer.depend=需要先安装 %s
install.installer.fabric=Fabric
install.installer.fabric-api=Fabric API
install.installer.fabric-api.warning=警告:Fabric API 是 Mod,将会被安装到新游戏的 Mod 文件夹,请你在安装游戏后不要修改当前游戏的版本隔离/游戏运行路径设置,如果你在之后修改了相关设置,Fabric API 需要被重新安装。
install.installer.fabric-api.warning=警告:Fabric API 是一个 Mod,将会被安装到新游戏的 Mod 文件夹,请你在安装游戏后不要修改当前游戏的版本隔离/游戏运行路径设置,如果你在之后修改了相关设置,Fabric API 需要被重新安装。
install.installer.forge=Forge
install.installer.game=Minecraft
install.installer.incompatible=与 %s 不兼容
@@ -458,9 +459,11 @@ launch.advice.uncorrected=如果您确实希望使用您自定义的 Java 虚拟
launch.advice.different_platform=您的系统是 64 位,但是 Java 是 32 位的,建议您安装 64 位 Java。
launch.advice.forge2760_liteloader=Forge 2760 与 LiteLoader 不兼容。请删除 LiteLoader 或者更换 Forge 至 2773 或更新的版本。是否继续启动?
launch.advice.forge28_2_2_optifine=Forge 28.2.2 或更高版本与 OptiFine 不兼容。请更换 Forge 至 28.2.1 或更低版本。是否继续启动?
launch.advice.forge37_0_60=Forge 低于 37.0.60 的版本不兼容 Java 17。请更新 Forge 到 37.0.60 或更高版本,或者使用 Java 16 启动游戏。是否继续启动?
launch.advice.java8_1_13=Minecraft 1.13 及以上版本只能运行在 Java 8 或更高版本上,请使用 Java 8 或最新版本。
launch.advice.java8_51_1_13=低于 1.8.0_51 的 Java 版本可能会导致 Minecraft 1.13 崩溃。建议您到 https://java.com 安装 Java 8。
launch.advice.java9=低于 1.13 的有安装 Mod 的 Minecraft 版本不支持 Java 9 或更高版本,请使用 Java 8。
launch.advice.modlauncher8=您所使用的 Forge 版本与当前使用的 Java 不兼容。请尝试更新 Forge,或使用 Java 8u312/11.0.13/17.0.1 及更早版本启动。是否继续启动?
launch.advice.newer_java=检测到您未使用 Java 8 及更新版本,Java 8 能使游戏更流畅,而且 Minecraft 1.12 及更新版本和很多 Mod 强制需要 Java 8。
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小 %dMB,所以可能影响游戏体验或无法启动游戏。是否继续启动?
launch.advice.require_newer_java_version=Minecraft %1$s 仅能运行在 Java %2$s 或更高版本上,是否下载?
@@ -500,7 +503,7 @@ launcher.background.default=默认(自动检索启动器同目录下的 backgr
launcher.background.network=网络
launcher.background.translucent=半透明
launcher.cache_directory=文件下载缓存目录
launcher.cache_directory.clean=清理
launcher.cache_directory.clean=清理缓存
launcher.cache_directory.choose=选择文件下载缓存目录
launcher.cache_directory.default=默认(%AppData%/.minecraft 或者 ~/.minecraft)
launcher.cache_directory.disabled=禁用(总是使用游戏路径)
@@ -523,7 +526,7 @@ main_page=主页
message.cancelled=操作被取消
message.confirm=提示
message.copied=已复制到剪贴板
message.copied=已拷贝至剪贴板
message.default=默认
message.doing=请耐心等待
message.downloading=正在下载
@@ -578,8 +581,8 @@ modpack.task.install.error=无法识别该整合包,目前仅支持导入 Curs
modpack.task.install.will=将会安装整合包:
modpack.type.curse=Curse
modpack.type.curse.completion=下载 Curse 整合包相关文件
modpack.type.curse.tolerable_error=但未能完成 Curse 整合包文件的下载,您可以在启动该游戏版本时继续 Curse 整合包文件的下载。由于网络问题,您可能需要重试多次。
modpack.type.curse.error=未能完成 Curse 整合包的下载,请多次重试或设置代理
modpack.type.curse.tolerable_error=但未能完成 Curse 整合包所需的依赖下载,您可以在启动该游戏版本时继续 Curse 整合包文件的下载。由于网络问题,您可能需要重试多次。
modpack.type.curse.error=未能完成 Curse 整合包所需的依赖下载,请多次重试或设置代理
modpack.type.curse.not_found=部分必需文件已经在网络中被删除并且再也无法下载,请尝试该整合包的最新版本或者安装其他整合包。
modpack.type.manual.warning=您大概需要直接解压该整合包,并使用其自带的启动器即可开始游戏,而不需要导入整合包。该整合包不是由启动器导出的整合包,而是人工打包 .minecraft 文件夹而来的,这类整合包通常附带游戏启动器。HMCL 可以尝试导入该整合包,但不保证可用性,是否继续?
modpack.type.mcbbs=我的世界中文论坛整合包标准
@@ -645,7 +648,7 @@ mods.mcmod.page=MC 百科页面
mods.mcmod.search=MC 百科搜索
mods.modrinth=Modrinth
mods.name=名称
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理!
mods.restore=回退
mods.url=官方页面
@@ -657,12 +660,12 @@ multiplayer.download.success=多人联机初始化完成
multiplayer.download.unsupported=多人联机依赖不支持当前系统或平台
multiplayer.exit.after_ready=多人联机会话意外退出,退出码 %d
multiplayer.exit.before_ready=多人联机房间创建失败,cato 退出码 %d
multiplayer.exit.timeout.static_token=无法连接多人联机服务,你的凭证可能无法正常工作,你可以使用空凭证再试。
multiplayer.exit.timeout.dynamic_token=无法连接多人联机服务,你可以在多人联机页面的反馈中反馈问题。
multiplayer.exit.timeout.static_token=无法连接多人联机服务,你的凭证可能无法正常工作,你可以使用空凭证再试。\n你可以联系在线管理员询问鉴权服务器是否正常工作,或者耐心等待十分钟。
multiplayer.exit.timeout.dynamic_token=无法连接多人联机服务,你可以在多人联机页面的反馈中反馈问题。\n可能原因:鉴权服务器被人占满,请等待每个半点,鉴权服务器会自动刷新一次。\n没有耐心且有闲钱的可以考虑购买联机凭证,不受鉴权服务器影响。
multiplayer.hint=多人联机功能处于实验阶段,如果有问题请反馈。
multiplayer.nat=网络检测
multiplayer.nat.failed=检测失败,但你仍然可以继续使用联机功能。
multiplayer.nat.hint=执行网络检测可以让你更清楚你的网络状况是否符合联机功能的需求。检测结果为差的网络可能导致联机失败。
multiplayer.nat.hint=执行网络检测可以让你更清楚你的网络状况是否符合联机功能的需求\n检测结果为差的网络可能导致联机失败
multiplayer.nat.latency=延迟
multiplayer.nat.not_yet_tested=尚未检测
multiplayer.nat.packet_loss_ratio=丢包率
@@ -676,16 +679,16 @@ multiplayer.nat.type.restricted_cone=中(受限圆锥型)
multiplayer.nat.type.symmetric=差(对称型)
multiplayer.nat.type.symmetric_udp_firewall=差(对称型+防火墙)
multiplayer.nat.type.unknown=未知
multiplayer.powered_by=多人联机服务由 这里 (<a href="https://noin.cn">noin.cn</a>) 提供。<a href="https://noin.cn/agreement">用户协议与免责声明</a>
multiplayer.powered_by=多人联机服务由 (<a href="https://noin.cn">noin.cn</a>) 提供 <a href="https://noin.cn/agreement">用户协议与免责声明</a>
multiplayer.relay=桥接服务
multiplayer.relay.hint=Cato 面向较差网络情况提供的网络质量增强优化解决方案。若无法联机请到 这里 的个人中心兑换凭证后启用。
multiplayer.relay.hint=Cato 面向较差网络情况提供的联机质量优化解决方案 无法联机时可在 这里 兑换凭证后启用
multiplayer.report=违法违规举报
multiplayer.session=房间
multiplayer.session.name.format=%1$s 的房间
multiplayer.session.name.motd=HMCL 多人联机房间 - %s
multiplayer.session.close=关闭房间
multiplayer.session.close.warning=关闭房间后,已经加入联机房间的玩家将会断开连接,是否继续?
multiplayer.session.copy_room_code=复制邀请码
multiplayer.session.copy_room_code=拷贝邀请码
multiplayer.session.create=创建房间
multiplayer.session.create.error.dynamic_token=创建联机房间失败,请稍后再试。
multiplayer.session.create.error.static_token=创建联机房间失败,你的凭证可能无法正常工作,你可以使用空凭证再试。
@@ -710,14 +713,14 @@ multiplayer.session.join.error=加入房间失败。如果你或对方的网络
multiplayer.session.join.error.connection=加入房间失败。无法与对方建立连接。如果你或对方的网络类型是差(对称型),可能无法使用联机功能。
multiplayer.session.join.hint=你需要向已经创建好房间的玩家索要邀请码以便加入多人联机房间
multiplayer.session.join.invitation_code=邀请码
multiplayer.session.join.invitation_code.error=邀请码不正确,请向开服玩家获取邀请码
multiplayer.session.join.invitation_code.error=邀请码格式不合法。请向房主获取邀请码,且检查房主和你使用的HMCL是否一致
multiplayer.session.join.kicked=你与房间失去连接:%s。
multiplayer.session.join.kicked.join_acceptance_timeout=对方未能即时同意你的加入申请
multiplayer.session.join.kicked.kicked=你被房主踢出房间。
multiplayer.session.join.kicked.version_not_matched=多人联机功能版本号不一致,请保证连接多人联机功能版本号一致。
multiplayer.session.join.lost_connection=你已与房间失去连接。这可能意味着房主已经解散房间,或者你无法连接至房间。
multiplayer.session.join.port.error=无法找到可用的本地网络端口,请确保 HMCL 拥有绑定本地端口的权限。
multiplayer.session.join.rejected=你被房主拒绝连接。
multiplayer.session.join.rejected=你被房主拒绝连接,或房主忘记同意。
multiplayer.session.join.wait=等待对方同意加入申请。
multiplayer.session.members=房间成员
multiplayer.session.quit=退出房间
@@ -742,7 +745,7 @@ world=世界
world.add=添加世界
world.datapack=管理数据包
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
world.description=%s. 上一次游戏时间: %s. 游戏版本: %s
world.description=%s。 上一次游戏时间: %s。 游戏版本: %s。
world.download=存档下载
world.export=导出此世界
world.export.title=选择该世界的存储位置
@@ -753,7 +756,7 @@ world.game_version=游戏版本
world.import.already_exists=此世界已经存在
world.import.choose=选择要导入的存档压缩包
world.import.failed=无法导入此世界:%s
world.import.invalid=无法识别的存档压缩包
world.import.invalid=无法识别该存档压缩包
world.manage=世界/数据包
world.name=世界名称
world.name.enter=输入世界名称
@@ -861,7 +864,7 @@ settings.game.java_directory.choose=选择 Java 路径
settings.game.java_directory.invalid=Java 路径不正确
settings.game.java_directory.template=%s(%s)
settings.game.management=管理
settings.game.working_directory=版本隔离(修改后请自行移动相关游戏文件,如存档模组配置等)
settings.game.working_directory=版本隔离【建议使用模组时建议开启[各版本独立],改后需移动相关游戏文件,如存档模组等】
settings.game.working_directory.choose=选择运行路径
settings.icon=游戏图标
@@ -918,7 +921,7 @@ settings.type.global.manage=全局游戏设置
settings.type.global.edit=编辑全局版本设置
settings.type.special.enable=启用游戏特定设置(不影响其他游戏版本)
settings.type.special.edit=编辑游戏特定设置
settings.type.special.edit.hint=当前游戏版本 %s 启动了游戏特定设置,本页面选项不对当前游戏生效。点击链接以修改当前游戏设置。
settings.type.special.edit.hint=当前游戏版本 %s 启用了游戏特定设置,因此本页面选项不对该游戏生效。点击链接前往该游戏版本的游戏特定设置页
sponsor=赞助
sponsor.bmclapi=国内下载源由 BMCLAPI 和我的世界中文论坛 (MCBBS) 提供高速下载服务。BMCLAPI 是公益服务,请赞助 BMCLAPI 以获得稳定高速的下载服务,点击此处查阅详细信息。
@@ -1,90 +0,0 @@
[
{
"module": "javafx.base",
"groupId": "org.openjfx",
"artifactId": "javafx-base",
"version": "17",
"sha1": {
"linux": "476c7640c2320244d3156edbbc19a7eda275c1ed",
"linux-arm32-monocle": "1dad2b0479066fc65ce83ae276632b0be4a648f7",
"linux-aarch64": "b62b6cae95847fda1dce4a8a29edcf8a31b85333",
"mac": "61d52392b063be78f7cc1b0fbfc04dda7a835a15",
"mac-aarch64": "d77b645fa3e1a6270783178541944e4b4a457cd2",
"win": "b63b9696f73009ca5002f49ec4db73576e2de168",
"win-x86": "d35c89f5cb5f9dd8c54c69b4a153a94377e60931"
}
},
{
"module": "javafx.graphics",
"groupId": "org.openjfx",
"artifactId": "javafx-graphics",
"version": "17",
"sha1": {
"linux": "88dc59568c14ec37ea1c42b3ba472d78b4b1030a",
"linux-arm32-monocle": "44284bba939b1249fcc3f5502a70bd48e88e1fa9",
"linux-aarch64": "40bd7094b8f43d4637a8a274b4b199835d6cfd5c",
"mac": "8b1d1124ce83e874db390016913fba7708f3592d",
"mac-aarch64": "b6326f61f7e0effbf733d62e83b8317eb8e768b4",
"win": "2c56d252b97eba8b42f049c6ce145172d744ce08",
"win-x86": "c4d74a11a7ffb182fb43803756e678c87876a0a4"
}
},
{
"module": "javafx.controls",
"groupId": "org.openjfx",
"artifactId": "javafx-controls",
"version": "17",
"sha1": {
"linux": "851a1896fbd8548fb3671aa689b305b2cfad956a",
"linux-arm32-monocle": "9add14fda362a1a2d1cd3a84de60d67da2d7a5ae",
"linux-aarch64": "eb4116ebad81183980dde74b072a15b0f6c795ef",
"mac": "e73acf695ffccb4f904ff7b76de8999eefb547c2",
"mac-aarch64": "2254b351c1e6ce2ac9adcd3480fed883cebbb08b",
"win": "efc0f4edde079c22987064922530c67fa1350a9b",
"win-x86": "c9997e885f2dd71d8a4a7b89bc510923d2d412d6"
}
},
{
"module": "javafx.fxml",
"groupId": "org.openjfx",
"artifactId": "javafx-fxml",
"version": "17",
"sha1": {
"linux": "0a6f029f4fab0f01f00a838de1e65e20c6a92f02",
"linux-arm32-monocle": "922b73ca1813dfa87e66613d2904286bdd06e7f1",
"linux-aarch64": "7cd2f92f399f48acedc85636c9f07f6634d29669",
"mac": "54026a476bb95426e2e159bcf25bbf0832d2b7bf",
"mac-aarch64": "09ba33b0d9c19e9dcfd76fe7d95748a933e09b3b",
"win": "08d79ed85fa9dde644d4b37d93a02a2ef2818769",
"win-x86": "3bde4e98cee0a464f327b9e983aded3017fddbc0"
}
},
{
"module": "javafx.media",
"groupId": "org.openjfx",
"artifactId": "javafx-media",
"version": "17",
"sha1": {
"linux": "e3bb829ce1447a37a8b0b669a446e0414e54e5c7",
"linux-aarch64": "7e7a50e66e44b902c624680601d5336d7fd16614",
"mac": "ef463caf627c4c97b8e5c6a5ae16050c56aed966",
"mac-aarch64": "970178b32e17650591698bae9f23bf5ce1295bb6",
"win": "6012cc30ced1539f167b64959cb9d6a4283f7aa7",
"win-x86": "8568e1edcfc803a75349124b21a27c0d609b3870"
}
},
{
"module": "javafx.web",
"groupId": "org.openjfx",
"artifactId": "javafx-web",
"version": "17",
"sha1": {
"linux": "bbd71beaf561b72f0d3eb6268d3114573b7799c3",
"linux-aarch64": "3cd86d9593e63ce7cbd5f9b5a09f8dcc0bbd7fdb",
"mac": "bf188c003e54ffb4cff6a9be7ce437fa269964fe",
"mac-aarch64": "a9408d443fa95cd3e2b1f170a3abeaace33d3af5",
"win": "be4a308dbcc947af2a9d9c42a72fd7f6d0ba77b9",
"win-x86": "3d93e1801c720747d4551de26ef5869ff9eacabc"
}
}
]
@@ -101,7 +101,7 @@ public abstract class SkinCanvasSupport implements Consumer<SkinCanvas> {
Stage stage = new Stage();
stage.setTitle(title);
SkinCanvas canvas = Test.createSkinCanvas();
canvas.updateSkin(new Image(input), skin.getName().contains("[alex]"));
canvas.updateSkin(new Image(input), skin.getName().contains("[alex]"), null);
Scene scene = new Scene(canvas);
stage.setScene(scene);
stage.show();
@@ -17,7 +17,7 @@ public class Test extends Application {
public static SkinCanvas createSkinCanvas() {
SkinCanvas canvas = new SkinCanvas(SkinCanvas.STEVE, 400, 400, true);
canvas.getAnimationplayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
canvas.getAnimationPlayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
FunctionHelper.alwaysB(Consumer<SkinCanvas>::accept, canvas, new SkinCanvasSupport.Mouse(.5), new SkinCanvasSupport.Drag(TITLE));
return canvas;
}
-27
View File
@@ -1,27 +0,0 @@
plugins {
id 'java-library'
}
dependencies {
api group: 'com.google.code.gson', name: 'gson', version: '2.8.1'
api group: 'com.moandjiezana.toml', name: 'toml4j', version: '0.7.2'
api group: 'org.tukaani', name: 'xz', version: '1.8'
api(group: 'org.hildan.fxgson', name: 'fx-gson', version: '3.1.0') {
exclude group: 'org.jetbrains', module: 'annotations'
}
api group: 'org.jenkins-ci', name: 'constant-pool-scanner', version: '1.2'
api group: 'com.github.steveice10', name: 'opennbt', version: '1.1'
api group: 'com.nqzero', name: 'permit-reflect', version: '0.3'
api group: 'org.nanohttpd', name: 'nanohttpd', version: '2.3.1'
api group: 'org.apache.commons', name: 'commons-compress', version: '1.21'
api group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
compileOnlyApi group: 'org.jetbrains', name: 'annotations', version: '16.0.3'
}
tasks.processResources {
dependsOn ':log4j-patch:jar'
into('assets/game') {
from project(':log4j-patch').file("build/libs")
}
}
+27
View File
@@ -0,0 +1,27 @@
plugins {
`java-library`
}
dependencies {
api("com.google.code.gson:gson:2.8.1")
api("com.moandjiezana.toml:toml4j:0.7.2")
api("org.tukaani:xz:1.8")
api("org.hildan.fxgson:fx-gson:3.1.0") {
exclude(group = "org.jetbrains", module = "annotations")
}
api("org.jenkins-ci:constant-pool-scanner:1.2")
api("com.github.steveice10:opennbt:1.1")
api("com.nqzero:permit-reflect:0.3")
api("org.nanohttpd:nanohttpd:2.3.1")
api("org.apache.commons:commons-compress:1.21")
api("org.apache.commons:commons-lang3:3.12.0")
compileOnlyApi("org.jetbrains:annotations:16.0.3")
}
tasks.processResources {
dependsOn(":log4j-patch:jar")
into("assets/game") {
from(project(":log4j-patch").file("build/libs"))
}
}
@@ -159,7 +159,7 @@ public class OfflineAccount extends Account {
try {
server.addCharacter(new YggdrasilServer.Character(uuid, username, skin.load(username).run()));
} catch (IOException e) {
throw e;
// ignore
} catch (Exception e) {
throw new IOException(e);
}
@@ -283,7 +283,7 @@ public class MaintainTask extends Task<Version> {
public static Version unique(Version version) {
List<Library> libraries = new ArrayList<>();
SimpleMultimap<String, Integer> multimap = new SimpleMultimap<String, Integer>(HashMap::new, LinkedList::new);
SimpleMultimap<String, Integer> multimap = new SimpleMultimap<String, Integer>(HashMap::new, ArrayList::new);
for (Library library : version.getLibraries()) {
String id = library.getGroupId() + ":" + library.getArtifactId();
@@ -24,8 +24,8 @@ import org.jackhuang.hmcl.task.Task;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -38,7 +38,7 @@ public final class FabricAPIInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final FabricAPIRemoteVersion remote;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricAPIRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -23,11 +23,8 @@ import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
import org.jackhuang.hmcl.util.Lang;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.jackhuang.hmcl.util.Lang.wrap;
@@ -55,16 +52,4 @@ public class FabricAPIVersionList extends VersionList<FabricAPIRemoteVersion> {
}
}));
}
@Override
protected Collection<FabricAPIRemoteVersion> getVersionsImpl(String gameVersion) {
Matcher matcher = GAME_VERSION_PATTERN.matcher(gameVersion);
if (matcher.find()) {
return super.getVersionsImpl(String.format("%s.%s", matcher.group("major"), matcher.group("minor")));
}
return super.getVersionsImpl(gameVersion);
}
private static final Pattern FABRIC_VERSION_PATTERN = Pattern.compile("^(?<version>[0-9.]+)\\+(build\\.(?<build>\\d+)-)?(?<mcversion>[0-9.]+)$");
private static final Pattern GAME_VERSION_PATTERN = Pattern.compile("^(?<major>[0-9]+)\\.(?<minor>[0-9]+)");
}
@@ -45,7 +45,7 @@ public final class FabricInstallTask extends Task<Version> {
private final Version version;
private final FabricRemoteVersion remote;
private final GetTask launchMetaTask;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
public FabricInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -102,7 +102,10 @@ public final class ForgeInstallTask extends Task<Version> {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) {
// Forge 1.13 is not compatible with fabric.
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass))
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER);
} else {
// Forge 1.12 and older versions is compatible with vanilla and launchwrapper.
@@ -182,8 +182,8 @@ public class ForgeNewInstallTask extends Task<Version> {
private final DefaultGameRepository gameRepository;
private final Version version;
private final Path installer;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>(1);
private final List<Task<?>> dependencies = new ArrayList<>(1);
private ForgeNewInstallProfile profile;
private List<Processor> processors;
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.*;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
@@ -41,7 +41,7 @@ public class ForgeOldInstallTask extends Task<Version> {
private final Version version;
private final Path installer;
private final String selfVersion;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) {
this.dependencyManager = dependencyManager;
@@ -34,8 +34,8 @@ import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
@@ -50,8 +50,8 @@ public final class GameAssetDownloadTask extends Task<Void> {
private final AssetIndexInfo assetIndexInfo;
private final Path assetIndexFile;
private final boolean integrityCheck;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>(1);
private final List<Task<?>> dependencies = new ArrayList<>();
/**
* Constructor.
@@ -33,7 +33,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
@@ -49,7 +49,7 @@ public final class GameAssetIndexDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final boolean forceDownloading;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
/**
* Constructor.
@@ -25,8 +25,8 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CacheRepository;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -37,7 +37,7 @@ public final class GameDownloadTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager;
@@ -23,9 +23,9 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
@@ -37,7 +37,7 @@ public class GameInstallTask extends Task<Version> {
private final Version version;
private final GameRemoteVersion remote;
private final VersionJsonDownloadTask downloadTask;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
public GameInstallTask(DefaultDependencyManager dependencyManager, Version version, GameRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
@@ -45,7 +45,7 @@ public final class GameLibrariesTask extends Task<Void> {
private final Version version;
private final boolean integrityCheck;
private final List<Library> libraries;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
/**
* Constructor.
@@ -29,8 +29,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -41,7 +41,7 @@ public final class GameVerificationFixTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager;
@@ -24,8 +24,8 @@ import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -35,8 +35,8 @@ import java.util.List;
public final class VersionJsonDownloadTask extends Task<String> {
private final String gameVersion;
private final DefaultDependencyManager dependencyManager;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>(1);
private final List<Task<?>> dependencies = new ArrayList<>(1);
private final VersionList<?> gameVersionList;
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
@@ -23,9 +23,9 @@ import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Lang;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
@@ -38,8 +38,8 @@ public final class LiteLoaderInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final LiteLoaderRemoteVersion remote;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -56,8 +56,8 @@ public final class OptiFineInstallTask extends Task<Version> {
private final Version version;
private final OptiFineRemoteVersion remote;
private final Path installer;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>(0);
private final List<Task<?>> dependencies = new ArrayList<>(1);
private Path dest;
private final Library optiFineLibrary;
@@ -131,7 +131,7 @@ public final class OptiFineInstallTask extends Task<Version> {
!LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER);
List<Library> libraries = new LinkedList<>();
List<Library> libraries = new ArrayList<>(4);
libraries.add(optiFineLibrary);
FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath());
@@ -110,7 +110,7 @@ public final class Arguments {
public static final List<Argument> DEFAULT_GAME_ARGUMENTS;
static {
List<Argument> jvm = new LinkedList<>();
List<Argument> jvm = new ArrayList<>(8);
jvm.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.WINDOWS))), Collections.singletonList("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump")));
jvm.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, new OSRestriction(OperatingSystem.WINDOWS, "^10\\."))), Arrays.asList("-Dos.name=Windows 10", "-Dos.version=10.0")));
jvm.add(new StringArgument("-Djava.library.path=${natives_directory}"));
@@ -120,7 +120,7 @@ public final class Arguments {
jvm.add(new StringArgument("${classpath}"));
DEFAULT_JVM_ARGUMENTS = Collections.unmodifiableList(jvm);
List<Argument> game = new LinkedList<>();
List<Argument> game = new ArrayList<>(1);
game.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, null, Collections.singletonMap("has_custom_resolution", true))), Arrays.asList("--width", "${resolution_width}", "--height", "${resolution_height}")));
DEFAULT_GAME_ARGUMENTS = Collections.unmodifiableList(game);
}
@@ -136,10 +136,6 @@ public class DefaultGameRepository implements GameRepository {
@Override
public File getVersionJar(Version version) {
Version v = version.resolve(this);
File currentVersionJar = new File(getVersionRoot(v.getId()), v.getId() + ".jar");
if (currentVersionJar.exists()) {
return currentVersionJar;
}
String id = Optional.ofNullable(v.getJar()).orElse(v.getId());
return new File(getVersionRoot(id), id + ".jar");
}
@@ -17,8 +17,8 @@
*/
package org.jackhuang.hmcl.game;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
@@ -36,7 +36,7 @@ public final class ExtractRules {
}
public ExtractRules(List<String> exclude) {
this.exclude = new LinkedList<>(exclude);
this.exclude = new ArrayList<>(exclude);
}
public List<String> getExclude() {
@@ -38,6 +38,7 @@ public class GameJavaVersion {
return majorVersion;
}
public static final GameJavaVersion JAVA_17 = new GameJavaVersion("java-runtime-beta", 17);
public static final GameJavaVersion JAVA_16 = new GameJavaVersion("java-runtime-alpha", 16);
public static final GameJavaVersion JAVA_8 = new GameJavaVersion("jre-legacy", 8);
}
@@ -36,6 +36,20 @@ public enum JavaVersionConstraint {
// Minecraft>=1.17 requires Java 16
VANILLA_JAVA_16(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.17", JavaVersionConstraint.MAX), versionRange("16", JavaVersionConstraint.MAX)),
VANILLA_JAVA_17(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.18", JavaVersionConstraint.MAX), versionRange("17", JavaVersionConstraint.MAX)),
// Minecraft<=1.17.1+Forge[37.0.0,37.0.60) not compatible with Java 17
MODDED_JAVA_16(JavaVersionConstraint.RULE_SUGGESTED, versionIs("1.17.1"), versionRange("16", "16.999")) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion) {
if (version == null) return false;
VersionNumber forgePatchVersion = LibraryAnalyzer.analyze(version)
.getVersion(LibraryAnalyzer.LibraryType.FORGE)
.map(LibraryAnalyzer.LibraryType.FORGE::patchVersion)
.map(VersionNumber::asVersion)
.orElse(null);
return forgePatchVersion != null && forgePatchVersion.compareTo(VersionNumber.asVersion("37.0.60")) < 0;
}
},
// Minecraft>=1.13 requires Java 8
VANILLA_JAVA_8(JavaVersionConstraint.RULE_MANDATORY, versionRange("1.13", JavaVersionConstraint.MAX), versionRange("1.8", JavaVersionConstraint.MAX)),
// Minecraft>=1.7.10+Forge accepts Java 8
@@ -113,7 +127,53 @@ public enum JavaVersionConstraint {
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
return javaVersion.getArchitecture().isX86();
}
};
},
// Minecraft 1.16+Forge with crash because JDK-8273826
MODLAUNCHER_8(JavaVersionConstraint.RULE_SUGGESTED, versionRange("1.16.3", "1.17.1"), versionRange(JavaVersionConstraint.MIN, JavaVersionConstraint.MAX)) {
@Override
protected boolean appliesToVersionImpl(VersionNumber gameVersionNumber, @Nullable Version version,
@Nullable JavaVersion javaVersion) {
if (version == null || javaVersion == null) return false;
VersionNumber forgePatchVersion = LibraryAnalyzer.analyze(version)
.getVersion(LibraryAnalyzer.LibraryType.FORGE)
.map(LibraryAnalyzer.LibraryType.FORGE::patchVersion)
.map(VersionNumber::asVersion)
.orElse(null);
if (forgePatchVersion == null) {
return false;
}
switch (gameVersionNumber.toString()) {
case "1.16.3":
return forgePatchVersion.compareTo(VersionNumber.asVersion("34.1.27")) >= 0;
case "1.16.4":
return true;
case "1.16.5":
return forgePatchVersion.compareTo(VersionNumber.asVersion("36.2.23")) <= 0;
case "1.17.1":
return versionRange("37.0.60", "37.0.75").contains(forgePatchVersion);
default:
return false;
}
}
@Override
public boolean checkJava(VersionNumber gameVersionNumber, Version version, JavaVersion javaVersion) {
int parsedJavaVersion = javaVersion.getParsedVersion();
if (parsedJavaVersion > 17) {
return false;
} else if (parsedJavaVersion == 8) {
return javaVersion.getVersionNumber().compareTo(VersionNumber.asVersion("1.8.0_321")) < 0;
} else if (parsedJavaVersion == 11) {
return javaVersion.getVersionNumber().compareTo(VersionNumber.asVersion("11.0.14")) < 0;
} else if (parsedJavaVersion == 15) {
return javaVersion.getVersionNumber().compareTo(VersionNumber.asVersion("15.0.6")) < 0;
} else if (parsedJavaVersion == 17) {
return javaVersion.getVersionNumber().compareTo(VersionNumber.asVersion("17.0.2")) < 0;
} else {
return true;
}
}
};;
private final int type;
private final Range<VersionNumber> gameVersionRange;
@@ -227,6 +287,10 @@ public enum JavaVersionConstraint {
return Range.between(VersionNumber.asVersion(fromInclusive), VersionNumber.asVersion(toExclusive));
}
static Range<VersionNumber> versionIs(String version) {
return Range.is(VersionNumber.asVersion(version));
}
public static class VersionRanges {
private final Range<VersionNumber> mandatory;
private final Range<VersionNumber> suggested;
@@ -40,6 +40,7 @@ public class LaunchOptions implements Serializable {
private String profileName;
private List<String> gameArguments = new ArrayList<>();
private List<String> javaArguments = new ArrayList<>();
private List<String> javaAgents = new ArrayList<>(0);
private Integer minMemory;
private Integer maxMemory;
private Integer metaspace;
@@ -114,6 +115,11 @@ public class LaunchOptions implements Serializable {
return Collections.unmodifiableList(javaArguments);
}
@NotNull
public List<String> getJavaAgents() {
return Collections.unmodifiableList(javaAgents);
}
/**
* The minimum memory that the JVM can allocate.
*/
@@ -310,6 +316,10 @@ public class LaunchOptions implements Serializable {
return options.javaArguments;
}
public List<String> getJavaAgents() {
return options.javaAgents;
}
/**
* The minimum memory that the JVM can allocate.
*/
@@ -460,6 +470,12 @@ public class LaunchOptions implements Serializable {
return this;
}
public Builder setJavaAgents(List<String> javaAgents) {
options.javaAgents.clear();
options.javaAgents.addAll(javaAgents);
return this;
}
public Builder setMinMemory(Integer minMemory) {
options.minMemory = minMemory;
return this;
@@ -42,7 +42,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.function.Supplier;
import java.util.logging.Level;
@@ -126,8 +125,6 @@ public class DefaultLauncher extends Launcher {
}
}
Optional<Library> log4j = findLog4j();
// JVM Args
if (!options.isNoGeneratedJVMArgs()) {
appendJvmArgs(res);
@@ -192,13 +189,6 @@ public class DefaultLauncher extends Launcher {
res.addDefault("-Dfml.ignoreInvalidMinecraftCertificates=", "true");
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
if (log4j.isPresent()) {
String enableJndi = res.addDefault("-Dlog4j2.enableJndi=", "false");
if (!"-Dlog4j2.enableJndi=true".equals(enableJndi)) {
res.add("-javaagent:" + repository.getLibraryFile(version, LOG4J_PATCH_AGENT).getAbsolutePath() + "=" + log4j.get().getVersion().startsWith("2.0-beta"));
}
}
}
// Fix RCE vulnerability of log4j2
@@ -207,7 +197,7 @@ public class DefaultLauncher extends Launcher {
res.addDefault("-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=", "false");
String formatMsgNoLookups = res.addDefault("-Dlog4j2.formatMsgNoLookups=", "true");
if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && log4j.isPresent()) {
if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && isUsingLog4j()) {
res.addDefault("-Dlog4j.configurationFile=", getLog4jConfigurationFile().getAbsolutePath());
}
@@ -259,9 +249,13 @@ public class DefaultLauncher extends Launcher {
if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.getJvm() != null && !argumentsFromAuthInfo.getJvm().isEmpty())
res.addAll(Arguments.parseArguments(argumentsFromAuthInfo.getJvm(), configuration));
for (String javaAgent : options.getJavaAgents()) {
res.add("-javaagent:" + javaAgent);
}
res.add(version.getMainClass());
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration));
Map<String, Boolean> features = getFeatures();
version.getArguments().map(Arguments::getGame).ifPresent(arguments -> res.addAll(Arguments.parseArguments(arguments, configuration, features)));
@@ -365,17 +359,8 @@ public class DefaultLauncher extends Launcher {
}
}
private static final Library LOG4J_PATCH_AGENT = new Library(new Artifact("org.glavo", "log4j-patch-agent", "1.0"));
private Optional<Library> findLog4j() {
return version.getLibraries().stream()
.filter(it -> it.is("org.apache.logging.log4j", "log4j-core")
&& (VersionNumber.VERSION_COMPARATOR.compare(it.getVersion(), "2.17") < 0 || "2.0-beta9".equals(it.getVersion())))
.findFirst();
}
private boolean isLog4jUnsafe(Library log4j) {
return VersionNumber.VERSION_COMPARATOR.compare(log4j.getVersion(), "2.17") < 0;
private boolean isUsingLog4j() {
return VersionNumber.VERSION_COMPARATOR.compare(repository.getGameVersion(version).orElse("1.7"), "1.7") >= 0;
}
public File getLog4jConfigurationFile() {
@@ -396,17 +381,6 @@ public class DefaultLauncher extends Launcher {
}
}
public void extractLog4jAgent() throws IOException {
Path log4jPatchPath = repository.getLibraryFile(version, LOG4J_PATCH_AGENT).toPath();
String patchName = LOG4J_PATCH_AGENT.getArtifactId() + "-" + LOG4J_PATCH_AGENT.getVersion();
if (Files.notExists(log4jPatchPath)) {
try (InputStream input = DefaultLauncher.class.getResourceAsStream("/assets/game/" + patchName + ".jar")) {
Files.createDirectories(log4jPatchPath.getParent());
Files.copy(input, log4jPatchPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
protected Map<String, String> getConfigurations() {
return mapOf(
// defined by Minecraft official launcher
@@ -466,12 +440,8 @@ public class DefaultLauncher extends Launcher {
decompressNatives(nativeFolder);
}
Optional<Library> log4j = findLog4j();
if (log4j.isPresent()) {
if (isUsingLog4j())
extractLog4jConfigurationFile();
if (isLog4jUnsafe(log4j.get()))
extractLog4jAgent();
}
File runDirectory = repository.getRunDirectory(version.getId());
@@ -542,12 +512,8 @@ public class DefaultLauncher extends Launcher {
decompressNatives(nativeFolder);
}
Optional<Library> log4j = findLog4j();
if (log4j.isPresent()) {
if (isUsingLog4j())
extractLog4jConfigurationFile();
if (isLog4jUnsafe(log4j.get()))
extractLog4jAgent();
}
String scriptExtension = FileUtils.getExtension(scriptFile);
boolean usePowerShell = "ps1".equals(scriptExtension);
@@ -27,7 +27,7 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
@@ -57,7 +57,7 @@ public final class MinecraftInstanceTask<T> extends Task<ModpackConfiguration<T>
@Override
public void execute() throws Exception {
List<ModpackConfiguration.FileInformation> overrides = new LinkedList<>();
List<ModpackConfiguration.FileInformation> overrides = new ArrayList<>();
try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(encoding).build()) {
Path root = fs.getPath(subDirectory);
@@ -25,7 +25,16 @@ import java.util.stream.Stream;
public interface RemoteModRepository {
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
enum SortType {
DATE_CREATED,
POPULARITY,
LAST_UPDATED,
NAME,
AUTHOR,
TOTAL_DOWNLOADS
}
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, SortType sort)
throws IOException;
Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException;
@@ -32,8 +32,8 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -52,7 +52,7 @@ public final class CurseCompletionTask extends Task<Void> {
private final ModManager modManager;
private final String version;
private CurseManifest manifest;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
private final AtomicBoolean allNameKnown = new AtomicBoolean(true);
private final AtomicInteger finished = new AtomicInteger(0);
@@ -63,10 +63,10 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
}
@Override
public Stream<RemoteMod> search(String gameVersion, RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, int sort) throws IOException {
public Stream<RemoteMod> search(String gameVersion, RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sort) throws IOException {
int categoryId = 0;
if (category != null) categoryId = ((Category) category.getSelf()).getId();
return searchPaginated(gameVersion, categoryId, pageOffset, pageSize, searchFilter, sort).stream()
return searchPaginated(gameVersion, categoryId, pageOffset, pageSize, searchFilter, sort.ordinal()).stream()
.map(CurseAddon::toMod);
}
@@ -33,8 +33,8 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
@@ -52,8 +52,8 @@ public final class CurseInstallTask extends Task<Void> {
private final String name;
private final File run;
private final ModpackConfiguration<CurseManifest> config;
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new ArrayList<>(4);
private final List<Task<?>> dependencies = new ArrayList<>(1);
/**
* Constructor.
@@ -23,8 +23,8 @@ import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.Validation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
@@ -47,7 +47,7 @@ public final class CurseManifestMinecraft implements Validation {
public CurseManifestMinecraft(String gameVersion, List<CurseManifestModLoader> modLoaders) {
this.gameVersion = gameVersion;
this.modLoaders = new LinkedList<>(modLoaders);
this.modLoaders = new ArrayList<>(modLoaders);
}
public String getGameVersion() {
@@ -63,7 +63,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
private final File configurationFile;
private ModpackConfiguration<McbbsModpackManifest> configuration;
private McbbsModpackManifest manifest;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
private final AtomicBoolean allNameKnown = new AtomicBoolean(true);
private final AtomicInteger finished = new AtomicInteger(0);
@@ -33,7 +33,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -47,8 +47,8 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
private final boolean update;
private final DefaultGameRepository repository;
private final MinecraftInstanceTask<McbbsModpackManifest> instanceTask;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(2);
private final List<Task<?>> dependents = new ArrayList<>(4);
public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, McbbsModpackManifest manifest, String name) {
this.dependencyManager = dependencyManager;
@@ -29,8 +29,8 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class McbbsModpackRemoteInstallTask extends Task<Void> {
@@ -38,8 +38,8 @@ public class McbbsModpackRemoteInstallTask extends Task<Void> {
private final String name;
private final DefaultDependencyManager dependency;
private final DefaultGameRepository repository;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
private final List<Task<?>> dependents = new ArrayList<>(1);
private final McbbsModpackManifest manifest;
public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, McbbsModpackManifest manifest, String name) {
@@ -52,11 +52,29 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
private ModrinthRemoteModRepository() {
}
public List<ModResult> searchPaginated(String gameVersion, int pageOffset, int pageSize, String searchFilter) throws IOException {
private static String convertSortType(SortType sortType) {
switch (sortType) {
case DATE_CREATED:
return "newest";
case POPULARITY:
case NAME:
case AUTHOR:
return "relevance";
case LAST_UPDATED:
return "updated";
case TOTAL_DOWNLOADS:
return "downloads";
default:
throw new IllegalArgumentException("Unsupported sort type " + sortType);
}
}
public List<ModResult> searchPaginated(String gameVersion, int pageOffset, int pageSize, String searchFilter, SortType sort) throws IOException {
Map<String, String> query = mapOf(
pair("query", searchFilter),
pair("offset", Integer.toString(pageOffset)),
pair("limit", Integer.toString(pageSize))
pair("limit", Integer.toString(pageSize)),
pair("index", convertSortType(sort))
);
if (StringUtils.isNotBlank(gameVersion)) {
query.put("version", "versions=" + gameVersion);
@@ -68,8 +86,8 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
}
@Override
public Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort) throws IOException {
return searchPaginated(gameVersion, pageOffset, pageSize, searchFilter).stream()
public Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, SortType sort) throws IOException {
return searchPaginated(gameVersion, pageOffset, pageSize, searchFilter, sort).stream()
.map(ModResult::toMod);
}
@@ -40,7 +40,6 @@ import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
@@ -55,8 +54,8 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
private final MultiMCInstanceConfiguration manifest;
private final String name;
private final DefaultGameRepository repository;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
private final List<Task<?>> dependents = new ArrayList<>(4);
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
this.zipFile = zipFile;
@@ -53,7 +53,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
private ModpackConfiguration<ServerModpackManifest> manifest;
private GetTask dependent;
private ServerModpackManifest remoteManifest;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) {
this(dependencyManager, version, null);
@@ -32,7 +32,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
public class ServerModpackLocalInstallTask extends Task<Void> {
@@ -42,8 +42,8 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
private final ServerModpackManifest manifest;
private final String name;
private final DefaultGameRepository repository;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>();
private final List<Task<?>> dependents = new ArrayList<>(4);
public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, ServerModpackManifest manifest, String name) {
this.zipFile = zipFile;
@@ -29,8 +29,8 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class ServerModpackRemoteInstallTask extends Task<Void> {
@@ -38,8 +38,8 @@ public class ServerModpackRemoteInstallTask extends Task<Void> {
private final String name;
private final DefaultDependencyManager dependency;
private final DefaultGameRepository repository;
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new ArrayList<>(1);
private final List<Task<?>> dependents = new ArrayList<>(1);
private final ServerModpackManifest manifest;
public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, ServerModpackManifest manifest, String name) {
@@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicInteger;
public abstract class TaskExecutor {
protected final Task<?> firstTask;
protected final List<TaskListener> taskListeners = new LinkedList<>();
protected final List<TaskListener> taskListeners = new ArrayList<>();
protected final AtomicInteger totTask = new AtomicInteger(0);
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
protected Exception exception;
@@ -199,13 +199,13 @@ public final class StringUtils {
public static List<String> tokenize(String str) {
if (str == null)
return new LinkedList<>();
return new ArrayList<>();
else
return tokenize(str, " \t\n\r\f");
}
public static List<String> tokenize(String str, String delim) {
LinkedList<String> result = new LinkedList<>();
ArrayList<String> result = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(str, delim);
while (tokenizer.hasMoreTokens()) {
delim = tokenizer.nextToken();
@@ -28,7 +28,7 @@ import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -393,7 +393,7 @@ public final class FileUtils {
}
public static List<File> listFilesByExtension(File file, String extension) {
List<File> result = new LinkedList<>();
List<File> result = new ArrayList<>();
File[] files = file.listFiles();
if (files != null)
for (File it : files)
@@ -35,7 +35,7 @@ public final class CommandBuilder {
private static final Pattern UNSTABLE_BOOLEAN_OPTION_PATTERN = Pattern.compile("-XX:(?<value>[+\\-])(?<key>[a-zA-Z0-9]+)");
private final OperatingSystem os;
private final List<Item> raw = new LinkedList<>();
private final List<Item> raw = new ArrayList<>();
public CommandBuilder() {
this(OperatingSystem.CURRENT_OS);
@@ -34,12 +34,12 @@ public class ManagedProcess {
private final String classpath;
private final Map<String, Object> properties = new HashMap<>();
private final Queue<String> lines = new ConcurrentLinkedQueue<>();
private final List<Thread> relatedThreads = new LinkedList<>();
private final List<Thread> relatedThreads = new ArrayList<>();
/**
* Constructor.
*
* @param process the raw system process that this instance manages.
* @param process the raw system process that this instance manages.
* @param commands the command line of {@code process}.
*/
public ManagedProcess(Process process, List<String> commands) {
@@ -51,8 +51,8 @@ public class ManagedProcess {
/**
* Constructor.
*
* @param process the raw system process that this instance manages.
* @param commands the command line of {@code process}.
* @param process the raw system process that this instance manages.
* @param commands the command line of {@code process}.
* @param classpath the classpath of java process
*/
public ManagedProcess(Process process, List<String> commands, String classpath) {
@@ -111,11 +111,11 @@ public class ManagedProcess {
/**
* Add related thread.
*
* <p>
* If a thread is monitoring this raw process,
* you are required to add the instance by this method.
*/
public void addRelatedThread(Thread thread) {
public synchronized void addRelatedThread(Thread thread) {
relatedThreads.add(thread);
}
@@ -146,7 +146,7 @@ public class ManagedProcess {
destroyRelatedThreads();
}
public void destroyRelatedThreads() {
public synchronized void destroyRelatedThreads() {
relatedThreads.forEach(Thread::interrupt);
}
@@ -180,7 +180,7 @@ public enum OperatingSystem {
.map(bytes -> (int) (bytes / 1024 / 1024))
.orElse(1024);
SUGGESTED_MEMORY = (int) (Math.round(1.0 * TOTAL_MEMORY / 4.0 / 128.0) * 128);
SUGGESTED_MEMORY = TOTAL_MEMORY >= 32768 ? 8192 : (int) (Math.round(1.0 * TOTAL_MEMORY / 4.0 / 128.0) * 128);
// setup the invalid names
if (CURRENT_OS == WINDOWS) {
@@ -258,8 +258,11 @@ public enum OperatingSystem {
public static void forceGC() {
System.gc();
System.runFinalization();
System.gc();
try {
System.runFinalization();
System.gc();
} catch (NoSuchMethodError ignored) {
}
}
public static Path getWorkingDirectory(String folder) {
Binary file not shown.
Submodule JSTUN deleted from 08ab1f8483.
-221
View File
@@ -1,221 +0,0 @@
buildscript {
repositories {
mavenCentral()
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath group: 'com.google.code.gson', name: 'gson', version: '2.8.1'
}
}
plugins {
id 'checkstyle'
}
group 'org.jackhuang'
version '3.0'
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven-publish'
apply plugin: 'checkstyle'
repositories {
flatDir name: 'libs', dirs: rootProject.file('lib')
mavenCentral()
maven { url 'https://jitpack.io' }
}
checkstyle {
sourceSets = []
}
tasks.withType(Checkstyle) {
exclude 'de/javawi/jstun'
}
sourceCompatibility = 1.8
compileJava.options.encoding = "UTF-8"
compileTestJava.options.encoding = "UTF-8"
dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
publishing {
publications {
maven(MavenPublication) {
from components.java
}
}
repositories {
mavenLocal()
}
}
}
defaultTasks 'clean', 'build'
var jfxModules = ['base', 'graphics', 'controls', 'fxml', 'media', 'web']
var jfxArches = ['linux', 'linux-arm32-monocle', 'linux-aarch64', 'mac', 'mac-aarch64', 'win', 'win-x86']
var jfxVersion = "17"
var jfxRepos = ['https://repo1.maven.org/maven2', 'https://maven.aliyun.com/repository/central']
var jfxDependenciesFile = file('HMCL/src/main/resources/assets/openjfx-dependencies.json')
var jfxUnsupported = [
'linux-arm32-monocle': ['media', 'web']
]
var jfxInClasspath = false
try {
Class.forName("javafx.application.Application", false, this.getClass().getClassLoader())
jfxInClasspath = true
} catch (Throwable ignored) {
}
if (!jfxInClasspath && JavaVersion.current() >= JavaVersion.VERSION_11) {
String os = null
String arch = null
String osName = System.getProperty("os.name").toLowerCase(Locale.US)
if (osName.contains("win"))
os = 'win'
else if (osName.contains("mac"))
os = 'mac'
else if (osName.contains("solaris") || osName.contains("linux") || osName.contains("unix") || osName.contains("sunos"))
os = 'linux'
else
os = null
String archName = System.getProperty("os.arch").toLowerCase(Locale.US)
switch (archName) {
case "x86_64":
case "x86-64":
case "amd64":
arch = ''
break
case "x86":
case "x86_32":
case "x86-32":
case "i386":
case "i486":
case "i586":
case "i686":
case "i86pc":
arch = '-x86'
break
case "aarch64":
arch = '-aarch64';
break
}
String platform = "$os$arch"
if (os != null && arch != null && jfxArches.contains(platform)) {
var jfxDependencies = jfxModules.collect { module -> "org.openjfx:javafx-$module:$jfxVersion:$platform" }
subprojects {
repositories {
mavenCentral()
}
dependencies {
jfxDependencies.forEach {
compileOnly it
}
}
}
}
}
import com.google.gson.*
task 'generateOpenJFXDependencies' {
doLast {
var jfxDependencies = new JsonArray()
jfxModules.forEach { module ->
JsonObject m = new JsonObject()
m.addProperty("module", "javafx.$module")
m.addProperty("groupId", "org.openjfx")
m.addProperty("artifactId", "javafx-$module")
m.addProperty("version", jfxVersion)
var sha1 = new JsonObject()
jfxArches.forEach { arch ->
if (jfxUnsupported.getOrDefault(arch, []).contains(module)) {
return
}
sha1.addProperty(
arch,
new URL("${jfxRepos.head()}/org/openjfx/javafx-$module/$jfxVersion/javafx-$module-$jfxVersion-${arch}.jar.sha1").getText("UTF-8")
)
}
m.add("sha1", sha1)
jfxDependencies.add(m)
}
jfxDependenciesFile.text = new GsonBuilder().setPrettyPrinting().create().toJson(jfxDependencies)
}
}
// Ensure that the mirror repository caches files
task 'preTouchOpenJFXDependencies' {
doLast {
jfxRepos.tail().forEach { repo ->
jfxModules.forEach { module ->
jfxArches.forEach { arch ->
if (jfxUnsupported.getOrDefault(arch, []).contains(module)) {
return
}
var jarUrl = "$repo/org/openjfx/javafx-$module/$jfxVersion/javafx-$module-$jfxVersion-${arch}.jar"
[jarUrl, jarUrl + ".sha1"].forEach { url ->
try {
new URL(url).getBytes()
System.out.println(url)
} catch (Throwable ignored) {
ignored.printStackTrace()
}
}
}
}
}
}
}
task 'checkTranslations' {
group 'verification'
doLast {
var en = new Properties()
var zh = new Properties()
var zh_CN = new Properties()
file("HMCL/src/main/resources/assets/lang/I18N.properties").withInputStream { en.load(it) }
file("HMCL/src/main/resources/assets/lang/I18N_zh.properties").withInputStream { zh.load(it) }
file("HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties").withInputStream { zh_CN.load(it) }
boolean success = true
zh_CN.forEach { k, v ->
if (!en.containsKey(k)) {
project.logger.log(LogLevel.WARN, "I18N.properties missing key '$k'")
success = false
}
}
zh_CN.forEach { k, v ->
if (!zh.containsKey(k)) {
project.logger.log(LogLevel.WARN, "I18N_zh.properties missing key '$k'")
success = false
}
}
if (!success) {
throw new GradleException("Part of the translation is missing")
}
}
}
+98
View File
@@ -0,0 +1,98 @@
plugins {
id("checkstyle")
}
group = "org.jackhuang"
version = "3.0"
subprojects {
apply {
plugin("java")
plugin("idea")
plugin("maven-publish")
plugin("checkstyle")
}
repositories {
flatDir {
name = "libs"
dirs = setOf(rootProject.file("lib"))
}
mavenCentral()
maven(url = "https://jitpack.io")
}
tasks.withType<JavaCompile> {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
options.encoding = "UTF-8"
}
configure<CheckstyleExtension> {
sourceSets = setOf()
}
tasks.withType<Checkstyle> {
exclude("de/javawi/jstun")
}
dependencies {
"testImplementation"("junit:junit:4.12")
}
configure<PublishingExtension> {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
repositories {
mavenLocal()
}
}
}
tasks.create("checkTranslations") {
doLast {
val hmclLangDir = file("HMCL/src/main/resources/assets/lang")
val en = java.util.Properties().apply {
hmclLangDir.resolve("I18N.properties").bufferedReader().use { load(it) }
}
val zh = java.util.Properties().apply {
hmclLangDir.resolve("I18N_zh.properties").bufferedReader().use { load(it) }
}
val zh_CN = java.util.Properties().apply {
hmclLangDir.resolve("I18N_zh_CN.properties").bufferedReader().use { load(it) }
}
var success = true
zh_CN.forEach {
if (!en.containsKey(it.key)) {
project.logger.warn("I18N.properties missing key '${it.key}'")
success = false
}
}
zh_CN.forEach {
if (!zh.containsKey(it.key)) {
project.logger.warn("I18N_zh.properties missing key '${it.key}'")
success = false
}
}
if (!success) {
throw GradleException("Part of the translation is missing")
}
}
}
apply {
from("javafx.gradle.kts")
}
defaultTasks("clean", "build")
+125
View File
@@ -0,0 +1,125 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.google.code.gson:gson:2.8.1")
}
}
val jfxVersion = "17.0.2"
data class Platform(
val name: String,
val classifier: String,
val groupId: String = "org.openjfx",
val version: String = jfxVersion,
val unsupportedModules: List<String> = listOf()
) {
val modules: List<String> = jfxModules.filter { it !in unsupportedModules }
fun fileUrl(
module: String, classifier: String, ext: String,
repo: String = "https://repo1.maven.org/maven2"
): java.net.URL =
java.net.URL(
"$repo/${groupId.replace('.', '/')}/javafx-$module/$version/javafx-$module-$version-$classifier.$ext"
)
}
val jfxModules = listOf("base", "graphics", "controls", "fxml", "media", "web")
val jfxMirrorRepos = listOf("https://maven.aliyun.com/repository/central")
val jfxDependenciesFile = project("HMCL").buildDir.resolve("openjfx-dependencies.json")
val jfxPlatforms = listOf(
Platform("windows-x86", "win-x86"),
Platform("windows-x86_64", "win"),
Platform("osx-x86_64", "mac"),
Platform("osx-arm64", "mac-aarch64"),
Platform("linux-x86_64", "linux"),
Platform("linux-arm32", "linux-arm32-monocle", unsupportedModules = listOf("media", "web")),
Platform("linux-arm64", "linux-aarch64"),
)
val jfxInClasspath =
try {
Class.forName("javafx.application.Application", false, this.javaClass.classLoader)
true
} catch (ignored: Throwable) {
false
}
if (!jfxInClasspath && JavaVersion.current() >= JavaVersion.VERSION_11) {
val os = System.getProperty("os.name").toLowerCase().let { osName ->
when {
osName.contains("win") -> "windows"
osName.contains("mac") -> "osx"
osName.contains("linux") || osName.contains("unix") -> "linux"
else -> null
}
}
val arch = when (System.getProperty("os.arch").toLowerCase()) {
"x86_64", "x86-64", "amd64", "ia32e", "em64t", "x64" -> "x86_64"
"x86", "x86_32", "x86-32", "i386", "i486", "i586", "i686", "i86pc", "ia32", "x32" -> "x86"
"arm64", "aarch64", "armv8", "armv9" -> "arm64"
else -> null
}
if (os != null && arch != null) {
val platform = jfxPlatforms.find { it.name == "$os-$arch" }
if (platform != null) {
val groupId = platform.groupId
val version = platform.version
val classifier = platform.classifier
rootProject.subprojects {
for (module in jfxModules) {
dependencies.add("compileOnly", "$groupId:javafx-$module:$version:$classifier")
}
}
}
}
}
rootProject.tasks.create("generateOpenJFXDependencies") {
outputs.file(jfxDependenciesFile)
doLast {
val jfxDependencies = jfxPlatforms.associate { platform ->
platform.name to platform.modules.map { module ->
mapOf(
"module" to "javafx.$module",
"groupId" to platform.groupId,
"artifactId" to "javafx-$module",
"version" to platform.version,
"classifier" to platform.classifier,
"sha1" to platform.fileUrl(module, platform.classifier, "jar.sha1").readText()
)
}
}
jfxDependenciesFile.parentFile.mkdirs()
jfxDependenciesFile.writeText(
com.google.gson.GsonBuilder().setPrettyPrinting().create().toJson(jfxDependencies)
)
}
}
// Ensure that the mirror repository caches files
rootProject.tasks.create("preTouchOpenJFXDependencies") {
doLast {
for (repo in jfxMirrorRepos) {
for (platform in jfxPlatforms) {
for (module in platform.modules) {
val url = platform.fileUrl(module, platform.classifier, "jar", repo = repo)
logger.quiet("Getting $url")
try {
url.readBytes()
} catch (e: Throwable) {
logger.warn("An exception occurred while pre touching $url", e)
}
}
}
}
}
}
@@ -1,17 +0,0 @@
version '1.0'
dependencies {
compileOnly project.files("lib/modlauncher-4.1.0.jar")
}
compileJava {
sourceCompatibility = 8
targetCompatibility = 8
}
jar {
manifest {
attributes 'Created-By': 'Copyright(c) 2013-2020 huangyuhui.',
'Implementation-Version': project.version
}
}
@@ -0,0 +1,19 @@
version = "1.0"
dependencies {
compileOnly(project.files("lib/modlauncher-4.1.0.jar"))
}
tasks.compileJava {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
}
tasks.jar {
manifest {
attributes(
"Created-By" to "Copyright(c) 2013-2020 huangyuhui.",
"Implementation-Version" to project.version
)
}
}
@@ -1,52 +0,0 @@
version '1.0'
sourceSets.create("agent") {
java {
srcDir 'src/main/agent'
}
}
dependencies {
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0-beta9'
}
tasks.withType(JavaCompile) {
sourceCompatibility = 8
targetCompatibility = 8
doLast {
FileTree tree = fileTree('build/classes/java')
tree.include '**/*.class'
tree.each {
RandomAccessFile rf = new RandomAccessFile(it, "rw")
rf.seek(7) // major version
rf.write(50) // java 6
rf.close()
}
}
}
task agentJar(type: Jar) {
dependsOn(tasks.compileJava)
baseName = 'log4j-patch-agent'
manifest {
attributes 'Premain-Class': 'org.glavo.log4j.patch.agent.Log4jAgent'
}
from(sourceSets.agent.output)
from(sourceSets.main.output) {
includeEmptyDirs = false
eachFile {
it.path = "org/glavo/log4j/patch/agent/${it.name}.bin"
}
}
}
tasks.jar {
enabled = false
dependsOn agentJar
}
@@ -0,0 +1,53 @@
import java.io.RandomAccessFile
version = "1.0"
sourceSets.create("agent") {
java {
srcDir("src/main/agent")
}
}
dependencies {
compileOnly("org.apache.logging.log4j:log4j-core:2.0-beta9")
}
tasks.withType<JavaCompile> {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
doLast {
val tree = fileTree(destinationDirectory)
tree.include("**/*.class")
tree.exclude("module-info.class")
tree.forEach {
RandomAccessFile(it, "rw").use { rf ->
rf.seek(7) // major version
rf.write(50) // java 6
rf.close()
}
}
}
}
val agentJar = tasks.create<Jar>("agentJar") {
dependsOn(tasks.compileJava)
archiveBaseName.set("log4j-patch-agent")
manifest {
attributes("Premain-Class" to "org.glavo.log4j.patch.agent.Log4jAgent")
}
from(sourceSets["agent"].output)
from(sourceSets["main"].output) {
includeEmptyDirs = false
eachFile { path = "org/glavo/log4j/patch/agent/$name.bin" }
}
}
tasks.jar {
enabled = false
dependsOn(agentJar)
}
-8
View File
@@ -1,8 +0,0 @@
rootProject.name = 'HMCL3'
include ':HMCL'
include ':HMCLCore'
include ':HMCLTransformerDiscoveryService'
include ':log4j-patch'
project(':HMCLTransformerDiscoveryService').projectDir = file('minecraft/libraries/HMCLTransformerDiscoveryService')
project(':log4j-patch').projectDir = file('minecraft/libraries/log4j-patch')
+13
View File
@@ -0,0 +1,13 @@
rootProject.name = "HMCL3"
include(
"HMCL",
"HMCLCore",
"HMCLTransformerDiscoveryService",
"log4j-patch"
)
val minecraftLibraries = listOf("log4j-patch", "HMCLTransformerDiscoveryService")
for (library in minecraftLibraries) {
project(":$library").projectDir = file("minecraft/libraries/$library")
}