Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af3719403d | ||
|
|
e0764d6f0c | ||
|
|
908f47f75e | ||
|
|
a3af719087 | ||
|
|
7f13d6646b | ||
|
|
e8b4c38451 | ||
|
|
38ba6802a0 | ||
|
|
381eead7b6 | ||
|
|
df953e5ea7 | ||
|
|
2fd2954a06 | ||
|
|
a8bb5c1570 | ||
|
|
a33377d1f2 | ||
|
|
e515542add | ||
|
|
48b6bd4ea1 | ||
|
|
9cf23f69a6 | ||
|
|
4104ca082d | ||
|
|
32f8ffad46 | ||
|
|
a5e04a7b81 | ||
|
|
57018bef47 | ||
|
|
0b65431044 | ||
|
|
991729b684 | ||
|
|
1605eb1775 | ||
|
|
94ccee0b76 | ||
|
|
8b94fe5b0b | ||
|
|
abec1abcff | ||
|
|
127938fa38 | ||
|
|
1187ae4f04 | ||
|
|
8efdc536ee | ||
|
|
78f080b6cc | ||
|
|
cc8bcfd924 | ||
|
|
806ca3f64a | ||
|
|
4a16fa13ee | ||
|
|
b88d7c4fd2 | ||
|
|
c0838468b1 | ||
|
|
90cfe3d0b7 | ||
|
|
baf1d22bc4 | ||
|
|
7ca5744559 | ||
|
|
57e728991b | ||
|
|
e08ba4d421 | ||
|
|
ba91c60e95 | ||
|
|
389b93178e | ||
|
|
37aaf153f8 | ||
|
|
2249f4ee0e | ||
|
|
eadfff349a | ||
|
|
cb4cd696a0 | ||
|
|
c85c074eee | ||
|
|
b26c478d4a | ||
|
|
2d83de7c31 | ||
|
|
20d93dd085 | ||
|
|
24fc436870 | ||
|
|
3a6c23aa9d | ||
|
|
0ee8b2dd82 | ||
|
|
7cce90bed8 | ||
|
|
c806a5d412 | ||
|
|
14e8b9212f | ||
|
|
67e96fb84f | ||
|
|
c39c19f215 | ||
|
|
88d7493870 | ||
|
|
404bc88ec8 | ||
|
|
e2355ca2ff | ||
|
|
aab271ac75 | ||
|
|
0da0a1f386 | ||
|
|
59316a866d | ||
|
|
d3087bd763 | ||
|
|
df7b63c491 | ||
|
|
dff893ba4b | ||
|
|
c539702a1b | ||
|
|
6fad149410 |
No files matched your search
@@ -8,18 +8,16 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
提交前请确认:
|
||||
* 该问题确实是 **HMCL 的错误**,而**不是 Minecraft 非正常退出**,如果你的 Minecraft 非正常退出,请前往 [KOOK 频道](https://kook.top/Kx7n3t) 或 [Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
|
||||
* 该问题确实是 **HMCL 的错误**,而**不是 Minecraft 非正常退出**,如果你的 Minecraft 非正常退出,请前往 [QQ 群](https://docs.hmcl.net/groups.html)/[KOOK 频道](https://kook.top/Kx7n3t)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
|
||||
* 你的启动器版本是**最新的快照版本**,可以点击 [此处](https://github.com/burningtnt/HMCL-Snapshot-Update/raw/master/datas/HMCL-dev.jar) 下载最新快照版本。
|
||||
|
||||
如果你的问题并不属于上述两类,你可以选取另一种 Issue 类型,或者直接前往 [KOOK 频道](https://kook.top/Kx7n3t) 或 [Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
|
||||
如果你希望在 QQ 群中反馈问题,请 [赞助 HMCL](https://hmcl.huangyuhui.net/api/redirect/sponsor) 后申请加入群聊,也请将下面的信息表填好直接发在群中,加快我们的沟通速度。
|
||||
如果你的问题并不属于上述两类,你可以选取另一种 Issue 类型,或者直接前往 [QQ 群](https://docs.hmcl.net/groups.html)/[KOOK 频道](https://kook.top/Kx7n3t)/[Discord 频道](https://discord.gg/jVvC7HfM6U) 中获取帮助。
|
||||
|
||||
Before submitting, please confirm:
|
||||
* The issue is indeed **an error of HMCL**, not **Minecraft abnormal exit**. If your Minecraft exits abnormally, please go to the [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
|
||||
* The issue is indeed **an error of HMCL**, not **Minecraft abnormal exit**. If your Minecraft exits abnormally, please go to the [QQ group](https://docs.hmcl.net/groups.html) or [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
|
||||
* Your launcher version is **the latest snapshot version**. You can click [here](https://github.com/burningtnt/HMCL-Snapshot-Update/raw/master/datas/HMCL-dev.jar) to download the latest snapshot version.
|
||||
|
||||
If your issue does not fall into the above two categories, you can choose another type of issue or directly go to the [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
|
||||
If you want to report a problem in the QQ group, please [sponsor HMCL](https://hmcl.huangyuhui.net/api/redirect/sponsor) and apply to join the group chat. Please also fill in the information form below and send it directly in the group to speed up our communication.
|
||||
If your issue does not fall into the above two categories, you can choose another type of issue or directly go to the [QQ group](https://docs.hmcl.net/groups.html) or [KOOK channel](https://kook.top/Kx7n3t) or [Discord channel](https://discord.gg/jVvC7HfM6U) for help.
|
||||
- type: input
|
||||
id: platform
|
||||
attributes:
|
||||
|
||||
@@ -6,9 +6,9 @@ contact_links:
|
||||
- name: Discord 频道 | Discord Channel
|
||||
url: https://discord.gg/jVvC7HfM6U
|
||||
about: Hello Minecraft! Launcher 的官方 Discord 频道。| The official Discord channel of Hello Minecraft! Launcher.
|
||||
- name: 赞助通道 | Sponsorship channel
|
||||
url: https://afdian.net/@huanghongxun
|
||||
about: 前往爱发电赞助 HMCL,赞助后可以申请加入官方 QQ 群反馈问题。| Sponsor Hello Minecraft! Launcher on afdian.net and you can apply to join the official QQ group.
|
||||
- name: QQ 群 | QQ Groups
|
||||
url: https://docs.hmcl.net/groups.html
|
||||
about: Hello Minecraft! Launcher 的官方 QQ 交流群。| The official QQ groups of Hello Minecraft! Laucher.
|
||||
- name: 其他反馈 | Others
|
||||
url: https://github.com/HMCL-dev/HMCL/discussions/new/choose
|
||||
about: 通过 Discussions 反馈其他问题。| Report other problems in Discussions.
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Build HMCLauncher
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'HMCLauncher/**'
|
||||
- '.github/workflows/build-launcher.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'HMCLauncher/**'
|
||||
- '.github/workflows/build-launcher.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
msbuild-architecture: x86
|
||||
- name: Build HMCLauncher
|
||||
run: msbuild /p:Configuration=Release /t:Rebuild /verbosity:detailed .\HMCLauncher\
|
||||
- name: Copy HMCLauncher to assets
|
||||
run: Copy-Item .\HMCLauncher\Release\HMCLauncher.exe -Destination .\HMCL\src\main\resources\assets\HMCLauncher.exe
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
run: .\gradlew makeExecutables --no-daemon
|
||||
env:
|
||||
MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }}
|
||||
MICROSOFT_AUTH_SECRET: ${{ secrets.MICROSOFT_AUTH_SECRET }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
- name: Get short SHA
|
||||
run: echo "SHORT_SHA=$("${{ github.sha }}".SubString(0, 7))" >> $env:GITHUB_ENV
|
||||
- name: Copy HMCLauncher to libs
|
||||
run: Copy-Item .\HMCLauncher\Release\HMCLauncher.exe -Destination .\HMCL\build\libs\HMCLauncher.exe
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: HMCLauncher-${{ env.SHORT_SHA }}
|
||||
path: HMCL/build/libs/*.exe
|
||||
@@ -1,21 +1,25 @@
|
||||
name: Check Style
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.java'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.java'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Check style main
|
||||
run: ./gradlew checkstyleMain
|
||||
run: ./gradlew checkstyleMain --no-daemon
|
||||
- name: Check style test
|
||||
run: ./gradlew checkstyleTest
|
||||
run: ./gradlew checkstyleTest --no-daemon
|
||||
@@ -1,19 +1,23 @@
|
||||
name: Check Translations
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.properties'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.properties'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Check style test
|
||||
run: ./gradlew checkTranslations
|
||||
run: ./gradlew checkTranslations --no-daemon
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
if: ${{ github.repository_owner == 'HMCL-dev' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch tags
|
||||
run: git fetch --all --tags
|
||||
- name: Install tools
|
||||
@@ -39,20 +39,24 @@ jobs:
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe.sha256"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar.sha256"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh.sha256"
|
||||
env:
|
||||
DOWNLOAD_BASE_URL: https://ci.huangyuhui.net/job/HMCL/lastSuccessfulBuild/artifact/HMCL/build/libs
|
||||
- name: Generate release note
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
echo "HMCL v$HMCL_VERSION" >> RELEASE_NOTE
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "The full changelogs can be found on the website: https://docs.hmcl.net/changelog/dev.html" >> RELEASE_NOTE
|
||||
echo "Notice: changelogs are written in Chinese." >> RELEASE_NOTE
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "| File Name | SHA-256 Checksum |" >> RELEASE_NOTE
|
||||
echo "| --- | --- |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.exe | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.jar | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.sh | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
- name: Create release
|
||||
if: ${{ env.continue == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
@@ -60,11 +64,8 @@ jobs:
|
||||
body_path: RELEASE_NOTE
|
||||
files: |
|
||||
HMCL-${{ env.HMCL_VERSION }}.exe
|
||||
HMCL-${{ env.HMCL_VERSION }}.exe.sha1
|
||||
HMCL-${{ env.HMCL_VERSION }}.jar
|
||||
HMCL-${{ env.HMCL_VERSION }}.jar.sha1
|
||||
HMCL-${{ env.HMCL_VERSION }}.sh
|
||||
HMCL-${{ env.HMCL_VERSION }}.sh.sha1
|
||||
target_commitish: ${{ env.HMCL_COMMIT_SHA }}
|
||||
name: ${{ env.HMCL_TAG_NAME }}
|
||||
tag_name: ${{ env.HMCL_TAG_NAME }}
|
||||
@@ -99,21 +100,27 @@ jobs:
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe.sha256"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar.sha256"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh.sha1"
|
||||
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.sh.sha256"
|
||||
env:
|
||||
DOWNLOAD_BASE_URL: https://ci.huangyuhui.net/job/HMCL-stable/lastSuccessfulBuild/artifact/HMCL/build/libs
|
||||
- name: Generate release note
|
||||
if: ${{ env.continue == 'true' }}
|
||||
run: |
|
||||
echo " === **Stable Version** === " >> RELEASE_NOTE
|
||||
echo "HMCL v$HMCL_VERSION" >> RELEASE_NOTE
|
||||
echo "**This version is a stable version.**" >> RELEASE_NOTE
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "The full changelogs can be found on the website: https://docs.hmcl.net/changelog/stable.html" >> RELEASE_NOTE
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "Notice: changelogs are written in Chinese." >> RELEASE_NOTE
|
||||
echo "" >> RELEASE_NOTE
|
||||
echo "| File Name | SHA-256 Checksum |" >> RELEASE_NOTE
|
||||
echo "| --- | --- |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.exe | $(cat HMCL-$HMCL_VERSION.exe.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.jar | $(cat HMCL-$HMCL_VERSION.jar.sha256) |" >> RELEASE_NOTE
|
||||
echo "| HMCL-$HMCL_VERSION.sh | $(cat HMCL-$HMCL_VERSION.sh.sha256) |" >> RELEASE_NOTE
|
||||
- name: Create release
|
||||
if: ${{ env.continue == 'true' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
@@ -121,11 +128,8 @@ jobs:
|
||||
body_path: RELEASE_NOTE
|
||||
files: |
|
||||
HMCL-${{ env.HMCL_VERSION }}.exe
|
||||
HMCL-${{ env.HMCL_VERSION }}.exe.sha1
|
||||
HMCL-${{ env.HMCL_VERSION }}.jar
|
||||
HMCL-${{ env.HMCL_VERSION }}.jar.sha1
|
||||
HMCL-${{ env.HMCL_VERSION }}.sh
|
||||
HMCL-${{ env.HMCL_VERSION }}.sh.sha1
|
||||
target_commitish: ${{ env.HMCL_COMMIT_SHA }}
|
||||
name: ${{ env.HMCL_TAG_NAME }}
|
||||
tag_name: ${{ env.HMCL_TAG_NAME }}
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mirror GitHub to Gitee
|
||||
uses: Yikun/hub-mirror-action@v1.3
|
||||
uses: Yikun/hub-mirror-action@v1.4
|
||||
with:
|
||||
src: github/HMCL-dev
|
||||
dst: gitee/huanghongxun
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
name: Java CI
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'HMCLauncher/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -8,15 +13,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-package: 'jdk+fx'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew build --no-daemon
|
||||
env:
|
||||
MICROSOFT_AUTH_ID: ${{ secrets.MICROSOFT_AUTH_ID }}
|
||||
MICROSOFT_AUTH_SECRET: ${{ secrets.MICROSOFT_AUTH_SECRET }}
|
||||
@@ -24,7 +29,7 @@ jobs:
|
||||
- name: Get short SHA
|
||||
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: HMCL-${{ env.SHORT_SHA }}
|
||||
path: HMCL/build/libs
|
||||
@@ -118,7 +118,7 @@ tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("sha
|
||||
|
||||
manifest {
|
||||
attributes(
|
||||
"Created-By" to "Copyright(c) 2013-2023 huangyuhui.",
|
||||
"Created-By" to "Copyright(c) 2013-2024 huangyuhui.",
|
||||
"Main-Class" to "org.jackhuang.hmcl.Main",
|
||||
"Multi-Release" to "true",
|
||||
"Implementation-Version" to project.version,
|
||||
|
||||
|
After Width: | Height: | Size: 31 KiB |
@@ -27,7 +27,7 @@ S = ';'
|
||||
MOD_SEPARATOR = ','
|
||||
|
||||
CURSEFORGE_PATTERN1 = re.compile(
|
||||
r'^/minecraft/(mc-mods|modpacks|customization|mc-addons|customization/configuration)/+(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
r'^/(minecraft|Minecraft|minecraft-bedrock)/(mc-mods|modpacks|customization|mc-addons|texture-packs|customization/configuration|addons)/+(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
CURSEFORGE_PATTERN2 = re.compile(
|
||||
r'^/projects/(?P<modid>[\w-]+)(/(.*?))?$')
|
||||
CURSEFORGE_PATTERN3 = re.compile(
|
||||
@@ -58,30 +58,6 @@ def parseMcmod(url):
|
||||
return match.group('modid')
|
||||
return ''
|
||||
|
||||
|
||||
MCBBS_HTML_PATTERN = re.compile(r'/+thread-(?P<modid>\d+)-(\d+)-(\d+).html')
|
||||
MCBBS_PHP_PATTERN = re.compile(r'')
|
||||
|
||||
|
||||
def parseMcbbs(url):
|
||||
res = urlparse(url)
|
||||
if res.scheme not in ['http', 'https']:
|
||||
return ''
|
||||
if res.netloc != 'www.mcbbs.net':
|
||||
return ''
|
||||
match = MCBBS_HTML_PATTERN.match(res.path)
|
||||
if match:
|
||||
return match.group('modid')
|
||||
|
||||
query = parse_qs(res.query)
|
||||
|
||||
if res.path == '/forum.php':
|
||||
if 'mod' in query and 'tid' in query and query['mod'] == ['viewthread']:
|
||||
return query['tid']
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
skip = [
|
||||
'Minecraft',
|
||||
'The Building Game'
|
||||
@@ -111,12 +87,11 @@ if __name__ == '__main__':
|
||||
exit(1)
|
||||
|
||||
black_lists = [
|
||||
'Master Chef'
|
||||
'Wood Converter'
|
||||
]
|
||||
|
||||
curseforge_id = ''
|
||||
mcmod_id = ''
|
||||
mcbbs_id = ''
|
||||
links = mod['links']['list']
|
||||
if 'curseforge' in links and links['curseforge'] and sub_name not in black_lists and chinese_name not in black_lists:
|
||||
for link in links['curseforge']:
|
||||
@@ -131,11 +106,6 @@ if __name__ == '__main__':
|
||||
if mcmod_id == '':
|
||||
print('Error mcmod ' + chinese_name)
|
||||
exit(1)
|
||||
if 'mcbbs' in links and links['mcbbs']:
|
||||
mcbbs_id = parseMcbbs(links['mcbbs'][0]['url'])
|
||||
if mcbbs_id == '':
|
||||
print('Error mcbbs ' + chinese_name)
|
||||
exit(1)
|
||||
|
||||
mod_id = []
|
||||
if 'modid' in mod and 'list' in mod['modid']:
|
||||
@@ -148,6 +118,6 @@ if __name__ == '__main__':
|
||||
mod_ids = MOD_SEPARATOR.join([str(id) for id in mod_id])
|
||||
|
||||
outfile.write(
|
||||
f'{curseforge_id}{S}{mcmod_id}{S}{mcbbs_id}{S}{mod_ids}{S}{chinese_name}{S}{sub_name}{S}{abbr}\n')
|
||||
f'{curseforge_id}{S}{mcmod_id}{S}{mod_ids}{S}{chinese_name}{S}{sub_name}{S}{abbr}\n')
|
||||
|
||||
print('Success!')
|
||||
@@ -12,14 +12,10 @@ import javafx.scene.transform.Rotate;
|
||||
import javafx.scene.transform.Scale;
|
||||
import javafx.scene.transform.Translate;
|
||||
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SkinCanvas extends Group {
|
||||
|
||||
public static final Image ALEX = FXUtils.newBuiltinImage("/assets/img/skin/alex.png");
|
||||
public static final Image STEVE = FXUtils.newBuiltinImage("/assets/img/skin/steve.png");
|
||||
|
||||
public static final SkinCube ALEX_LARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, true);
|
||||
public static final SkinCube ALEX_RARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, true);
|
||||
|
||||
@@ -152,10 +148,6 @@ public class SkinCanvas extends Group {
|
||||
capeGroup.setVisible(hasCape);
|
||||
}
|
||||
|
||||
public SkinCanvas(double preW, double preH) {
|
||||
this(STEVE, preW, preH, true);
|
||||
}
|
||||
|
||||
public SkinCanvas(Image skin, double preW, double preH, boolean msaa) {
|
||||
this.skin = skin;
|
||||
this.preW = preW;
|
||||
|
||||
@@ -25,9 +25,6 @@ import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.auth.offline.Skin;
|
||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.setting.SambaException;
|
||||
import org.jackhuang.hmcl.task.AsyncTaskExecutor;
|
||||
@@ -53,13 +50,10 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Launcher extends Application {
|
||||
@@ -71,14 +65,12 @@ public final class Launcher extends Application {
|
||||
|
||||
CookieHandler.setDefault(COOKIE_MANAGER);
|
||||
|
||||
register();
|
||||
|
||||
LOG.info("JavaFX Version: " + System.getProperty("javafx.runtime.version"));
|
||||
try {
|
||||
Object pipeline = Class.forName("com.sun.prism.GraphicsPipeline").getMethod("getPipeline").invoke(null);
|
||||
LOG.info("Prism pipeline: " + (pipeline == null ? "null" : pipeline.getClass().getName()));
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to get prism pipeline", e);
|
||||
LOG.warning("Failed to get prism pipeline", e);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -87,7 +79,7 @@ public final class Launcher extends Application {
|
||||
} catch (SambaException ignored) {
|
||||
Main.showWarningAndContinue(i18n("fatal.samba"));
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to load config", e);
|
||||
LOG.error("Failed to load config", e);
|
||||
checkConfigInTempDir();
|
||||
checkConfigOwner();
|
||||
Main.showErrorAndExit(i18n("fatal.config_loading_failure", ConfigHolder.configLocation().getParent()));
|
||||
@@ -128,45 +120,6 @@ public final class Launcher extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private static void register() {
|
||||
Skin.registerDefaultSkinLoader((type) -> {
|
||||
switch (type) {
|
||||
case ALEX:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/alex.png");
|
||||
case ARI:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/ari.png");
|
||||
case EFE:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/efe.png");
|
||||
case KAI:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/kai.png");
|
||||
case MAKENA:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/makena.png");
|
||||
case NOOR:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/noor.png");
|
||||
case STEVE:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/steve.png");
|
||||
case SUNNY:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/sunny.png");
|
||||
case ZURI:
|
||||
return Skin.class.getResourceAsStream("/assets/img/skin/zuri.png");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMod.registerEmptyRemoteMod(new RemoteMod("", "", i18n("mods.broken_dependency.title"), i18n("mods.broken_dependency.desc"), new ArrayList<>(), "", "/assets/img/icon@8x.png", new RemoteMod.IMod() {
|
||||
@Override
|
||||
public List<RemoteMod> loadDependencies(RemoteModRepository modRepository) throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<RemoteMod.Version> loadVersions(RemoteModRepository modRepository) throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static ButtonType showAlert(AlertType alertType, String contentText, ButtonType... buttons) {
|
||||
return new Alert(alertType, contentText, buttons).showAndWait().orElse(null);
|
||||
}
|
||||
@@ -211,7 +164,7 @@ public final class Launcher extends Application {
|
||||
private static void checkConfigInTempDir() {
|
||||
if (ConfigHolder.isNewlyCreated() && isConfigInTempDir()
|
||||
&& showAlert(AlertType.WARNING, i18n("fatal.config_in_temp_dir"), ButtonType.YES, ButtonType.NO) == ButtonType.NO) {
|
||||
System.exit(0);
|
||||
Main.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +177,7 @@ public final class Launcher extends Application {
|
||||
try {
|
||||
owner = Files.getOwner(ConfigHolder.configLocation()).getName();
|
||||
} catch (IOException ioe) {
|
||||
LOG.log(Level.WARNING, "Failed to get file owner", ioe);
|
||||
LOG.warning("Failed to get file owner", ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,17 +202,18 @@ public final class Launcher extends Application {
|
||||
Clipboard.getSystemClipboard()
|
||||
.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, command));
|
||||
}
|
||||
System.exit(1);
|
||||
Main.exit(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
super.stop();
|
||||
Controllers.onApplicationStop();
|
||||
LOG.shutdown();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (UpdateHandler.processArguments(args)) {
|
||||
LOG.shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -277,6 +231,7 @@ public final class Launcher extends Application {
|
||||
LOG.info("Current Directory: " + System.getProperty("user.dir"));
|
||||
LOG.info("HMCL Directory: " + Metadata.HMCL_DIRECTORY);
|
||||
LOG.info("HMCL Jar Path: " + Lang.requireNonNullElse(JarUtils.thisJarPath(), "Not Found"));
|
||||
LOG.info("HMCL Log File: " + Lang.requireNonNullElse(LOG.getLogFile(), "In Memory"));
|
||||
LOG.info("Memory: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
|
||||
LOG.info("Physical memory: " + OperatingSystem.TOTAL_MEMORY + " MB");
|
||||
LOG.info("Metaspace: " + ManagementFactory.getMemoryPoolMXBeans().stream()
|
||||
@@ -284,8 +239,10 @@ public final class Launcher extends Application {
|
||||
.findAny()
|
||||
.map(bean -> bean.getUsage().getUsed() / 1024 / 1024 + "MB")
|
||||
.orElse("Unknown"));
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD())
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
LOG.info("XDG Session Type: " + System.getenv("XDG_SESSION_TYPE"));
|
||||
LOG.info("XDG Current Desktop: " + System.getenv("XDG_CURRENT_DESKTOP"));
|
||||
}
|
||||
|
||||
launch(Launcher.class, args);
|
||||
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
|
||||
|
||||
@@ -21,7 +21,6 @@ import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import org.jackhuang.hmcl.ui.AwtUtils;
|
||||
import org.jackhuang.hmcl.util.FractureiserDetector;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.SelfDependencyPatcher;
|
||||
import org.jackhuang.hmcl.ui.SwingUtils;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
@@ -43,10 +42,9 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Main {
|
||||
@@ -55,10 +53,12 @@ public final class Main {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.setProperty("java.net.useSystemProxies", "true");
|
||||
System.setProperty("javafx.autoproxy.disable", "true");
|
||||
System.getProperties().putIfAbsent("java.net.useSystemProxies", "true");
|
||||
System.getProperties().putIfAbsent("javafx.autoproxy.disable", "true");
|
||||
System.getProperties().putIfAbsent("http.agent", "HMCL/" + Metadata.VERSION);
|
||||
|
||||
LOG.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||
|
||||
checkDirectoryPath();
|
||||
|
||||
if (JavaVersion.CURRENT_JAVA.getParsedVersion() < 9)
|
||||
@@ -68,8 +68,6 @@ public final class Main {
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX)
|
||||
initIcon();
|
||||
|
||||
Logging.start(Metadata.HMCL_DIRECTORY.resolve("logs"));
|
||||
|
||||
checkJavaFX();
|
||||
verifyJavaFX();
|
||||
detectFractureiser();
|
||||
@@ -77,6 +75,11 @@ public final class Main {
|
||||
Launcher.main(args);
|
||||
}
|
||||
|
||||
public static void exit(int exitCode) {
|
||||
LOG.shutdown();
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
private static void initIcon() {
|
||||
java.awt.Image image = java.awt.Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/assets/img/icon@8x.png"));
|
||||
AwtUtils.setAppleIcon(image);
|
||||
@@ -93,7 +96,7 @@ public final class Main {
|
||||
|
||||
private static void detectFractureiser() {
|
||||
if (FractureiserDetector.detect()) {
|
||||
LOG.log(Level.SEVERE, "Detected that this computer is infected by fractureiser");
|
||||
LOG.error("Detected that this computer is infected by fractureiser");
|
||||
showErrorAndExit(i18n("fatal.fractureiser"));
|
||||
}
|
||||
}
|
||||
@@ -102,14 +105,14 @@ public final class Main {
|
||||
try {
|
||||
SelfDependencyPatcher.patch();
|
||||
} catch (SelfDependencyPatcher.PatchException e) {
|
||||
LOG.log(Level.SEVERE, "unable to patch JVM", e);
|
||||
LOG.error("unable to patch JVM", e);
|
||||
showErrorAndExit(i18n("fatal.javafx.missing"));
|
||||
} catch (SelfDependencyPatcher.IncompatibleVersionException e) {
|
||||
LOG.log(Level.SEVERE, "unable to patch JVM", e);
|
||||
LOG.error("unable to patch JVM", e);
|
||||
showErrorAndExit(i18n("fatal.javafx.incompatible"));
|
||||
} catch (CancellationException e) {
|
||||
LOG.log(Level.SEVERE, "User cancels downloading JavaFX", e);
|
||||
System.exit(0);
|
||||
LOG.error("User cancels downloading JavaFX", e);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,13 +139,13 @@ public final class Main {
|
||||
try {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
new Alert(Alert.AlertType.ERROR, message).showAndWait();
|
||||
System.exit(1);
|
||||
exit(1);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
SwingUtils.showErrorDialog(message);
|
||||
System.exit(1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +195,7 @@ public final class Main {
|
||||
LOG.info("Added Lets Encrypt root certificates as additional trust");
|
||||
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException |
|
||||
KeyManagementException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to load lets encrypt certificate. Expect problems", e);
|
||||
LOG.error("Failed to load lets encrypt certificate. Expect problems", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.jackhuang.hmcl.countly;
|
||||
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
@@ -13,6 +12,7 @@ import java.util.Map;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class CrashReport {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class CrashReport {
|
||||
pair("_ram_current", getMemoryAvailable()),
|
||||
pair("_ram_total", Runtime.getRuntime().maxMemory() / BYTES_IN_MB),
|
||||
pair("_error", stackTrace),
|
||||
pair("_logs", Logging.getLogs()),
|
||||
pair("_logs", LOG.getLogs()),
|
||||
pair("_name", throwable.getLocalizedMessage()),
|
||||
pair("_nonfatal", nonFatal)
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.launch.DefaultLauncher;
|
||||
import org.jackhuang.hmcl.launch.ProcessListener;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.ManagedProcess;
|
||||
@@ -29,9 +28,9 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
@@ -72,7 +71,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
if (I18n.getCurrentLocale().getLocale() != Locale.CHINA) {
|
||||
if (!I18n.isUseChinese()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,7 +96,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
|
||||
try {
|
||||
FileUtils.writeText(optionsFile, String.format("lang:%s\n", lang));
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to generate options.txt", e);
|
||||
LOG.warning("Unable to generate options.txt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,12 +50,11 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
public class HMCLGameRepository extends DefaultGameRepository {
|
||||
@@ -124,7 +123,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
if (!file.exists() && !versions.isEmpty())
|
||||
FileUtils.writeText(file, PROFILE);
|
||||
} catch (IOException ex) {
|
||||
LOG.log(Level.WARNING, "Unable to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex);
|
||||
LOG.warning("Unable to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex);
|
||||
}
|
||||
|
||||
// https://github.com/HMCL-dev/HMCL/issues/938
|
||||
@@ -259,8 +258,50 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
return vs;
|
||||
}
|
||||
|
||||
public File getVersionIconFile(String id) {
|
||||
return new File(getVersionRoot(id), "icon.png");
|
||||
public Optional<File> getVersionIconFile(String id) {
|
||||
File root = getVersionRoot(id);
|
||||
|
||||
File iconFile = new File(root, "icon.png");
|
||||
if (iconFile.exists()) {
|
||||
return Optional.of(iconFile);
|
||||
}
|
||||
|
||||
iconFile = new File(root, "icon.jpg");
|
||||
if (iconFile.exists()) {
|
||||
return Optional.of(iconFile);
|
||||
}
|
||||
|
||||
iconFile = new File(root, "icon.bmp");
|
||||
if (iconFile.exists()) {
|
||||
return Optional.of(iconFile);
|
||||
}
|
||||
|
||||
iconFile = new File(root, "icon.gif");
|
||||
if (iconFile.exists()) {
|
||||
return Optional.of(iconFile);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void setVersionIconFile(String id, File iconFile) throws IOException {
|
||||
String ext = FileUtils.getExtension(iconFile).toLowerCase(Locale.ROOT);
|
||||
if (!ext.equals("png") && !ext.equals("jpg") && !ext.equals("bmp") && !ext.equals("gif")) {
|
||||
throw new IllegalArgumentException("Unsupported icon file: " + ext);
|
||||
}
|
||||
|
||||
deleteIconFile(id);
|
||||
|
||||
FileUtils.copyFile(iconFile, new File(getVersionRoot(id), "icon." + ext));
|
||||
}
|
||||
|
||||
public void deleteIconFile(String id) {
|
||||
File root = getVersionRoot(id);
|
||||
|
||||
new File(root, "icon.png").delete();
|
||||
new File(root, "icon.jpg").delete();
|
||||
new File(root, "icon.bmp").delete();
|
||||
new File(root, "icon.gif").delete();
|
||||
}
|
||||
|
||||
public Image getVersionIconImage(String id) {
|
||||
@@ -272,12 +313,12 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
|
||||
if (iconType == VersionIconType.DEFAULT) {
|
||||
Version version = getVersion(id).resolve(this);
|
||||
File iconFile = getVersionIconFile(id);
|
||||
if (iconFile.exists()) {
|
||||
try (InputStream inputStream = new FileInputStream(iconFile)) {
|
||||
Optional<File> iconFile = getVersionIconFile(id);
|
||||
if (iconFile.isPresent()) {
|
||||
try (InputStream inputStream = new FileInputStream(iconFile.get())) {
|
||||
return new Image(inputStream);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to load version icon of " + id, e);
|
||||
LOG.warning("Failed to load version icon of " + id, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +357,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
|
||||
FileUtils.writeText(file, GSON.toJson(localVersionSettings.get(id)));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Unable to save version setting of " + id, e);
|
||||
LOG.error("Unable to save version setting of " + id, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,12 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.Lang.resolveException;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -112,7 +111,7 @@ public final class LauncherHelper {
|
||||
public void launch() {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
Logging.LOG.info("Launching game version: " + selectedVersion);
|
||||
LOG.info("Launching game version: " + selectedVersion);
|
||||
|
||||
Controllers.dialog(launchingStepsPane);
|
||||
launch0();
|
||||
@@ -333,7 +332,8 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
private static Task<JavaVersion> checkGameState(Profile profile, VersionSetting setting, Version version) {
|
||||
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(profile.getRepository().getGameVersion(version));
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, profile.getRepository().getGameVersion(version).orElse(null));
|
||||
GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(analyzer.getVersion(LibraryAnalyzer.LibraryType.MINECRAFT));
|
||||
|
||||
if (setting.isNotCheckJVM()) {
|
||||
return Task.composeAsync(() -> setting.getJavaVersion(gameVersion, version))
|
||||
@@ -354,7 +354,9 @@ public final class LauncherHelper {
|
||||
|
||||
if (org.jackhuang.hmcl.util.platform.Platform.isCompatibleWithX86Java()) {
|
||||
JavaVersionConstraint.VersionRanges range = JavaVersionConstraint.findSuitableJavaVersionRange(gameVersion, version);
|
||||
if (range.getMandatory().contains(VersionNumber.asVersion("17.0.1"))) {
|
||||
if (range.getMandatory().contains(VersionNumber.asVersion("21.0.3"))) {
|
||||
targetJavaVersion = GameJavaVersion.JAVA_21;
|
||||
} else 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;
|
||||
@@ -388,7 +390,7 @@ public final class LauncherHelper {
|
||||
future.complete(downloadedJavaVersion);
|
||||
})
|
||||
.exceptionally(throwable -> {
|
||||
LOG.log(Level.WARNING, "Failed to download java", throwable);
|
||||
LOG.warning("Failed to download java", throwable);
|
||||
Controllers.confirm(i18n("launch.failed.no_accepted_java"), i18n("message.warning"), MessageType.WARNING, continueAction, () -> {
|
||||
future.completeExceptionally(new CancellationException("No accepted java"));
|
||||
});
|
||||
@@ -417,7 +419,6 @@ public final class LauncherHelper {
|
||||
JavaVersionConstraint violatedMandatoryConstraint = null;
|
||||
List<JavaVersionConstraint> violatedSuggestedConstraints = null;
|
||||
|
||||
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, gameVersion.toString());
|
||||
for (JavaVersionConstraint constraint : JavaVersionConstraint.ALL) {
|
||||
if (constraint.appliesToVersion(gameVersion, version, javaVersion, analyzer)) {
|
||||
if (!constraint.checkJava(gameVersion, version, javaVersion)) {
|
||||
@@ -452,7 +453,7 @@ public final class LauncherHelper {
|
||||
future.complete(downloadedJavaVersion);
|
||||
}, Schedulers.javafx())
|
||||
.whenCompleteAsync((result, throwable) -> {
|
||||
LOG.log(Level.WARNING, "Failed to download java", throwable);
|
||||
LOG.warning("Failed to download java", throwable);
|
||||
breakAction.run();
|
||||
}, Schedulers.javafx());
|
||||
return Task.fromCompletableFuture(future);
|
||||
@@ -466,6 +467,11 @@ public final class LauncherHelper {
|
||||
() -> FXUtils.openLink(OPENJDK_DOWNLOAD_LINK), null);
|
||||
breakAction.run();
|
||||
return Task.fromCompletableFuture(future);
|
||||
case VANILLA_JAVA_21:
|
||||
Controllers.confirm(i18n("launch.advice.require_newer_java_version", gameVersion.toString(), 21), i18n("message.warning"),
|
||||
() -> FXUtils.openLink(OPENJDK_DOWNLOAD_LINK), null);
|
||||
breakAction.run();
|
||||
return Task.fromCompletableFuture(future);
|
||||
case VANILLA_JAVA_8:
|
||||
Controllers.dialog(i18n("launch.advice.java8_1_13"), i18n("message.error"), MessageType.ERROR, breakAction);
|
||||
return Task.fromCompletableFuture(future);
|
||||
@@ -605,7 +611,7 @@ public final class LauncherHelper {
|
||||
.thenAcceptAsync(future::complete)
|
||||
.exceptionally(throwable -> {
|
||||
Throwable resolvedException = resolveException(throwable);
|
||||
LOG.log(Level.WARNING, "Failed to download java", throwable);
|
||||
LOG.warning("Failed to download java", throwable);
|
||||
if (!(resolvedException instanceof CancellationException)) {
|
||||
Controllers.dialog(DownloadProviders.localizeErrorMessage(resolvedException), i18n("install.failed"));
|
||||
}
|
||||
@@ -644,11 +650,11 @@ public final class LauncherHelper {
|
||||
try {
|
||||
return Task.completed(account.logIn());
|
||||
} catch (CredentialExpiredException e) {
|
||||
LOG.log(Level.INFO, "Credential has expired", e);
|
||||
LOG.info("Credential has expired", e);
|
||||
|
||||
return Task.completed(DialogController.logIn(account));
|
||||
} catch (AuthenticationException e) {
|
||||
LOG.log(Level.WARNING, "Authentication failed, try skipping refresh", e);
|
||||
LOG.warning("Authentication failed, try skipping refresh", e);
|
||||
|
||||
CompletableFuture<Task<AuthInfo>> future = new CompletableFuture<>();
|
||||
runInFX(() -> {
|
||||
@@ -835,7 +841,7 @@ public final class LauncherHelper {
|
||||
@Override
|
||||
public void onExit(int exitCode, ExitType exitType) {
|
||||
if (showLogs) {
|
||||
Platform.runLater(() -> logWindow.logLine(String.format("[HMCL ProcessListener] Minecraft exit with code %d(0x%x).", exitCode, exitCode), Log4jLevel.INFO));
|
||||
Platform.runLater(() -> logWindow.logLine(String.format("[HMCL ProcessListener] Minecraft exit with code %d(0x%x), type is %s.", exitCode, exitCode, exitType), Log4jLevel.INFO));
|
||||
}
|
||||
|
||||
launchingLatch.countDown();
|
||||
|
||||
@@ -27,10 +27,9 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public abstract class LocalizedRemoteModRepository implements RemoteModRepository {
|
||||
private static final int CONTAIN_CHINESE_WEIGHT = 10;
|
||||
@@ -69,7 +68,7 @@ public abstract class LocalizedRemoteModRepository implements RemoteModRepositor
|
||||
SearchResult searchResult = getBackedRemoteModRepository().search(gameVersion, category, pageOffset, pageSize, String.join(" ", englishSearchFiltersSet), getBackedRemoteModRepositorySortOrder(), sortOrder);
|
||||
for (Iterator<RemoteMod> iterator = searchResult.getUnsortedResults().iterator(); iterator.hasNext(); ) {
|
||||
if (chineseIndex > englishIndex) {
|
||||
LOG.log(Level.WARNING, "Too many search results! Are the backed remote mod repository broken? Or are the API broken?");
|
||||
LOG.warning("Too many search results! Are the backed remote mod repository broken? Or are the API broken?");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.logging.Logger;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.Zipper;
|
||||
@@ -35,9 +35,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class LogExporter {
|
||||
private LogExporter() {
|
||||
@@ -70,9 +69,9 @@ public final class LogExporter {
|
||||
processLogs(runDirectory, "*.log", "runDirectory", zipper);
|
||||
processLogs(runDirectory.resolve("crash-reports"), "*.txt", "crash-reports", zipper);
|
||||
|
||||
zipper.putTextFile(Logging.getLogs(), "hmcl.log");
|
||||
zipper.putTextFile(LOG.getLogs(), "hmcl.log");
|
||||
zipper.putTextFile(logs, "minecraft.log");
|
||||
zipper.putTextFile(Logging.filterForbiddenToken(launchScript), OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "launch.bat" : "launch.sh");
|
||||
zipper.putTextFile(Logger.filterForbiddenToken(launchScript), OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "launch.bat" : "launch.sh");
|
||||
|
||||
for (String id : versions) {
|
||||
Path versionJson = baseDirectory.resolve("versions").resolve(id).resolve(id + ".json");
|
||||
@@ -94,13 +93,17 @@ public final class LogExporter {
|
||||
if (Files.isRegularFile(file)) {
|
||||
FileTime time = Files.readAttributes(file, BasicFileAttributes.class).lastModifiedTime();
|
||||
if (time.toMillis() >= processStartTime) {
|
||||
String crashLog = Logging.filterForbiddenToken(FileUtils.readText(file, OperatingSystem.NATIVE_CHARSET));
|
||||
zipper.putTextFile(crashLog, file.getFileName().toString());
|
||||
try {
|
||||
String crashLog = Logger.filterForbiddenToken(FileUtils.readText(file, OperatingSystem.NATIVE_CHARSET));
|
||||
zipper.putTextFile(crashLog, file.getFileName().toString());
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to read log file: " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to find any log on " + logDirectory, e);
|
||||
LOG.warning("Failed to find any log on " + logDirectory, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.auth.OAuth;
|
||||
import org.jackhuang.hmcl.event.Event;
|
||||
import org.jackhuang.hmcl.event.EventManager;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
@@ -34,10 +33,10 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
|
||||
@@ -80,7 +79,7 @@ public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
|
||||
try {
|
||||
session.parseBody(files);
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Failed to read post data", e);
|
||||
LOG.warning("Failed to read post data", e);
|
||||
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_HTML, "");
|
||||
} catch (ResponseException re) {
|
||||
return newFixedLengthResponse(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
|
||||
@@ -97,7 +96,7 @@ public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
|
||||
idToken = query.get("id_token");
|
||||
future.complete(query.get("code"));
|
||||
} else {
|
||||
Logging.LOG.warning("Error: " + parameters);
|
||||
LOG.warning("Error: " + parameters);
|
||||
future.completeExceptionally(new AuthenticationException("failed to authenticate"));
|
||||
}
|
||||
|
||||
@@ -106,7 +105,7 @@ public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
|
||||
html = IOUtils.readFullyAsString(OAuthServer.class.getResourceAsStream("/assets/microsoft_auth.html"))
|
||||
.replace("%close-page%", i18n("account.methods.microsoft.close_page"));
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to load html");
|
||||
LOG.error("Failed to load html", e);
|
||||
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_HTML, "");
|
||||
}
|
||||
thread(() -> {
|
||||
@@ -114,7 +113,7 @@ public final class OAuthServer extends NanoHTTPD implements OAuth.Session {
|
||||
Thread.sleep(1000);
|
||||
stop();
|
||||
} catch (InterruptedException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to sleep for 1 second");
|
||||
LOG.error("Failed to sleep for 1 second");
|
||||
}
|
||||
});
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/html; charset=UTF-8", html);
|
||||
|
||||
@@ -52,13 +52,12 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* @author yushijinhun
|
||||
@@ -116,7 +115,7 @@ public final class TexturesLoader {
|
||||
} catch (Exception e) {
|
||||
if (Files.isRegularFile(file)) {
|
||||
// concurrency conflict?
|
||||
LOG.log(Level.WARNING, "Failed to download texture " + texture.getUrl() + ", but the file is available", e);
|
||||
LOG.warning("Failed to download texture " + texture.getUrl() + ", but the file is available", e);
|
||||
} else {
|
||||
throw new IOException("Failed to download texture " + texture.getUrl());
|
||||
}
|
||||
@@ -140,30 +139,42 @@ public final class TexturesLoader {
|
||||
// ====
|
||||
|
||||
// ==== Skins ====
|
||||
private final static Map<TextureModel, LoadedTexture> DEFAULT_SKINS = new EnumMap<>(TextureModel.class);
|
||||
private static final String[] DEFAULT_SKINS = {"alex", "ari", "efe", "kai", "makena", "noor", "steve", "sunny", "zuri"};
|
||||
|
||||
static {
|
||||
loadDefaultSkin("/assets/img/skin/steve.png", TextureModel.STEVE);
|
||||
loadDefaultSkin("/assets/img/skin/alex.png", TextureModel.ALEX);
|
||||
public static Image getDefaultSkinImage() {
|
||||
return FXUtils.newBuiltinImage("/assets/img/skin/wide/steve.png");
|
||||
}
|
||||
|
||||
private static void loadDefaultSkin(String path, TextureModel model) {
|
||||
DEFAULT_SKINS.put(model, new LoadedTexture(FXUtils.newBuiltinImage(path), singletonMap("model", model.modelName)));
|
||||
public static LoadedTexture getDefaultSkin(UUID uuid) {
|
||||
int idx = Math.floorMod(uuid.hashCode(), DEFAULT_SKINS.length * 2);
|
||||
TextureModel model;
|
||||
Image skin;
|
||||
if (idx < DEFAULT_SKINS.length) {
|
||||
model = TextureModel.SLIM;
|
||||
skin = FXUtils.newBuiltinImage("/assets/img/skin/slim/" + DEFAULT_SKINS[idx] + ".png");
|
||||
} else {
|
||||
model = TextureModel.WIDE;
|
||||
skin = FXUtils.newBuiltinImage("/assets/img/skin/wide/" + DEFAULT_SKINS[idx - DEFAULT_SKINS.length] + ".png");
|
||||
}
|
||||
|
||||
return new LoadedTexture(skin, singletonMap("model", model.modelName));
|
||||
}
|
||||
|
||||
public static LoadedTexture getDefaultSkin(TextureModel model) {
|
||||
return DEFAULT_SKINS.get(model);
|
||||
public static TextureModel getDefaultModel(UUID uuid) {
|
||||
return TextureModel.WIDE.modelName.equals(getDefaultSkin(uuid).getMetadata().get("model"))
|
||||
? TextureModel.WIDE
|
||||
: TextureModel.SLIM;
|
||||
}
|
||||
|
||||
public static ObjectBinding<LoadedTexture> skinBinding(YggdrasilService service, UUID uuid) {
|
||||
LoadedTexture uuidFallback = getDefaultSkin(TextureModel.detectUUID(uuid));
|
||||
LoadedTexture uuidFallback = getDefaultSkin(uuid);
|
||||
return BindingMapping.of(service.getProfileRepository().binding(uuid))
|
||||
.map(profile -> profile
|
||||
.flatMap(it -> {
|
||||
try {
|
||||
return YggdrasilService.getTextures(it);
|
||||
} catch (ServerResponseMalformedException e) {
|
||||
LOG.log(Level.WARNING, "Failed to parse texture payload", e);
|
||||
LOG.warning("Failed to parse texture payload", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
})
|
||||
@@ -176,7 +187,7 @@ public final class TexturesLoader {
|
||||
try {
|
||||
return loadTexture(texture);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
LOG.warning("Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
return uuidFallback;
|
||||
}
|
||||
}, POOL);
|
||||
@@ -187,7 +198,7 @@ public final class TexturesLoader {
|
||||
}
|
||||
|
||||
public static ObservableValue<LoadedTexture> skinBinding(Account account) {
|
||||
LoadedTexture uuidFallback = getDefaultSkin(TextureModel.detectUUID(account.getUUID()));
|
||||
LoadedTexture uuidFallback = getDefaultSkin(account.getUUID());
|
||||
if (account instanceof OfflineAccount) {
|
||||
OfflineAccount offlineAccount = (OfflineAccount) account;
|
||||
|
||||
@@ -200,7 +211,7 @@ public final class TexturesLoader {
|
||||
if (skin != null) {
|
||||
skin.load(username).setExecutor(POOL).whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (exception != null) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture", exception);
|
||||
LOG.warning("Failed to load texture", exception);
|
||||
} else if (result != null && result.getSkin() != null && result.getSkin().getImage() != null) {
|
||||
Map<String, String> metadata;
|
||||
if (result.getModel() != null) {
|
||||
@@ -231,7 +242,7 @@ public final class TexturesLoader {
|
||||
try {
|
||||
return loadTexture(texture);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
LOG.warning("Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
|
||||
return uuidFallback;
|
||||
}
|
||||
}, POOL);
|
||||
@@ -348,7 +359,7 @@ public final class TexturesLoader {
|
||||
fxAvatarBinding(canvas, skinBinding(account));
|
||||
else {
|
||||
unbindAvatar(canvas);
|
||||
drawAvatar(canvas, getDefaultSkin(TextureModel.detectUUID(account.getUUID())).image);
|
||||
drawAvatar(canvas, getDefaultSkin(account.getUUID()).image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ import org.jackhuang.hmcl.auth.microsoft.MicrosoftService;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.game.OAuthServer;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||
@@ -49,7 +47,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static javafx.collections.FXCollections.observableArrayList;
|
||||
@@ -57,7 +54,7 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
|
||||
import static org.jackhuang.hmcl.util.Lang.immutableListOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -74,7 +71,7 @@ public final class Accounts {
|
||||
try {
|
||||
((AuthlibInjectorDownloader) AUTHLIB_INJECTOR_DOWNLOADER).checkUpdate();
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to check update for authlib-injector", e);
|
||||
LOG.warning("Failed to check update for authlib-injector", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -83,17 +80,15 @@ public final class Accounts {
|
||||
public static final OAuthServer.Factory OAUTH_CALLBACK = new OAuthServer.Factory();
|
||||
|
||||
public static final OfflineAccountFactory FACTORY_OFFLINE = new OfflineAccountFactory(AUTHLIB_INJECTOR_DOWNLOADER);
|
||||
public static final YggdrasilAccountFactory FACTORY_MOJANG = YggdrasilAccountFactory.MOJANG;
|
||||
public static final AuthlibInjectorAccountFactory FACTORY_AUTHLIB_INJECTOR = new AuthlibInjectorAccountFactory(AUTHLIB_INJECTOR_DOWNLOADER, Accounts::getOrCreateAuthlibInjectorServer);
|
||||
public static final MicrosoftAccountFactory FACTORY_MICROSOFT = new MicrosoftAccountFactory(new MicrosoftService(OAUTH_CALLBACK));
|
||||
public static final List<AccountFactory<?>> FACTORIES = immutableListOf(FACTORY_OFFLINE, FACTORY_MOJANG, FACTORY_MICROSOFT, FACTORY_AUTHLIB_INJECTOR);
|
||||
public static final List<AccountFactory<?>> FACTORIES = immutableListOf(FACTORY_OFFLINE, FACTORY_MICROSOFT, FACTORY_AUTHLIB_INJECTOR);
|
||||
|
||||
// ==== login type / account factory mapping ====
|
||||
private static final Map<String, AccountFactory<?>> type2factory = new HashMap<>();
|
||||
private static final Map<AccountFactory<?>, String> factory2type = new HashMap<>();
|
||||
static {
|
||||
type2factory.put("offline", FACTORY_OFFLINE);
|
||||
type2factory.put("yggdrasil", FACTORY_MOJANG);
|
||||
type2factory.put("authlibInjector", FACTORY_AUTHLIB_INJECTOR);
|
||||
type2factory.put("microsoft", FACTORY_MICROSOFT);
|
||||
|
||||
@@ -126,8 +121,6 @@ public final class Accounts {
|
||||
return FACTORY_OFFLINE;
|
||||
else if (account instanceof AuthlibInjectorAccount)
|
||||
return FACTORY_AUTHLIB_INJECTOR;
|
||||
else if (account instanceof YggdrasilAccount)
|
||||
return FACTORY_MOJANG;
|
||||
else if (account instanceof MicrosoftAccount)
|
||||
return FACTORY_MICROSOFT;
|
||||
else
|
||||
@@ -184,7 +177,7 @@ public final class Accounts {
|
||||
Config.CONFIG_GSON.fromJson(reader, new TypeToken<List<Map<Object, Object>>>() {
|
||||
}.getType()));
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to load global accounts", e);
|
||||
LOG.warning("Failed to load global accounts", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +189,7 @@ public final class Accounts {
|
||||
FileUtils.saveSafely(globalAccountsFile, json);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to save global accounts", e);
|
||||
LOG.error("Failed to save global accounts", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -215,7 +208,7 @@ public final class Accounts {
|
||||
try {
|
||||
return factory.fromStorage(storage);
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Failed to load account: " + storage, e);
|
||||
LOG.warning("Failed to load account: " + storage, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -327,7 +320,7 @@ public final class Accounts {
|
||||
try {
|
||||
finalSelected.logIn();
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to log " + finalSelected + " in", e);
|
||||
LOG.warning("Failed to log " + finalSelected + " in", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -341,7 +334,7 @@ public final class Accounts {
|
||||
try {
|
||||
server.fetchMetadataResponse();
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to fetch authlib-injector server metdata: " + server, e);
|
||||
LOG.warning("Failed to fetch authlib-injector server metdata: " + server, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -414,7 +407,6 @@ public final class Accounts {
|
||||
// ==== Login type name i18n ===
|
||||
private static final Map<AccountFactory<?>, String> unlocalizedLoginTypeNames = mapOf(
|
||||
pair(Accounts.FACTORY_OFFLINE, "account.methods.offline"),
|
||||
pair(Accounts.FACTORY_MOJANG, "account.methods.yggdrasil"),
|
||||
pair(Accounts.FACTORY_AUTHLIB_INJECTOR, "account.methods.authlib_injector"),
|
||||
pair(Accounts.FACTORY_MICROSOFT, "account.methods.microsoft"));
|
||||
|
||||
|
||||
@@ -34,10 +34,9 @@ import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class AuthlibInjectorServers implements Validation {
|
||||
|
||||
@@ -77,7 +76,7 @@ public final class AuthlibInjectorServers implements Validation {
|
||||
String content = FileUtils.readText(configLocation);
|
||||
configInstance = JsonUtils.GSON.fromJson(content, AuthlibInjectorServers.class);
|
||||
} catch (IOException | JsonParseException e) {
|
||||
LOG.log(Level.WARNING, "Malformed authlib-injectors.json", e);
|
||||
LOG.warning("Malformed authlib-injectors.json", e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,16 @@ import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.JarUtils;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class ConfigHolder {
|
||||
|
||||
@@ -79,7 +80,7 @@ public final class ConfigHolder {
|
||||
|
||||
configLocation = locateConfig();
|
||||
|
||||
LOG.log(Level.INFO, "Config location: " + configLocation);
|
||||
LOG.info("Config location: " + configLocation);
|
||||
|
||||
configInstance = loadConfig();
|
||||
configInstance.addListener(source -> markConfigDirty());
|
||||
@@ -87,6 +88,9 @@ public final class ConfigHolder {
|
||||
globalConfigInstance = loadGlobalConfig();
|
||||
globalConfigInstance.addListener(source -> markGlobalConfigDirty());
|
||||
|
||||
Locale.setDefault(config().getLocalization().getLocale());
|
||||
I18n.setLocale(configInstance.getLocalization());
|
||||
LOG.setLogRetention(globalConfig().getLogRetention());
|
||||
Settings.init();
|
||||
|
||||
if (newlyCreated) {
|
||||
@@ -97,7 +101,7 @@ public final class ConfigHolder {
|
||||
try {
|
||||
Files.setAttribute(configLocation, "dos:hidden", true);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to set hidden attribute of " + configLocation, e);
|
||||
LOG.warning("Failed to set hidden attribute of " + configLocation, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +161,7 @@ public final class ConfigHolder {
|
||||
ownerChanged = true;
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
LOG.log(Level.WARNING, "Failed to get owner");
|
||||
LOG.warning("Failed to get owner");
|
||||
}
|
||||
try {
|
||||
String content = FileUtils.readText(configLocation);
|
||||
@@ -169,7 +173,7 @@ public final class ConfigHolder {
|
||||
return deserialized;
|
||||
}
|
||||
} catch (JsonParseException e) {
|
||||
LOG.log(Level.WARNING, "Malformed config.", e);
|
||||
LOG.warning("Malformed config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +186,7 @@ public final class ConfigHolder {
|
||||
try {
|
||||
writeToConfig(content);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to save config", e);
|
||||
LOG.error("Failed to save config", e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -214,7 +218,7 @@ public final class ConfigHolder {
|
||||
return deserialized;
|
||||
}
|
||||
} catch (JsonParseException e) {
|
||||
LOG.log(Level.WARNING, "Malformed config.", e);
|
||||
LOG.warning("Malformed config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +230,7 @@ public final class ConfigHolder {
|
||||
try {
|
||||
writeToGlobalConfig(content);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Failed to save config", e);
|
||||
LOG.error("Failed to save config", e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -23,10 +23,9 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
final class ConfigUpgrader {
|
||||
private ConfigUpgrader() {
|
||||
@@ -48,12 +47,12 @@ final class ConfigUpgrader {
|
||||
}
|
||||
|
||||
if (configVersion > CURRENT_VERSION) {
|
||||
LOG.log(Level.WARNING, String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", CURRENT_VERSION, configVersion));
|
||||
LOG.warning(String.format("Current HMCL only support the configuration version up to %d. However, the version now is %d.", CURRENT_VERSION, configVersion));
|
||||
deserialized.setConfigVersion(CURRENT_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.log(Level.INFO, String.format("Updating configuration from %d to %d.", configVersion, CURRENT_VERSION));
|
||||
LOG.info(String.format("Updating configuration from %d to %d.", configVersion, CURRENT_VERSION));
|
||||
Map<?, ?> rawJson = Collections.unmodifiableMap(new Gson().<Map<?, ?>>fromJson(rawContent, Map.class));
|
||||
|
||||
if (configVersion < 1) {
|
||||
|
||||
@@ -55,7 +55,6 @@ public final class DownloadProviders {
|
||||
|
||||
private static final MojangDownloadProvider MOJANG;
|
||||
private static final BMCLAPIDownloadProvider BMCLAPI;
|
||||
// private static final BMCLAPIDownloadProvider MCBBS;
|
||||
|
||||
public static final String DEFAULT_PROVIDER_ID = "balanced";
|
||||
public static final String DEFAULT_RAW_PROVIDER_ID = "bmclapi";
|
||||
@@ -69,27 +68,19 @@ public final class DownloadProviders {
|
||||
|
||||
MOJANG = new MojangDownloadProvider();
|
||||
BMCLAPI = new BMCLAPIDownloadProvider(bmclapiRoot);
|
||||
// MCBBS = new BMCLAPIDownloadProvider("https://download.mcbbs.net");
|
||||
rawProviders = mapOf(
|
||||
pair("mojang", MOJANG),
|
||||
pair("bmclapi", BMCLAPI)
|
||||
// pair("mcbbs", MCBBS)
|
||||
);
|
||||
|
||||
AdaptedDownloadProvider fileProvider = new AdaptedDownloadProvider();
|
||||
fileProvider.setDownloadProviderCandidates(Arrays.asList(
|
||||
// MCBBS,
|
||||
BMCLAPI,
|
||||
MOJANG));
|
||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(
|
||||
MOJANG,
|
||||
// MCBBS,
|
||||
BMCLAPI);
|
||||
fileProvider.setDownloadProviderCandidates(Arrays.asList(BMCLAPI, MOJANG));
|
||||
BalancedDownloadProvider balanced = new BalancedDownloadProvider(MOJANG, BMCLAPI);
|
||||
|
||||
providersById = mapOf(
|
||||
pair("official", new AutoDownloadProvider(MOJANG, fileProvider)),
|
||||
pair("balanced", new AutoDownloadProvider(balanced, fileProvider)),
|
||||
pair("mirror", new AutoDownloadProvider(BMCLAPI /* MCBBS */, fileProvider)));
|
||||
pair("mirror", new AutoDownloadProvider(BMCLAPI, fileProvider)));
|
||||
|
||||
observer = FXUtils.observeWeak(() -> {
|
||||
FetchTask.setDownloadExecutorConcurrency(
|
||||
@@ -108,12 +99,11 @@ public final class DownloadProviders {
|
||||
.orElse(providersById.get(DEFAULT_PROVIDER_ID));
|
||||
});
|
||||
|
||||
FXUtils.onChangeAndOperate(config().downloadTypeProperty(), downloadType -> {
|
||||
if (!rawProviders.containsKey(downloadType)) {
|
||||
config().setDownloadType(DEFAULT_RAW_PROVIDER_ID);
|
||||
return;
|
||||
}
|
||||
if (!rawProviders.containsKey(config().getDownloadType())) {
|
||||
config().setDownloadType(DEFAULT_RAW_PROVIDER_ID);
|
||||
}
|
||||
|
||||
FXUtils.onChangeAndOperate(config().downloadTypeProperty(), downloadType -> {
|
||||
DownloadProvider primary = Optional.ofNullable(rawProviders.get(downloadType))
|
||||
.orElse(rawProviders.get(DEFAULT_RAW_PROVIDER_ID));
|
||||
fileDownloadProvider.setDownloadProviderCandidates(
|
||||
|
||||
@@ -21,9 +21,7 @@ import com.google.gson.*;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
|
||||
import org.jackhuang.hmcl.util.javafx.PropertyUtils;
|
||||
@@ -33,7 +31,7 @@ import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
|
||||
@JsonAdapter(GlobalConfig.Serializer.class)
|
||||
public class GlobalConfig implements Cloneable, Observable {
|
||||
public final class GlobalConfig implements Cloneable, Observable {
|
||||
|
||||
@Nullable
|
||||
public static GlobalConfig fromJson(String json) throws JsonParseException {
|
||||
@@ -47,17 +45,15 @@ public class GlobalConfig implements Cloneable, Observable {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private IntegerProperty agreementVersion = new SimpleIntegerProperty();
|
||||
private final IntegerProperty agreementVersion = new SimpleIntegerProperty();
|
||||
|
||||
private IntegerProperty platformPromptVersion = new SimpleIntegerProperty();
|
||||
private final IntegerProperty platformPromptVersion = new SimpleIntegerProperty();
|
||||
|
||||
private BooleanProperty multiplayerRelay = new SimpleBooleanProperty();
|
||||
|
||||
private IntegerProperty multiplayerAgreementVersion = new SimpleIntegerProperty(0);
|
||||
private final IntegerProperty logRetention = new SimpleIntegerProperty();
|
||||
|
||||
private final Map<String, Object> unknownFields = new HashMap<>();
|
||||
|
||||
private transient ObservableHelper helper = new ObservableHelper(this);
|
||||
private final transient ObservableHelper helper = new ObservableHelper(this);
|
||||
|
||||
public GlobalConfig() {
|
||||
PropertyUtils.attachListener(this, helper);
|
||||
@@ -106,37 +102,23 @@ public class GlobalConfig implements Cloneable, Observable {
|
||||
this.platformPromptVersion.set(platformPromptVersion);
|
||||
}
|
||||
|
||||
public boolean isMultiplayerRelay() {
|
||||
return multiplayerRelay.get();
|
||||
public int getLogRetention() {
|
||||
return logRetention.get();
|
||||
}
|
||||
|
||||
public BooleanProperty multiplayerRelayProperty() {
|
||||
return multiplayerRelay;
|
||||
public IntegerProperty logRetentionProperty() {
|
||||
return logRetention;
|
||||
}
|
||||
|
||||
public void setMultiplayerRelay(boolean multiplayerRelay) {
|
||||
this.multiplayerRelay.set(multiplayerRelay);
|
||||
public void setLogRetention(int logRetention) {
|
||||
this.logRetention.set(logRetention);
|
||||
}
|
||||
|
||||
public int getMultiplayerAgreementVersion() {
|
||||
return multiplayerAgreementVersion.get();
|
||||
}
|
||||
|
||||
public IntegerProperty multiplayerAgreementVersionProperty() {
|
||||
return multiplayerAgreementVersion;
|
||||
}
|
||||
|
||||
public void setMultiplayerAgreementVersion(int multiplayerAgreementVersion) {
|
||||
this.multiplayerAgreementVersion.set(multiplayerAgreementVersion);
|
||||
}
|
||||
|
||||
public static class Serializer implements JsonSerializer<GlobalConfig>, JsonDeserializer<GlobalConfig> {
|
||||
public static final class Serializer implements JsonSerializer<GlobalConfig>, JsonDeserializer<GlobalConfig> {
|
||||
private static final Set<String> knownFields = new HashSet<>(Arrays.asList(
|
||||
"agreementVersion",
|
||||
"platformPromptVersion",
|
||||
"multiplayerToken",
|
||||
"multiplayerRelay",
|
||||
"multiplayerAgreementVersion"
|
||||
"logRetention"
|
||||
));
|
||||
|
||||
@Override
|
||||
@@ -148,8 +130,7 @@ public class GlobalConfig implements Cloneable, Observable {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.add("agreementVersion", context.serialize(src.getAgreementVersion()));
|
||||
jsonObject.add("platformPromptVersion", context.serialize(src.getPlatformPromptVersion()));
|
||||
jsonObject.add("multiplayerRelay", context.serialize(src.isMultiplayerRelay()));
|
||||
jsonObject.add("multiplayerAgreementVersion", context.serialize(src.getMultiplayerAgreementVersion()));
|
||||
jsonObject.add("logRetention", context.serialize(src.getLogRetention()));
|
||||
for (Map.Entry<String, Object> entry : src.unknownFields.entrySet()) {
|
||||
jsonObject.add(entry.getKey(), context.serialize(entry.getValue()));
|
||||
}
|
||||
@@ -166,8 +147,7 @@ public class GlobalConfig implements Cloneable, Observable {
|
||||
GlobalConfig config = new GlobalConfig();
|
||||
config.setAgreementVersion(Optional.ofNullable(obj.get("agreementVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setPlatformPromptVersion(Optional.ofNullable(obj.get("platformPromptVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setMultiplayerRelay(Optional.ofNullable(obj.get("multiplayerRelay")).map(JsonElement::getAsBoolean).orElse(false));
|
||||
config.setMultiplayerAgreementVersion(Optional.ofNullable(obj.get("multiplayerAgreementVersion")).map(JsonElement::getAsInt).orElse(0));
|
||||
config.setLogRetention(Optional.ofNullable(obj.get("logRetention")).map(JsonElement::getAsInt).orElse(20));
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
|
||||
if (!knownFields.contains(entry.getKey())) {
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import org.jackhuang.hmcl.auth.OAuth;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
public final class HMCLAccounts {
|
||||
|
||||
private static final ObjectProperty<HMCLAccount> account = new SimpleObjectProperty<>();
|
||||
|
||||
private HMCLAccounts() {
|
||||
}
|
||||
|
||||
public static HMCLAccount getAccount() {
|
||||
return account.get();
|
||||
}
|
||||
|
||||
public static ObjectProperty<HMCLAccount> accountProperty() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public static void setAccount(HMCLAccount account) {
|
||||
HMCLAccounts.account.set(account);
|
||||
}
|
||||
|
||||
public static Task<Void> login() {
|
||||
String nonce = UUIDTypeAdapter.fromUUID(UUID.randomUUID());
|
||||
String scope = "openid offline_access";
|
||||
|
||||
return Task.supplyAsync(() -> {
|
||||
OAuth.Session session = Accounts.OAUTH_CALLBACK.startServer();
|
||||
Accounts.OAUTH_CALLBACK.openBrowser(NetworkUtils.withQuery(
|
||||
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
mapOf(
|
||||
pair("client_id", Accounts.OAUTH_CALLBACK.getClientId()),
|
||||
pair("response_type", "id_token code"),
|
||||
pair("response_mode", "form_post"),
|
||||
pair("scope", scope),
|
||||
pair("redirect_uri", session.getRedirectURI()),
|
||||
pair("nonce", nonce)
|
||||
)));
|
||||
String code = session.waitFor();
|
||||
|
||||
// Authorization Code -> Token
|
||||
String responseText = HttpRequest.POST("https://login.microsoftonline.com/common/oauth2/v2.0/token")
|
||||
.form(mapOf(pair("client_id", Accounts.OAUTH_CALLBACK.getClientId()), pair("code", code),
|
||||
pair("grant_type", "authorization_code"), pair("client_secret", Accounts.OAUTH_CALLBACK.getClientSecret()),
|
||||
pair("redirect_uri", session.getRedirectURI()), pair("scope", scope)))
|
||||
.getString();
|
||||
OAuth.AuthorizationResponse response = JsonUtils.fromNonNullJson(responseText,
|
||||
OAuth.AuthorizationResponse.class);
|
||||
|
||||
HMCLAccountProfile profile = HttpRequest.GET("https://hmcl.huangyuhui.net/api/user")
|
||||
.header("Token-Type", response.tokenType)
|
||||
.header("Access-Token", response.accessToken)
|
||||
.header("Authorization-Provider", "microsoft")
|
||||
.authorization("Bearer", session.getIdToken())
|
||||
.getJson(HMCLAccountProfile.class);
|
||||
|
||||
return new HMCLAccount("microsoft", profile.nickname, profile.email, profile.role, session.getIdToken(), response.tokenType, response.accessToken, response.refreshToken);
|
||||
}).thenAcceptAsync(Schedulers.javafx(), account -> {
|
||||
setAccount(account);
|
||||
});
|
||||
}
|
||||
|
||||
public static class HMCLAccount implements HttpRequest.Authorization {
|
||||
private final String provider;
|
||||
private final String nickname;
|
||||
private final String email;
|
||||
private final String role;
|
||||
private final String idToken;
|
||||
private final String tokenType;
|
||||
private final String accessToken;
|
||||
private final String refreshToken;
|
||||
|
||||
public HMCLAccount(String provider, String nickname, String email, String role, String idToken, String tokenType, String accessToken, String refreshToken) {
|
||||
this.provider = provider;
|
||||
this.nickname = nickname;
|
||||
this.email = email;
|
||||
this.role = role;
|
||||
this.idToken = idToken;
|
||||
this.tokenType = tokenType;
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public String getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public String getIdToken() {
|
||||
return idToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HMCLAccountProfile {
|
||||
@SerializedName("ID")
|
||||
String id;
|
||||
@SerializedName("Provider")
|
||||
String provider;
|
||||
@SerializedName("Email")
|
||||
String email;
|
||||
@SerializedName("NickName")
|
||||
String nickname;
|
||||
@SerializedName("Role")
|
||||
String role;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import java.net.Proxy;
|
||||
import java.net.Proxy.Type;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class ProxyManager {
|
||||
private ProxyManager() {
|
||||
|
||||
@@ -24,8 +24,6 @@ import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.util.CacheRepository;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
|
||||
public final class Settings {
|
||||
@@ -47,9 +45,6 @@ public final class Settings {
|
||||
}
|
||||
|
||||
private Settings() {
|
||||
config().localizationProperty().addListener(unused -> updateSystemLocale());
|
||||
updateSystemLocale();
|
||||
|
||||
DownloadProviders.init();
|
||||
ProxyManager.init();
|
||||
Accounts.init();
|
||||
@@ -81,8 +76,4 @@ public final class Settings {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateSystemLocale() {
|
||||
Locale.setDefault(config().getLocalization().getLocale());
|
||||
}
|
||||
}
|
||||
@@ -23,23 +23,27 @@ import com.google.gson.stream.JsonWriter;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
@JsonAdapter(Theme.TypeAdapter.class)
|
||||
public class Theme {
|
||||
@@ -82,6 +86,30 @@ public class Theme {
|
||||
return cssCharset;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static Optional<Font> font;
|
||||
|
||||
private static Optional<Font> tryLoadFont() {
|
||||
//noinspection OptionalAssignedToNull
|
||||
if (font != null) {
|
||||
return font;
|
||||
}
|
||||
|
||||
Path path = Paths.get("font.ttf");
|
||||
if (!Files.isRegularFile(path)) {
|
||||
path = Paths.get("font.otf");
|
||||
}
|
||||
|
||||
if (Files.isRegularFile(path)) {
|
||||
try {
|
||||
return font = Optional.ofNullable(Font.loadFont(path.toAbsolutePath().toUri().toURL().toExternalForm(), 0));
|
||||
} catch (MalformedURLException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return font = Optional.empty();
|
||||
}
|
||||
|
||||
public static Theme getTheme() {
|
||||
Theme theme = config().getTheme();
|
||||
return theme == null ? BLUE : theme;
|
||||
@@ -121,9 +149,24 @@ public class Theme {
|
||||
String css = "/assets/css/blue.css";
|
||||
|
||||
String fontFamily = System.getProperty("hmcl.font.override", overrideFontFamily);
|
||||
String fontStyle = null;
|
||||
if (fontFamily == null) {
|
||||
Optional<Font> font = tryLoadFont();
|
||||
if (font.isPresent()) {
|
||||
fontFamily = font.get().getFamily();
|
||||
fontStyle = font.get().getStyle();
|
||||
}
|
||||
}
|
||||
|
||||
if (fontFamily != null || !this.color.equalsIgnoreCase(BLUE.color)) {
|
||||
Color textFill = getForegroundColor();
|
||||
String fontCss = "";
|
||||
if (fontFamily != null) {
|
||||
fontCss = "-fx-font-family: \"" + fontFamily + "\";";
|
||||
if (fontStyle != null && !fontStyle.isEmpty())
|
||||
fontCss += " -fx-font-style: \"" + fontStyle + "\";";
|
||||
}
|
||||
|
||||
try {
|
||||
File temp = File.createTempFile("hmcl", ".css");
|
||||
String themeText = IOUtils.readFullyAsString(Theme.class.getResourceAsStream("/assets/css/custom.css"))
|
||||
@@ -134,12 +177,12 @@ public class Theme {
|
||||
.replace("%base-rippler-color%", String.format("rgba(%d, %d, %d, 0.3)", (int) Math.ceil(paint.getRed() * 256), (int) Math.ceil(paint.getGreen() * 256), (int) Math.ceil(paint.getBlue() * 256)))
|
||||
.replace("%disabled-font-color%", String.format("rgba(%d, %d, %d, 0.7)", (int) Math.ceil(textFill.getRed() * 256), (int) Math.ceil(textFill.getGreen() * 256), (int) Math.ceil(textFill.getBlue() * 256)))
|
||||
.replace("%font-color%", getColorDisplayName(getForegroundColor()))
|
||||
.replace("%font%", Optional.ofNullable(fontFamily).map(f -> "-fx-font-family: \"" + f + "\";").orElse(""));
|
||||
.replace("%font%", fontCss);
|
||||
FileUtils.writeText(temp, themeText, getCssCharset());
|
||||
temp.deleteOnExit();
|
||||
css = temp.toURI().toString();
|
||||
} catch (IOException | NullPointerException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to create theme stylesheet. Fallback to blue theme.", e);
|
||||
LOG.error("Unable to create theme stylesheet. Fallback to blue theme.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.*;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Controllers {
|
||||
@@ -165,7 +166,7 @@ public final class Controllers {
|
||||
}
|
||||
|
||||
public static void initialize(Stage stage) {
|
||||
Logging.LOG.info("Start initializing application");
|
||||
LOG.info("Start initializing application");
|
||||
|
||||
Controllers.stage = stage;
|
||||
|
||||
@@ -298,7 +299,7 @@ public final class Controllers {
|
||||
});
|
||||
JFXButton noButton = new JFXButton(i18n("launcher.agreement.decline"));
|
||||
noButton.getStyleClass().add("dialog-cancel");
|
||||
noButton.setOnAction(e -> System.exit(1));
|
||||
noButton.setOnAction(e -> javafx.application.Platform.exit());
|
||||
agreementPane.setActions(agreementLink, yesButton, noButton);
|
||||
Controllers.dialog(agreementPane);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class CrashWindow extends Stage {
|
||||
FXUtils.setIcon(this);
|
||||
setTitle(i18n("message.error"));
|
||||
|
||||
setOnCloseRequest(e -> System.exit(1));
|
||||
setOnCloseRequest(e -> javafx.application.Platform.exit());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,7 +55,6 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.ResourceNotFoundError;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.ExtendedProperties;
|
||||
@@ -79,6 +78,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
@@ -87,12 +87,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class FXUtils {
|
||||
@@ -109,7 +108,7 @@ public final class FXUtils {
|
||||
try {
|
||||
Files.deleteIfExists(entry.getValue());
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, String.format("Failed to delete cache file %s.", entry.getValue()), e);
|
||||
LOG.warning(String.format("Failed to delete cache file %s.", entry.getValue()), e);
|
||||
}
|
||||
remoteImageCache.remove(entry.getKey());
|
||||
}
|
||||
@@ -336,7 +335,7 @@ public final class FXUtils {
|
||||
Tooltip.class.getMethod("setHideDelay", Duration.class).invoke(tooltip, new Duration(closeDelay));
|
||||
} catch (ReflectiveOperationException e2) {
|
||||
e.addSuppressed(e2);
|
||||
Logging.LOG.log(Level.SEVERE, "Cannot install tooltip", e);
|
||||
LOG.error("Cannot install tooltip", e);
|
||||
}
|
||||
Tooltip.install(node, tooltip);
|
||||
}
|
||||
@@ -369,7 +368,7 @@ public final class FXUtils {
|
||||
|
||||
public static void openFolder(File file) {
|
||||
if (!FileUtils.makeDirectory(file)) {
|
||||
LOG.log(Level.SEVERE, "Unable to make directory " + file);
|
||||
LOG.error("Unable to make directory " + file);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -396,7 +395,7 @@ public final class FXUtils {
|
||||
else
|
||||
LOG.warning("Open " + path + " failed with code " + exitCode);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Unable to open " + path + " by executing " + openCommand, e);
|
||||
LOG.warning("Unable to open " + path + " by executing " + openCommand, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +403,7 @@ public final class FXUtils {
|
||||
try {
|
||||
java.awt.Desktop.getDesktop().open(file);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.SEVERE, "Unable to open " + path + " by java.awt.Desktop.getDesktop()::open", e);
|
||||
LOG.error("Unable to open " + path + " by java.awt.Desktop.getDesktop()::open", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -431,7 +430,7 @@ public final class FXUtils {
|
||||
else
|
||||
LOG.warning("Show " + path + " in explorer failed with code " + exitCode);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Unable to show " + path + " in explorer", e);
|
||||
LOG.warning("Unable to show " + path + " in explorer", e);
|
||||
}
|
||||
|
||||
// Fallback to open folder
|
||||
@@ -468,7 +467,7 @@ public final class FXUtils {
|
||||
Runtime.getRuntime().exec(new String[]{"rundll32.exe", "url.dll,FileProtocolHandler", link});
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "An exception occurred while calling rundll32", e);
|
||||
LOG.warning("An exception occurred while calling rundll32", e);
|
||||
}
|
||||
}
|
||||
if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
@@ -480,7 +479,7 @@ public final class FXUtils {
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
Logging.LOG.log(Level.WARNING, "No known browser found");
|
||||
LOG.warning("No known browser found");
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -490,9 +489,9 @@ public final class FXUtils {
|
||||
try {
|
||||
Runtime.getRuntime().exec(new String[]{"/usr/bin/open", link});
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to open link: " + link, ex);
|
||||
LOG.warning("Unable to open link: " + link, ex);
|
||||
}
|
||||
Logging.LOG.log(Level.WARNING, "Failed to open link: " + link, e);
|
||||
LOG.warning("Failed to open link: " + link, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -508,7 +507,7 @@ public final class FXUtils {
|
||||
stage.setTitle(title);
|
||||
stage.showAndWait();
|
||||
} catch (NoClassDefFoundError | UnsatisfiedLinkError e) {
|
||||
LOG.log(Level.WARNING, "WebView is missing or initialization failed, use JEditorPane replaced", e);
|
||||
LOG.warning("WebView is missing or initialization failed, use JEditorPane replaced", e);
|
||||
|
||||
SwingUtils.initLookAndFeel();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
@@ -760,7 +759,7 @@ public final class FXUtils {
|
||||
try (InputStream inputStream = Files.newInputStream(currentPath)) {
|
||||
return new Image(inputStream, requestedWidth, requestedHeight, preserveRatio, smooth);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "An exception encountered while reading data from cached image file.", e);
|
||||
LOG.warning("An exception encountered while reading data from cached image file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,7 +769,7 @@ public final class FXUtils {
|
||||
try {
|
||||
Files.deleteIfExists(currentPath);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "An exception encountered while deleting broken cached image file.", e);
|
||||
LOG.warning("An exception encountered while deleting broken cached image file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +942,14 @@ public final class FXUtils {
|
||||
if ("a".equals(element.getTagName())) {
|
||||
String href = element.getAttribute("href");
|
||||
JFXHyperlink hyperlink = new JFXHyperlink(element.getTextContent());
|
||||
hyperlink.setOnAction(e -> hyperlinkAction.accept(href));
|
||||
hyperlink.setOnAction(e -> {
|
||||
String link = href;
|
||||
try {
|
||||
link = new URI(href).toASCIIString();
|
||||
} catch (URISyntaxException ignored) {
|
||||
}
|
||||
hyperlinkAction.accept(link);
|
||||
});
|
||||
texts.add(hyperlink);
|
||||
} else if ("b".equals(element.getTagName())) {
|
||||
Text text = new Text(element.getTextContent());
|
||||
@@ -960,7 +966,7 @@ public final class FXUtils {
|
||||
}
|
||||
return texts;
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to parse xml", e);
|
||||
LOG.warning("Failed to parse xml", e);
|
||||
return Collections.singletonList(new Text(segment));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,9 @@ import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.logging.Logger;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.CommandBuilder;
|
||||
@@ -60,14 +61,13 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -102,7 +102,7 @@ public class GameCrashWindow extends Stage {
|
||||
|
||||
memory = Optional.ofNullable(launchOptions.getMaxMemory()).map(i -> i + " MB").orElse("-");
|
||||
|
||||
total_memory = Optional.ofNullable(OperatingSystem.TOTAL_MEMORY).map(i -> i + " MB").orElse("-");
|
||||
total_memory = OperatingSystem.TOTAL_MEMORY + " MB";
|
||||
|
||||
this.java = launchOptions.getJava().getArchitecture() == Architecture.SYSTEM_ARCH
|
||||
? launchOptions.getJava().getVersion()
|
||||
@@ -131,7 +131,7 @@ public class GameCrashWindow extends Stage {
|
||||
try {
|
||||
crashReport = CrashReportAnalyzer.findCrashReport(rawLog);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to read crash report", e);
|
||||
LOG.warning("Failed to read crash report", e);
|
||||
}
|
||||
if (crashReport == null) {
|
||||
crashReport = CrashReportAnalyzer.extractCrashReport(rawLog);
|
||||
@@ -148,7 +148,7 @@ public class GameCrashWindow extends Stage {
|
||||
try {
|
||||
log = FileUtils.readText(latestLog);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to read logs/latest.log", e);
|
||||
LOG.warning("Failed to read logs/latest.log", e);
|
||||
return pair(new HashSet<CrashReportAnalyzer.Result>(), new HashSet<String>());
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public class GameCrashWindow extends Stage {
|
||||
loading.set(false);
|
||||
|
||||
if (exception != null) {
|
||||
LOG.log(Level.WARNING, "Failed to analyze crash report", exception);
|
||||
LOG.warning("Failed to analyze crash report", exception);
|
||||
reasonTextFlow.getChildren().setAll(FXUtils.parseSegment(i18n("game.crash.reason.unknown"), Controllers::onHyperlinkAction));
|
||||
} else {
|
||||
EnumMap<CrashReportAnalyzer.Rule, CrashReportAnalyzer.Result> results = new EnumMap<>(CrashReportAnalyzer.Rule.class);
|
||||
@@ -169,64 +169,60 @@ public class GameCrashWindow extends Stage {
|
||||
keywords.addAll(pair.getValue());
|
||||
}
|
||||
|
||||
List<Node> segments = new ArrayList<>();
|
||||
List<Node> segments = new ArrayList<>(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
|
||||
|
||||
boolean hasMultipleRules = results.keySet().stream().distinct().count() > 1;
|
||||
if (hasMultipleRules) {
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
|
||||
LOG.info("Number of reasons: " + results.size());
|
||||
if (results.size() > 1) {
|
||||
segments.add(new Text("\n"));
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason.multiple"), Controllers::onHyperlinkAction));
|
||||
LOG.log(Level.INFO, "Multiple reasons detected");
|
||||
} else {
|
||||
segments.add(new Text("\n\n"));
|
||||
}
|
||||
|
||||
for (CrashReportAnalyzer.Result result : results.values()) {
|
||||
String message;
|
||||
switch (result.getRule()) {
|
||||
case TOO_OLD_JAVA:
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason.too_old_java",
|
||||
CrashReportAnalyzer.getJavaVersionFromMajorVersion(Integer.parseInt(result.getMatcher().group("expected")))), Controllers::onHyperlinkAction));
|
||||
message = i18n("game.crash.reason.too_old_java", CrashReportAnalyzer.getJavaVersionFromMajorVersion(Integer.parseInt(result.getMatcher().group("expected"))));
|
||||
break;
|
||||
case MOD_RESOLUTION_CONFLICT:
|
||||
case MOD_RESOLUTION_MISSING:
|
||||
case MOD_RESOLUTION_COLLECTION:
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
message = i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
translateFabricModId(result.getMatcher().group("sourcemod")),
|
||||
parseFabricModId(result.getMatcher().group("destmod")),
|
||||
parseFabricModId(result.getMatcher().group("destmod"))), Controllers::onHyperlinkAction));
|
||||
parseFabricModId(result.getMatcher().group("destmod")));
|
||||
break;
|
||||
case MOD_RESOLUTION_MISSING_MINECRAFT:
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
message = i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
translateFabricModId(result.getMatcher().group("mod")),
|
||||
result.getMatcher().group("version")), Controllers::onHyperlinkAction));
|
||||
result.getMatcher().group("version"));
|
||||
break;
|
||||
case MOD_FOREST_OPTIFINE:
|
||||
case TWILIGHT_FOREST_OPTIFINE:
|
||||
case PERFORMANT_FOREST_OPTIFINE:
|
||||
case JADE_FOREST_OPTIFINE:
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason.mod", "OptiFine"), Controllers::onHyperlinkAction));
|
||||
case NEOFORGE_FOREST_OPTIFINE:
|
||||
message = i18n("game.crash.reason.mod", "OptiFine");
|
||||
LOG.info("Crash cause: " + result.getRule() + ": " + i18n("game.crash.reason.mod", "OptiFine"));
|
||||
break;
|
||||
default:
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
message = i18n("game.crash.reason." + result.getRule().name().toLowerCase(Locale.ROOT),
|
||||
Arrays.stream(result.getRule().getGroupNames()).map(groupName -> result.getMatcher().group(groupName))
|
||||
.toArray()), Controllers::onHyperlinkAction));
|
||||
.toArray());
|
||||
break;
|
||||
}
|
||||
segments.add(new Text("\n"));
|
||||
if (hasMultipleRules) {
|
||||
segments.add(new Text("\n"));
|
||||
} else {
|
||||
segments.add(new Text("\n"));
|
||||
segments.addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
|
||||
segments.add(new Text("\n"));
|
||||
}
|
||||
LOG.log(Level.INFO, "Crash cause: " + result.getRule());
|
||||
LOG.info("Crash cause: " + result.getRule() + ": " + message);
|
||||
segments.addAll(FXUtils.parseSegment(message, Controllers::onHyperlinkAction));
|
||||
segments.add(new Text("\n\n"));
|
||||
}
|
||||
if (results.isEmpty()) {
|
||||
if (!keywords.isEmpty()) {
|
||||
reasonTextFlow.getChildren().setAll(new Text(i18n("game.crash.reason.stacktrace", String.join(", ", keywords))));
|
||||
LOG.log(Level.INFO, "Crash reason unknown, but some log keywords have been found: " + String.join(", ", keywords));
|
||||
LOG.info("Crash reason unknown, but some log keywords have been found: " + String.join(", ", keywords));
|
||||
} else {
|
||||
reasonTextFlow.getChildren().setAll(FXUtils.parseSegment(i18n("game.crash.reason.unknown"), Controllers::onHyperlinkAction));
|
||||
LOG.log(Level.INFO, "Crash reason unknown");
|
||||
LOG.info("Crash reason unknown");
|
||||
}
|
||||
} else {
|
||||
feedbackTextFlow.setVisible(false);
|
||||
@@ -268,7 +264,7 @@ public class GameCrashWindow extends Stage {
|
||||
private void showLogWindow() {
|
||||
LogWindow logWindow = new LogWindow(managedProcess);
|
||||
|
||||
logWindow.logLine(Logging.filterForbiddenToken("Command: " + new CommandBuilder().addAll(managedProcess.getCommands())), Log4jLevel.INFO);
|
||||
logWindow.logLine(Logger.filterForbiddenToken("Command: " + new CommandBuilder().addAll(managedProcess.getCommands())), Log4jLevel.INFO);
|
||||
if (managedProcess.getClasspath() != null)
|
||||
logWindow.logLine("ClassPath: " + managedProcess.getClasspath(), Log4jLevel.INFO);
|
||||
for (Map.Entry<String, Log4jLevel> entry : logs)
|
||||
@@ -284,15 +280,20 @@ public class GameCrashWindow extends Stage {
|
||||
logs.stream().map(Pair::getKey).collect(Collectors.joining(OperatingSystem.LINE_SEPARATOR)))
|
||||
.thenComposeAsync(logs ->
|
||||
LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString()))
|
||||
.thenRunAsync(() -> {
|
||||
FXUtils.showFileInExplorer(logFile);
|
||||
.handleAsync((result, exception) -> {
|
||||
Alert alert;
|
||||
|
||||
if (exception == null) {
|
||||
FXUtils.showFileInExplorer(logFile);
|
||||
alert = new Alert(Alert.AlertType.INFORMATION, i18n("settings.launcher.launcher_log.export.success", logFile));
|
||||
} else {
|
||||
LOG.warning("Failed to export game crash info", exception);
|
||||
alert = new Alert(Alert.AlertType.WARNING, i18n("settings.launcher.launcher_log.export.failed") + "\n" + StringUtils.getStackTrace(exception));
|
||||
}
|
||||
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION, i18n("settings.launcher.launcher_log.export.success", logFile));
|
||||
alert.setTitle(i18n("settings.launcher.launcher_log.export"));
|
||||
alert.showAndWait();
|
||||
}, Schedulers.javafx())
|
||||
.exceptionally(e -> {
|
||||
LOG.log(Level.WARNING, "Failed to export game crash info", e);
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,13 +55,12 @@ import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.parseEscapeSequence;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -210,7 +209,7 @@ public final class LogWindow extends Stage {
|
||||
try {
|
||||
Files.write(logFile, logs.stream().map(x -> x.log).collect(Collectors.toList()));
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to export logs", e);
|
||||
LOG.warning("Failed to export logs", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -238,7 +237,7 @@ public final class LogWindow extends Stage {
|
||||
FXUtils.showFileInExplorer(dumpFile);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Failed to create minecraft jstack dump", e);
|
||||
LOG.warning("Failed to create minecraft jstack dump", e);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, i18n("logwindow.export_dump.dependency_ok.button"));
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum SVG {
|
||||
GEAR_OUTLINE("M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"),
|
||||
BACK("M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"),
|
||||
CANCEL("M12 2C17.5 2 22 6.5 22 12S17.5 22 12 22 2 17.5 2 12 6.5 2 12 2M12 4C10.1 4 8.4 4.6 7.1 5.7L18.3 16.9C19.3 15.5 20 13.8 20 12C20 7.6 16.4 4 12 4M16.9 18.3L5.7 7.1C4.6 8.4 4 10.1 4 12C4 16.4 7.6 20 12 20C13.9 20 15.6 19.4 16.9 18.3Z"),
|
||||
CHAT("M20,2A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H6L2,22V4C2,2.89 2.9,2 4,2H20M4,4V17.17L5.17,16H20V4H4M6,7H18V9H6V7M6,11H15V13H6V11Z"),
|
||||
CLOSE("M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"),
|
||||
COPY("M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"),
|
||||
DOTS_VERTICAL("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"),
|
||||
|
||||
@@ -27,11 +27,10 @@ import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.Metadata.CHANGELOG_URL;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class UpgradeDialog extends JFXDialogLayout {
|
||||
@@ -56,7 +55,7 @@ public class UpgradeDialog extends JFXDialogLayout {
|
||||
});
|
||||
setBody(webView);
|
||||
} catch (NoClassDefFoundError | UnsatisfiedLinkError e) {
|
||||
LOG.log(Level.WARNING, "WebView is missing or initialization failed", e);
|
||||
LOG.warning("WebView is missing or initialization failed", e);
|
||||
FXUtils.openLink(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import javafx.scene.control.Tooltip;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
@@ -58,7 +57,7 @@ public class AccountAdvancedListItem extends AdvancedListItem {
|
||||
tooltip.setText(i18n("account.create"));
|
||||
|
||||
TexturesLoader.unbindAvatar(canvas);
|
||||
TexturesLoader.drawAvatar(canvas, TexturesLoader.getDefaultSkin(TextureModel.STEVE).getImage());
|
||||
TexturesLoader.drawAvatar(canvas, TexturesLoader.getDefaultSkinImage());
|
||||
|
||||
} else {
|
||||
titleProperty().bind(BindingMapping.of(account, Account::getCharacter));
|
||||
|
||||
@@ -54,11 +54,10 @@ import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class AccountListItem extends RadioButton {
|
||||
@@ -109,30 +108,26 @@ public class AccountListItem extends RadioButton {
|
||||
} catch (CancellationException e1) {
|
||||
// ignore cancellation
|
||||
} catch (Exception e1) {
|
||||
LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1);
|
||||
LOG.warning("Failed to refresh " + account + " with password", e1);
|
||||
throw e1;
|
||||
}
|
||||
} catch (AuthenticationException e) {
|
||||
LOG.log(Level.WARNING, "Failed to refresh " + account + " with token", e);
|
||||
LOG.warning("Failed to refresh " + account + " with token", e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ObservableBooleanValue canUploadSkin() {
|
||||
if (account instanceof YggdrasilAccount) {
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
AuthlibInjectorAccount aiAccount = (AuthlibInjectorAccount) account;
|
||||
ObjectBinding<Optional<CompleteGameProfile>> profile = aiAccount.getYggdrasilService().getProfileRepository().binding(aiAccount.getUUID());
|
||||
return createBooleanBinding(() -> {
|
||||
Set<TextureType> uploadableTextures = profile.get()
|
||||
.map(AuthlibInjectorAccount::getUploadableTextures)
|
||||
.orElse(emptySet());
|
||||
return uploadableTextures.contains(TextureType.SKIN);
|
||||
}, profile);
|
||||
} else {
|
||||
return createBooleanBinding(() -> true);
|
||||
}
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
AuthlibInjectorAccount aiAccount = (AuthlibInjectorAccount) account;
|
||||
ObjectBinding<Optional<CompleteGameProfile>> profile = aiAccount.getYggdrasilService().getProfileRepository().binding(aiAccount.getUUID());
|
||||
return createBooleanBinding(() -> {
|
||||
Set<TextureType> uploadableTextures = profile.get()
|
||||
.map(AuthlibInjectorAccount::getUploadableTextures)
|
||||
.orElse(emptySet());
|
||||
return uploadableTextures.contains(TextureType.SKIN);
|
||||
}, profile);
|
||||
} else if (account instanceof OfflineAccount || account instanceof MicrosoftAccount) {
|
||||
return createBooleanBinding(() -> true);
|
||||
} else {
|
||||
|
||||
@@ -44,10 +44,9 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.createSelectedItemPropertyFor;
|
||||
|
||||
@@ -107,14 +106,6 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
offlineItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_OFFLINE)));
|
||||
boxMethods.getChildren().add(offlineItem);
|
||||
|
||||
AdvancedListItem mojangItem = new AdvancedListItem();
|
||||
mojangItem.getStyleClass().add("navigation-drawer-item");
|
||||
mojangItem.setActionButtonVisible(false);
|
||||
mojangItem.setTitle(i18n("account.methods.yggdrasil"));
|
||||
mojangItem.setLeftGraphic(wrap(SVG.MOJANG));
|
||||
mojangItem.setOnAction(e -> Controllers.dialog(new CreateAccountPane(Accounts.FACTORY_MOJANG)));
|
||||
boxMethods.getChildren().add(mojangItem);
|
||||
|
||||
AdvancedListItem microsoftItem = new AdvancedListItem();
|
||||
microsoftItem.getStyleClass().add("navigation-drawer-item");
|
||||
microsoftItem.setActionButtonVisible(false);
|
||||
@@ -145,7 +136,7 @@ public class AccountListPage extends DecoratorAnimatedPage implements DecoratorP
|
||||
try {
|
||||
host = URI.create(server.getUrl()).getHost();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.log(Level.WARNING, "Unparsable authlib-injector server url " + server.getUrl(), e);
|
||||
LOG.warning("Unparsable authlib-injector server url " + server.getUrl(), e);
|
||||
}
|
||||
item.subtitleProperty().set(host);
|
||||
Tooltip tooltip = new Tooltip();
|
||||
|
||||
@@ -35,11 +35,10 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class AddAuthlibInjectorServerPane extends TransitionPane implements DialogAware {
|
||||
@@ -201,7 +200,7 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
||||
|
||||
this.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else {
|
||||
LOG.log(Level.WARNING, "Failed to resolve auth server: " + url, exception);
|
||||
LOG.warning("Failed to resolve auth server: " + url, exception);
|
||||
lblCreationWarning.setText(resolveFetchExceptionMessage(exception));
|
||||
}
|
||||
}).start();
|
||||
|
||||
@@ -24,27 +24,21 @@ import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.ClassicAccount;
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.ui.construct.RequiredValidator;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class ClassicAccountLoginDialog extends StackPane {
|
||||
@@ -84,20 +78,6 @@ public class ClassicAccountLoginDialog extends StackPane {
|
||||
txtPassword.setPromptText(i18n("account.password"));
|
||||
|
||||
body.getChildren().setAll(usernameLabel, txtPassword);
|
||||
|
||||
if (oldAccount instanceof YggdrasilAccount && !(oldAccount instanceof AuthlibInjectorAccount)) {
|
||||
HBox linkPane = new HBox(8);
|
||||
body.getChildren().add(linkPane);
|
||||
|
||||
JFXHyperlink migrationLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration"));
|
||||
migrationLink.setExternalLink(YggdrasilService.PROFILE_URL);
|
||||
|
||||
JFXHyperlink migrationHowLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration.how"));
|
||||
migrationHowLink.setExternalLink(YggdrasilService.MIGRATION_FAQ_URL);
|
||||
|
||||
linkPane.getChildren().setAll(migrationLink, migrationHowLink);
|
||||
}
|
||||
|
||||
dialogLayout.setBody(body);
|
||||
}
|
||||
|
||||
@@ -128,7 +108,7 @@ public class ClassicAccountLoginDialog extends StackPane {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
progressBar.setVisible(false);
|
||||
}, e -> {
|
||||
LOG.log(Level.INFO, "Failed to login with password: " + oldAccount, e);
|
||||
LOG.info("Failed to login with password: " + oldAccount, e);
|
||||
if (e instanceof NoSelectedCharacterException) {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
} else {
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.jackhuang.hmcl.auth.authlibinjector.BoundAuthlibInjectorAccountFactor
|
||||
import org.jackhuang.hmcl.auth.microsoft.MicrosoftAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||
import org.jackhuang.hmcl.game.OAuthServer;
|
||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||
@@ -312,7 +311,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
birthLink.setExternalLink("https://support.microsoft.com/account-billing/837badbc-999e-54d2-2617-d19206b9540a");
|
||||
JFXHyperlink profileLink = new JFXHyperlink(i18n("account.methods.microsoft.profile"));
|
||||
profileLink.setExternalLink("https://account.live.com/editprof.aspx");
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.microsoft.purchase"));
|
||||
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
|
||||
JFXHyperlink deauthorizeLink = new JFXHyperlink(i18n("account.methods.microsoft.deauthorize"));
|
||||
deauthorizeLink.setExternalLink("https://account.live.com/consent/Edit?client_id=000000004C794E0A");
|
||||
@@ -344,24 +343,6 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
}
|
||||
|
||||
detailsPane = vbox;
|
||||
} else if (factory == Accounts.FACTORY_MOJANG) {
|
||||
VBox vbox = new VBox(8);
|
||||
HintPane hintPane = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
hintPane.setText(i18n("account.methods.yggdrasil.migration.hint"));
|
||||
|
||||
HBox linkPane = new HBox(8);
|
||||
|
||||
JFXHyperlink migrationLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration"));
|
||||
migrationLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.PROFILE_URL));
|
||||
|
||||
JFXHyperlink migrationHowLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration.how"));
|
||||
migrationHowLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.MIGRATION_FAQ_URL));
|
||||
|
||||
linkPane.getChildren().setAll(migrationLink, migrationHowLink);
|
||||
|
||||
vbox.getChildren().setAll(hintPane, linkPane);
|
||||
detailsPane = vbox;
|
||||
btnAccept.setDisable(true);
|
||||
} else {
|
||||
detailsPane = new AccountDetailsInputPane(factory, btnAccept::fire);
|
||||
btnAccept.disableProperty().bind(((AccountDetailsInputPane) detailsPane).validProperty().not());
|
||||
@@ -526,30 +507,11 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
if (factory instanceof YggdrasilAccountFactory) {
|
||||
HBox box = new HBox();
|
||||
GridPane.setColumnSpan(box, 2);
|
||||
|
||||
JFXHyperlink migrationLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration"));
|
||||
migrationLink.setExternalLink("https://aka.ms/MinecraftMigration");
|
||||
|
||||
JFXHyperlink migrationHowLink = new JFXHyperlink(i18n("account.methods.yggdrasil.migration.how"));
|
||||
migrationHowLink.setExternalLink("https://help.minecraft.net/hc/articles/4411173197709");
|
||||
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
|
||||
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
|
||||
|
||||
box.getChildren().setAll(migrationLink, migrationHowLink, purchaseLink);
|
||||
add(box, 0, rowIndex);
|
||||
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
if (factory instanceof OfflineAccountFactory) {
|
||||
txtUsername.setPromptText(i18n("account.methods.offline.name.special_characters"));
|
||||
runInFX(() -> FXUtils.installFastTooltip(txtUsername, i18n("account.methods.offline.name.special_characters")));
|
||||
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.microsoft.purchase"));
|
||||
purchaseLink.setExternalLink(YggdrasilService.PURCHASE_URL);
|
||||
HBox linkPane = new HBox(purchaseLink);
|
||||
GridPane.setColumnSpan(linkPane, 2);
|
||||
@@ -620,9 +582,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
}
|
||||
|
||||
private boolean requiresEmailAsUsername() {
|
||||
if (factory instanceof YggdrasilAccountFactory) {
|
||||
return true;
|
||||
} else if ((factory instanceof AuthlibInjectorAccountFactory) && this.server != null) {
|
||||
if ((factory instanceof AuthlibInjectorAccountFactory) && this.server != null) {
|
||||
return !server.isNonEmailLogin();
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -21,9 +21,8 @@ import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class OAuthAccountLoginDialog extends DialogPane {
|
||||
@@ -70,7 +69,7 @@ public class OAuthAccountLoginDialog extends DialogPane {
|
||||
birthLink.setOnAction(e -> FXUtils.openLink("https://support.microsoft.com/account-billing/how-to-change-a-birth-date-on-a-microsoft-account-837badbc-999e-54d2-2617-d19206b9540a"));
|
||||
JFXHyperlink profileLink = new JFXHyperlink(i18n("account.methods.microsoft.profile"));
|
||||
profileLink.setOnAction(e -> FXUtils.openLink("https://account.live.com/editprof.aspx"));
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.yggdrasil.purchase"));
|
||||
JFXHyperlink purchaseLink = new JFXHyperlink(i18n("account.methods.microsoft.purchase"));
|
||||
purchaseLink.setOnAction(e -> FXUtils.openLink(YggdrasilService.PURCHASE_URL));
|
||||
box.getChildren().setAll(profileLink, birthLink, purchaseLink);
|
||||
GridPane.setColumnSpan(box, 2);
|
||||
@@ -96,7 +95,7 @@ public class OAuthAccountLoginDialog extends DialogPane {
|
||||
success.accept(authInfo);
|
||||
onSuccess();
|
||||
} else {
|
||||
LOG.log(Level.INFO, "Failed to login when credentials expired: " + account, exception);
|
||||
LOG.info("Failed to login when credentials expired: " + account, exception);
|
||||
onFailure(Accounts.localizeErrorMessage(exception));
|
||||
}
|
||||
}).start();
|
||||
|
||||
@@ -25,7 +25,6 @@ import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.scene.layout.*;
|
||||
@@ -35,6 +34,7 @@ import moe.mickey.minecraft.skin.fx.animation.SkinAniWavingArms;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.Skin;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
|
||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
@@ -42,11 +42,11 @@ import org.jackhuang.hmcl.ui.construct.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class OfflineAccountSkinPane extends StackPane {
|
||||
@@ -71,7 +71,7 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
|
||||
BorderPane pane = new BorderPane();
|
||||
|
||||
SkinCanvas canvas = new SkinCanvas(SkinCanvas.STEVE, 300, 300, true);
|
||||
SkinCanvas canvas = new SkinCanvas(TexturesLoader.getDefaultSkinImage(), 300, 300, true);
|
||||
StackPane canvasPane = new StackPane(canvas);
|
||||
canvasPane.setPrefWidth(300);
|
||||
canvasPane.setPrefHeight(300);
|
||||
@@ -116,11 +116,11 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
));
|
||||
|
||||
modelCombobox.setConverter(stringConverter(model -> i18n("account.skin.model." + model.modelName)));
|
||||
modelCombobox.getItems().setAll(TextureModel.STEVE, TextureModel.ALEX);
|
||||
modelCombobox.getItems().setAll(TextureModel.WIDE, TextureModel.SLIM);
|
||||
|
||||
if (account.getSkin() == null) {
|
||||
skinItem.setSelectedData(Skin.Type.DEFAULT);
|
||||
modelCombobox.setValue(TextureModel.STEVE);
|
||||
modelCombobox.setValue(TextureModel.WIDE);
|
||||
} else {
|
||||
skinItem.setSelectedData(account.getSkin().getType());
|
||||
cslApiField.setText(account.getSkin().getCslApi());
|
||||
@@ -133,16 +133,21 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
getSkin().load(account.getUsername())
|
||||
.whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (exception != null) {
|
||||
LOG.log(Level.WARNING, "Failed to load skin", exception);
|
||||
LOG.warning("Failed to load skin", exception);
|
||||
Controllers.showToast(i18n("message.failed"));
|
||||
} else {
|
||||
UUID uuid = this.account.getUUID();
|
||||
if (result == null || result.getSkin() == null && result.getCape() == null) {
|
||||
canvas.updateSkin(getDefaultTexture(), isDefaultSlim(), null);
|
||||
canvas.updateSkin(
|
||||
TexturesLoader.getDefaultSkin(uuid).getImage(),
|
||||
TexturesLoader.getDefaultModel(uuid) == TextureModel.SLIM,
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
canvas.updateSkin(
|
||||
result.getSkin() != null ? result.getSkin().getImage() : getDefaultTexture(),
|
||||
result.getModel() == TextureModel.ALEX,
|
||||
result.getSkin() != null ? result.getSkin().getImage() : TexturesLoader.getDefaultSkin(uuid).getImage(),
|
||||
result.getModel() == TextureModel.SLIM,
|
||||
result.getCape() != null ? result.getCape().getImage() : null);
|
||||
}
|
||||
}).start();
|
||||
@@ -198,17 +203,4 @@ public class OfflineAccountSkinPane extends StackPane {
|
||||
private Skin getSkin() {
|
||||
return new Skin(skinItem.getSelectedData(), cslApiField.getText(), modelCombobox.getValue(), skinSelector.getValue(), capeSelector.getValue());
|
||||
}
|
||||
|
||||
private boolean isDefaultSlim() {
|
||||
return TextureModel.detectUUID(account.getUUID()) == TextureModel.ALEX;
|
||||
}
|
||||
|
||||
private Image getDefaultTexture() {
|
||||
if (isDefaultSlim()) {
|
||||
return SkinCanvas.ALEX;
|
||||
} else {
|
||||
return SkinCanvas.STEVE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,11 +31,11 @@ import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
import org.jackhuang.hmcl.ui.wizard.Navigation;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class Navigator extends TransitionPane {
|
||||
private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Navigator.class.getName() + ".closeListener";
|
||||
@@ -65,7 +65,7 @@ public class Navigator extends TransitionPane {
|
||||
if (from == node)
|
||||
return;
|
||||
|
||||
Logging.LOG.info("Navigate to " + node);
|
||||
LOG.info("Navigate to " + node);
|
||||
|
||||
stack.push(node);
|
||||
backable.set(canGoBack());
|
||||
@@ -104,11 +104,11 @@ public class Navigator extends TransitionPane {
|
||||
|
||||
if (stack.peek() != from) {
|
||||
// Allow page to be closed multiple times.
|
||||
Logging.LOG.log(Level.INFO, "Closing already closed page: " + from, new Throwable());
|
||||
LOG.info("Closing already closed page: " + from, new Throwable());
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LOG.info("Closed page " + from);
|
||||
LOG.info("Closed page " + from);
|
||||
|
||||
Node poppedNode = stack.pop();
|
||||
NavigationEvent exited = new NavigationEvent(this, poppedNode, Navigation.NavigationDirection.PREVIOUS, NavigationEvent.EXITED);
|
||||
|
||||
@@ -19,11 +19,12 @@ package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class StackContainerPane extends StackPane {
|
||||
private final Stack<Node> stack = new Stack<>();
|
||||
|
||||
@@ -39,7 +40,7 @@ public class StackContainerPane extends StackPane {
|
||||
stack.push(node);
|
||||
getChildren().setAll(node);
|
||||
|
||||
Logging.LOG.info(this + " " + stack);
|
||||
LOG.info(this + " " + stack);
|
||||
}
|
||||
|
||||
public void pop(Node node) {
|
||||
@@ -49,7 +50,7 @@ public class StackContainerPane extends StackPane {
|
||||
else
|
||||
getChildren().setAll(stack.peek());
|
||||
|
||||
Logging.LOG.info(this + " " + stack + ", removed: " + flag + ", object: " + node);
|
||||
LOG.info(this + " " + stack + ", removed: " + flag + ", object: " + node);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
@@ -50,6 +50,10 @@ import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -58,15 +62,13 @@ import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.newBuiltinImage;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.io.FileUtils.getExtension;
|
||||
|
||||
public class DecoratorController {
|
||||
@@ -168,8 +170,23 @@ public class DecoratorController {
|
||||
break;
|
||||
case NETWORK:
|
||||
String backgroundImageUrl = config().getBackgroundImageUrl();
|
||||
if (backgroundImageUrl != null && NetworkUtils.isURL(backgroundImageUrl))
|
||||
image = tryLoadImage(backgroundImageUrl).orElse(null);
|
||||
if (backgroundImageUrl != null) {
|
||||
try {
|
||||
URLConnection connection = NetworkUtils.createConnection(new URL(backgroundImageUrl));
|
||||
if (connection instanceof HttpURLConnection) {
|
||||
connection = NetworkUtils.resolveConnection((HttpURLConnection) connection);
|
||||
}
|
||||
|
||||
try (InputStream input = connection.getInputStream()) {
|
||||
image = new Image(input);
|
||||
if (image.isError()) {
|
||||
throw image.getException();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warning("Couldn't load background image", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CLASSIC:
|
||||
image = newBuiltinImage("/assets/img/background-classic.jpg");
|
||||
@@ -215,7 +232,7 @@ public class DecoratorController {
|
||||
})
|
||||
.collect(toList());
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to list files in ./bg", e);
|
||||
LOG.warning("Failed to list files in ./bg", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -244,12 +261,12 @@ public class DecoratorController {
|
||||
try {
|
||||
img = new Image(url);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.log(WARNING, "Couldn't load background image", e);
|
||||
LOG.warning("Couldn't load background image", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (img.getException() != null) {
|
||||
LOG.log(WARNING, "Couldn't load background image", img.getException());
|
||||
LOG.warning("Couldn't load background image", img.getException());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,9 @@ import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class LocalModpackPage extends ModpackPage {
|
||||
@@ -121,7 +120,7 @@ public final class LocalModpackPage extends ModpackPage {
|
||||
|
||||
controller.getSettings().put(MODPACK_MANUALLY_CREATED, true);
|
||||
} else if (exception != null) {
|
||||
LOG.log(Level.WARNING, "Failed to read modpack manifest", exception);
|
||||
LOG.warning("Failed to read modpack manifest", exception);
|
||||
Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
|
||||
Platform.runLater(controller::onEnd);
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.jackhuang.hmcl.download.quilt.QuiltAPIRemoteVersion;
|
||||
import org.jackhuang.hmcl.download.quilt.QuiltRemoteVersion;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.setting.VersionIconType;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||
@@ -51,17 +52,15 @@ import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.ui.wizard.Navigation;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.HMCLService;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.wrap;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.formatDateTime;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -232,7 +231,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LOG.log(Level.WARNING, "Failed to fetch versions list", exception);
|
||||
LOG.warning("Failed to fetch versions list", exception);
|
||||
Platform.runLater(() -> {
|
||||
if (versionList != currentVersionList) return;
|
||||
root.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
|
||||
@@ -263,7 +262,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
||||
private void onBack() { navigation.onPrev(true); }
|
||||
|
||||
private void onSponsor() {
|
||||
HMCLService.openRedirectLink("bmclapi_sponsor");
|
||||
FXUtils.openLink("https://bmclapidoc.bangbang93.com");
|
||||
}
|
||||
|
||||
private static class RemoteVersionListCell extends ListCell<RemoteVersion> {
|
||||
|
||||
@@ -179,7 +179,7 @@ public final class ModpackInfoPage extends Control implements WizardPage {
|
||||
if (skinnable.controller.getSettings().get(MODPACK_TYPE) == MODPACK_TYPE_SERVER) {
|
||||
Hyperlink hyperlink = new Hyperlink(i18n("modpack.wizard.step.initialization.server"));
|
||||
hyperlink.setOnMouseClicked(e -> {
|
||||
FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/server-modpack");
|
||||
FXUtils.openLink("https://docs.hmcl.net/modpack/serverpack.html");
|
||||
});
|
||||
borderPane.setTop(hyperlink);
|
||||
} else {
|
||||
|
||||
@@ -80,6 +80,18 @@ public class AboutPage extends StackPane {
|
||||
zkitefly.setSubtitle(i18n("about.thanks_to.zkitefly.statement"));
|
||||
zkitefly.setExternalLink("https://github.com/zkitefly");
|
||||
|
||||
IconedTwoLineListItem burningtnt = new IconedTwoLineListItem();
|
||||
burningtnt.setImage(FXUtils.newBuiltinImage("/assets/img/burningtnt.png"));
|
||||
burningtnt.setTitle("Burning_TNT");
|
||||
burningtnt.setSubtitle(i18n("about.thanks_to.burningtnt.statement"));
|
||||
burningtnt.setExternalLink("https://github.com/burningtnt");
|
||||
|
||||
IconedTwoLineListItem shulkerSakura = new IconedTwoLineListItem();
|
||||
shulkerSakura.setTitle("ShulkerSakura");
|
||||
shulkerSakura.setImage(FXUtils.newBuiltinImage("/assets/img/ShulkerSakura.png"));
|
||||
shulkerSakura.setSubtitle(i18n("about.thanks_to.shulkersakura.statement"));
|
||||
shulkerSakura.setExternalLink("https://github.com/ShulkerSakura");
|
||||
|
||||
IconedTwoLineListItem gamerteam = new IconedTwoLineListItem();
|
||||
gamerteam.setTitle("gamerteam");
|
||||
gamerteam.setImage(FXUtils.newBuiltinImage("/assets/img/gamerteam.png"));
|
||||
@@ -91,18 +103,17 @@ public class AboutPage extends StackPane {
|
||||
redLnn.setImage(FXUtils.newBuiltinImage("/assets/img/red_lnn.png"));
|
||||
redLnn.setSubtitle(i18n("about.thanks_to.red_lnn.statement"));
|
||||
|
||||
// IconedTwoLineListItem mcbbs = new IconedTwoLineListItem();
|
||||
// mcbbs.setImage(FXUtils.newBuiltinImage("/assets/img/chest.png"));
|
||||
// mcbbs.setTitle(i18n("about.thanks_to.mcbbs"));
|
||||
// mcbbs.setSubtitle(i18n("about.thanks_to.mcbbs.statement"));
|
||||
// mcbbs.setExternalLink("https://www.mcbbs.net/");
|
||||
|
||||
IconedTwoLineListItem mcmod = new IconedTwoLineListItem();
|
||||
mcmod.setImage(FXUtils.newBuiltinImage("/assets/img/mcmod.png"));
|
||||
mcmod.setTitle(i18n("about.thanks_to.mcmod"));
|
||||
mcmod.setSubtitle(i18n("about.thanks_to.mcmod.statement"));
|
||||
mcmod.setExternalLink("https://www.mcmod.cn/");
|
||||
|
||||
IconedTwoLineListItem mcbbs = new IconedTwoLineListItem();
|
||||
mcbbs.setImage(FXUtils.newBuiltinImage("/assets/img/chest.png"));
|
||||
mcbbs.setTitle(i18n("about.thanks_to.mcbbs"));
|
||||
mcbbs.setSubtitle(i18n("about.thanks_to.mcbbs.statement"));
|
||||
|
||||
IconedTwoLineListItem contributors = new IconedTwoLineListItem();
|
||||
contributors.setImage(FXUtils.newBuiltinImage("/assets/img/github.png"));
|
||||
contributors.setTitle(i18n("about.thanks_to.contributors"));
|
||||
@@ -113,9 +124,9 @@ public class AboutPage extends StackPane {
|
||||
users.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
|
||||
users.setTitle(i18n("about.thanks_to.users"));
|
||||
users.setSubtitle(i18n("about.thanks_to.users.statement"));
|
||||
users.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
|
||||
users.setExternalLink("https://docs.hmcl.net/groups.html");
|
||||
|
||||
thanks.getContent().setAll(yushijinhun, bangbang93, glavo, zekerzhayard, zkitefly, /* mcbbs, */ mcmod, gamerteam, redLnn, contributors, users);
|
||||
thanks.getContent().setAll(yushijinhun, bangbang93, glavo, zekerzhayard, zkitefly, burningtnt, mcmod, mcbbs, shulkerSakura, gamerteam, redLnn, contributors, users);
|
||||
}
|
||||
|
||||
ComponentList dep = new ComponentList();
|
||||
@@ -138,7 +149,7 @@ public class AboutPage extends StackPane {
|
||||
IconedTwoLineListItem xz = new IconedTwoLineListItem();
|
||||
xz.setTitle("XZ for Java");
|
||||
xz.setSubtitle("Lasse Collin, Igor Pavlov, and/or Brett Okken.\nPublic Domain.");
|
||||
xz.setExternalLink("https://xz.tukaani.org/xz-for-java/");
|
||||
xz.setExternalLink("https://tukaani.org/xz/java.html");
|
||||
|
||||
IconedTwoLineListItem fxgson = new IconedTwoLineListItem();
|
||||
fxgson.setTitle("fx-gson");
|
||||
|
||||
@@ -42,10 +42,10 @@ public class FeedbackPage extends SpinnerPane {
|
||||
ComponentList community = new ComponentList();
|
||||
{
|
||||
IconedTwoLineListItem users = new IconedTwoLineListItem();
|
||||
users.setImage(FXUtils.newBuiltinImage("/assets/img/craft_table.png"));
|
||||
users.setImage(FXUtils.newBuiltinImage("/assets/img/icon.png"));
|
||||
users.setTitle(i18n("feedback.qq_group"));
|
||||
users.setSubtitle(i18n("feedback.qq_group.statement"));
|
||||
users.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
|
||||
users.setExternalLink("https://docs.hmcl.net/groups.html");
|
||||
|
||||
IconedTwoLineListItem github = new IconedTwoLineListItem();
|
||||
github.setImage(FXUtils.newBuiltinImage("/assets/img/github.png"));
|
||||
|
||||
@@ -46,7 +46,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
private final TabHeader.Tab<HelpPage> helpTab = new TabHeader.Tab<>("helpPage");
|
||||
private final TabHeader.Tab<AboutPage> aboutTab = new TabHeader.Tab<>("aboutPage");
|
||||
private final TabHeader.Tab<FeedbackPage> feedbackTab = new TabHeader.Tab<>("feedbackPage");
|
||||
private final TabHeader.Tab<SponsorPage> sponsorTab = new TabHeader.Tab<>("sponsorPage");
|
||||
private final TransitionPane transitionPane = new TransitionPane();
|
||||
|
||||
public LauncherSettingsPage() {
|
||||
@@ -56,9 +55,8 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
downloadTab.setNodeSupplier(DownloadSettingsPage::new);
|
||||
helpTab.setNodeSupplier(HelpPage::new);
|
||||
feedbackTab.setNodeSupplier(FeedbackPage::new);
|
||||
sponsorTab.setNodeSupplier(SponsorPage::new);
|
||||
aboutTab.setNodeSupplier(AboutPage::new);
|
||||
tab = new TabHeader(gameTab, settingsTab, personalizationTab, downloadTab, helpTab, feedbackTab, sponsorTab, aboutTab);
|
||||
tab = new TabHeader(gameTab, settingsTab, personalizationTab, downloadTab, helpTab, feedbackTab, aboutTab);
|
||||
|
||||
tab.select(gameTab);
|
||||
gameTab.initializeIfNeeded();
|
||||
@@ -108,12 +106,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor
|
||||
feedbackItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(feedbackTab));
|
||||
feedbackItem.setOnAction(e -> tab.select(feedbackTab));
|
||||
})
|
||||
.addNavigationDrawerItem(sponsorItem -> {
|
||||
sponsorItem.setTitle(i18n("sponsor"));
|
||||
sponsorItem.setLeftGraphic(wrap(SVG.HAND_HEAR_OUTLINE));
|
||||
sponsorItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(sponsorTab));
|
||||
sponsorItem.setOnAction(e -> tab.select(sponsorTab));
|
||||
})
|
||||
.addNavigationDrawerItem(aboutItem -> {
|
||||
aboutItem.setTitle(i18n("about"));
|
||||
aboutItem.setLeftGraphic(wrap(SVG.INFORMATION_OUTLINE));
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.account.AccountAdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListItem;
|
||||
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
@@ -55,12 +54,11 @@ import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
@@ -103,7 +101,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
try {
|
||||
Controllers.navigate(new NBTEditorPage(file));
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Fail to open nbt file", e);
|
||||
LOG.warning("Fail to open nbt file", e);
|
||||
Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e),
|
||||
i18n("message.error"), MessageDialogPane.MessageType.ERROR);
|
||||
}
|
||||
@@ -170,25 +168,19 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
runInFX(() -> FXUtils.installFastTooltip(downloadItem, i18n("download.hint")));
|
||||
|
||||
// fifth item in left sidebar
|
||||
AdvancedListItem multiplayerItem = new AdvancedListItem();
|
||||
multiplayerItem.setLeftGraphic(wrap(SVG.LAN));
|
||||
multiplayerItem.setActionButtonVisible(false);
|
||||
multiplayerItem.setTitle(i18n("multiplayer"));
|
||||
JFXHyperlink link = new JFXHyperlink(i18n("multiplayer.hint.details"));
|
||||
link.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/multiplayer-migrate");
|
||||
multiplayerItem.setOnAction(e -> Controllers.dialog(
|
||||
new MessageDialogPane.Builder(i18n("multiplayer.hint"), null, MessageDialogPane.MessageType.INFO)
|
||||
.addAction(link)
|
||||
.ok(null)
|
||||
.build()));
|
||||
|
||||
// sixth item in left sidebar
|
||||
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
|
||||
launcherSettingsItem.setLeftGraphic(wrap(SVG.GEAR_OUTLINE));
|
||||
launcherSettingsItem.setActionButtonVisible(false);
|
||||
launcherSettingsItem.setTitle(i18n("settings"));
|
||||
launcherSettingsItem.setOnAction(e -> Controllers.navigate(Controllers.getSettingsPage()));
|
||||
|
||||
// sixth item in left sidebar
|
||||
AdvancedListItem chatItem = new AdvancedListItem();
|
||||
chatItem.setLeftGraphic(wrap(SVG.CHAT));
|
||||
chatItem.setActionButtonVisible(false);
|
||||
chatItem.setTitle(i18n("chat"));
|
||||
chatItem.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/groups.html"));
|
||||
|
||||
// the left sidebar
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.startCategory(i18n("account").toUpperCase(Locale.ROOT))
|
||||
@@ -198,8 +190,9 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
|
||||
.add(gameItem)
|
||||
.add(downloadItem)
|
||||
.startCategory(i18n("settings.launcher.general").toUpperCase(Locale.ROOT))
|
||||
.add(multiplayerItem)
|
||||
.add(launcherSettingsItem);
|
||||
.add(launcherSettingsItem)
|
||||
.add(chatItem)
|
||||
;
|
||||
|
||||
// the root page, with the sidebar in left, navigator in center.
|
||||
setLeft(sideBar);
|
||||
|
||||
@@ -31,23 +31,23 @@ import org.jackhuang.hmcl.upgrade.RemoteVersion;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChannel;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateHandler;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor;
|
||||
|
||||
@@ -129,11 +129,11 @@ public final class SettingsPage extends SettingsView {
|
||||
Path logFile = Paths.get("hmcl-exported-logs-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".log").toAbsolutePath();
|
||||
|
||||
LOG.info("Exporting logs to " + logFile);
|
||||
try {
|
||||
Files.write(logFile, Logging.getRawLogs());
|
||||
try (OutputStream output = Files.newOutputStream(logFile)) {
|
||||
LOG.exportLogs(output);
|
||||
} catch (IOException e) {
|
||||
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + e, null, MessageType.ERROR));
|
||||
LOG.log(Level.WARNING, "Failed to export logs", e);
|
||||
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + StringUtils.getStackTrace(e), null, MessageType.ERROR));
|
||||
LOG.warning("Failed to export logs", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public final class SettingsPage extends SettingsView {
|
||||
|
||||
@Override
|
||||
protected void onSponsor() {
|
||||
FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
|
||||
FXUtils.openLink("https://github.com/HMCL-dev/HMCL");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentSublist;
|
||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -176,8 +177,8 @@ public abstract class SettingsView extends StackPane {
|
||||
languagePane.setLeft(left);
|
||||
|
||||
cboLanguage = new JFXComboBox<>();
|
||||
cboLanguage.setConverter(stringConverter(locale -> locale.getName(config().getLocalization().getResourceBundle())));
|
||||
FXUtils.setLimitWidth(cboLanguage, 400);
|
||||
cboLanguage.setConverter(stringConverter(I18n::getName));
|
||||
FXUtils.setLimitWidth(cboLanguage, 300);
|
||||
languagePane.setRight(cboLanguage);
|
||||
|
||||
settingsPane.getContent().add(languagePane);
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.main;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.jfoenix.controls.JFXListCell;
|
||||
import com.jfoenix.controls.JFXListView;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.Holder;
|
||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class SponsorPage extends StackPane {
|
||||
private final JFXListView<Sponsor> listView;
|
||||
|
||||
public SponsorPage() {
|
||||
VBox content = new VBox();
|
||||
content.setPadding(new Insets(10));
|
||||
content.setSpacing(10);
|
||||
content.setFillWidth(true);
|
||||
|
||||
{
|
||||
StackPane sponsorPane = new StackPane();
|
||||
sponsorPane.getStyleClass().add("card");
|
||||
sponsorPane.setCursor(Cursor.HAND);
|
||||
sponsorPane.setOnMouseClicked(e -> onSponsor());
|
||||
|
||||
GridPane gridPane = new GridPane();
|
||||
|
||||
ColumnConstraints col = new ColumnConstraints();
|
||||
col.setHgrow(Priority.SOMETIMES);
|
||||
col.setMaxWidth(Double.POSITIVE_INFINITY);
|
||||
|
||||
gridPane.getColumnConstraints().setAll(col);
|
||||
|
||||
RowConstraints row = new RowConstraints();
|
||||
row.setMinHeight(Double.NEGATIVE_INFINITY);
|
||||
row.setValignment(VPos.TOP);
|
||||
row.setVgrow(Priority.SOMETIMES);
|
||||
gridPane.getRowConstraints().setAll(row);
|
||||
|
||||
{
|
||||
Label label = new Label(i18n("sponsor.hmcl"));
|
||||
label.setWrapText(true);
|
||||
label.setTextAlignment(TextAlignment.JUSTIFY);
|
||||
GridPane.setRowIndex(label, 0);
|
||||
GridPane.setColumnIndex(label, 0);
|
||||
gridPane.getChildren().add(label);
|
||||
}
|
||||
|
||||
sponsorPane.getChildren().setAll(gridPane);
|
||||
content.getChildren().add(sponsorPane);
|
||||
}
|
||||
|
||||
{
|
||||
StackPane pane = new StackPane();
|
||||
pane.getStyleClass().add("card");
|
||||
listView = new JFXListView<>();
|
||||
Holder<Object> lastCell = new Holder<>();
|
||||
listView.setCellFactory((listView) -> new JFXListCell<Sponsor>() {
|
||||
@Override
|
||||
public void updateItem(Sponsor item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
// https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
|
||||
if (this == lastCell.value && !isVisible())
|
||||
return;
|
||||
lastCell.value = this;
|
||||
|
||||
if (!empty) {
|
||||
setText(item.getName());
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
VBox.setVgrow(pane, Priority.ALWAYS);
|
||||
pane.getChildren().setAll(listView);
|
||||
content.getChildren().add(pane);
|
||||
}
|
||||
|
||||
loadSponsorList();
|
||||
|
||||
getChildren().setAll(content);
|
||||
}
|
||||
|
||||
private void onSponsor() {
|
||||
FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
|
||||
}
|
||||
|
||||
private void loadSponsorList() {
|
||||
Task.<List<Sponsor>>supplyAsync(() -> HttpRequest.GET("https://hmcl.huangyuhui.net/api/sponsor").getJson(new TypeToken<List<Sponsor>>() {
|
||||
}.getType())).thenAcceptAsync(Schedulers.javafx(), sponsors -> {
|
||||
listView.getItems().setAll(sponsors);
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static class Sponsor {
|
||||
@SerializedName("name")
|
||||
private final String name;
|
||||
|
||||
@SerializedName("create_time")
|
||||
private final Instant createTime;
|
||||
|
||||
@SerializedName("money")
|
||||
private final BigDecimal money;
|
||||
|
||||
@SerializedName("contact")
|
||||
private final String contact;
|
||||
|
||||
@SerializedName("afdian_id")
|
||||
private final String afdianId;
|
||||
|
||||
public Sponsor() {
|
||||
this("", Instant.now(), BigDecimal.ZERO, "", "");
|
||||
}
|
||||
|
||||
public Sponsor(String name, Instant createTime, BigDecimal money, String contact, String afdianId) {
|
||||
this.name = name;
|
||||
this.createTime = createTime;
|
||||
this.money = money;
|
||||
this.contact = contact;
|
||||
this.afdianId = afdianId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Instant getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public BigDecimal getMoney() {
|
||||
return money;
|
||||
}
|
||||
|
||||
public String getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public String getAfdianId() {
|
||||
return afdianId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,9 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class NBTEditorPage extends BorderPane implements DecoratorPage {
|
||||
@@ -53,7 +52,7 @@ public class NBTEditorPage extends BorderPane implements DecoratorPage {
|
||||
try {
|
||||
save();
|
||||
} catch (IOException ex) {
|
||||
LOG.log(Level.WARNING, "Failed to save NBT file", ex);
|
||||
LOG.warning("Failed to save NBT file", ex);
|
||||
Controllers.dialog(i18n("nbt.save.failed") + "\n\n" + StringUtils.getStackTrace(ex));
|
||||
}
|
||||
});
|
||||
@@ -73,7 +72,7 @@ public class NBTEditorPage extends BorderPane implements DecoratorPage {
|
||||
}, Schedulers.javafx())
|
||||
.handleAsync((result, e) -> {
|
||||
if (e != null) {
|
||||
LOG.log(Level.WARNING, "Fail to open nbt file", e);
|
||||
LOG.warning("Fail to open nbt file", e);
|
||||
Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e), null, MessageDialogPane.MessageType.WARNING, cancelButton::fire);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.ListPageBase;
|
||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
|
||||
|
||||
@@ -38,8 +37,8 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class DatapackListPage extends ListPageBase<DatapackListPageSkin.DatapackInfoObject> implements DecoratorPage {
|
||||
@@ -71,7 +70,7 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
|
||||
zip.loadFromZip();
|
||||
zip.installTo(worldDir);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to parse datapack file " + datapack, e);
|
||||
LOG.warning("Unable to parse datapack file " + datapack, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +116,7 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
|
||||
datapack.deletePack(pack);
|
||||
} catch (IOException e) {
|
||||
// Fail to remove mods if the game is running or the datapack is absent.
|
||||
Logging.LOG.warning("Failed to delete datapack " + pack);
|
||||
LOG.warning("Failed to delete datapack " + pack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
|
||||
protected void updateControl(RemoteMod dataItem, boolean empty) {
|
||||
if (empty) return;
|
||||
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(getSkinnable().repository.getType()).getModByCurseForgeId(dataItem.getSlug());
|
||||
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : dataItem.getTitle());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : dataItem.getTitle());
|
||||
content.setSubtitle(dataItem.getDescription());
|
||||
content.getTags().setAll(dataItem.getCategories().stream()
|
||||
.map(category -> getSkinnable().getLocalizedCategory(category))
|
||||
|
||||
@@ -240,7 +240,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
HBox.setHgrow(content, Priority.ALWAYS);
|
||||
ModTranslations.Mod mod = getSkinnable().translations.getModByCurseForgeId(getSkinnable().addon.getSlug());
|
||||
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : getSkinnable().addon.getTitle());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : getSkinnable().addon.getTitle());
|
||||
content.setSubtitle(getSkinnable().addon.getDescription());
|
||||
content.getTags().setAll(getSkinnable().addon.getCategories().stream()
|
||||
.map(category -> getSkinnable().page.getLocalizedCategory(category))
|
||||
@@ -253,14 +253,6 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
descriptionPane.getChildren().add(openMcmodButton);
|
||||
openMcmodButton.setMinWidth(Region.USE_PREF_SIZE);
|
||||
runInFX(() -> FXUtils.installFastTooltip(openMcmodButton, i18n("mods.mcmod")));
|
||||
|
||||
// if (StringUtils.isNotBlank(getSkinnable().mod.getMcbbs())) {
|
||||
// JFXHyperlink openMcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
|
||||
// openMcbbsButton.setExternalLink(ModManager.getMcbbsUrl(getSkinnable().mod.getMcbbs()));
|
||||
// descriptionPane.getChildren().add(openMcbbsButton);
|
||||
// openMcbbsButton.setMinWidth(Region.USE_PREF_SIZE);
|
||||
// runInFX(() -> FXUtils.installFastTooltip(openMcbbsButton, i18n("mods.mcbbs")));
|
||||
// }
|
||||
}
|
||||
|
||||
JFXHyperlink openUrlButton = new JFXHyperlink(control.page.getLocalizedOfficialPage());
|
||||
@@ -352,15 +344,21 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
container.setOnMouseClicked(e -> Controllers.navigate(new DownloadPage(page, addon, version, callback)));
|
||||
getChildren().setAll(container);
|
||||
|
||||
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug());
|
||||
content.setTitle(mod != null && I18n.getCurrentLocale().getLocale() == Locale.CHINA ? mod.getDisplayName() : addon.getTitle());
|
||||
content.setSubtitle(addon.getDescription());
|
||||
content.getTags().setAll(addon.getCategories().stream()
|
||||
.map(page::getLocalizedCategory)
|
||||
.collect(Collectors.toList()));
|
||||
if (addon != RemoteMod.BROKEN) {
|
||||
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(page.repository.getType()).getModByCurseForgeId(addon.getSlug());
|
||||
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.getTitle());
|
||||
content.setSubtitle(addon.getDescription());
|
||||
content.getTags().setAll(addon.getCategories().stream()
|
||||
.map(page::getLocalizedCategory)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
if (StringUtils.isNotBlank(addon.getIconUrl())) {
|
||||
imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true));
|
||||
if (StringUtils.isNotBlank(addon.getIconUrl())) {
|
||||
imageView.setImage(FXUtils.newRemoteImage(addon.getIconUrl(), 40, 40, true, true, true));
|
||||
}
|
||||
} else {
|
||||
content.setTitle(i18n("mods.broken_dependency.title"));
|
||||
content.setSubtitle(i18n("mods.broken_dependency.desc"));
|
||||
imageView.setImage(FXUtils.newBuiltinImage("/assets/img/icon@8x.png", 40, 40, true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,11 @@ import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT;
|
||||
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
|
||||
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class GameItem extends Control {
|
||||
@@ -84,7 +83,7 @@ public class GameItem extends Control {
|
||||
if (config == null) return;
|
||||
tag.set(config.getVersion());
|
||||
} catch (IOException | JsonParseException e) {
|
||||
LOG.log(Level.WARNING, "Failed to read modpack configuration from " + version, e);
|
||||
LOG.warning("Failed to read modpack configuration from " + version, e);
|
||||
}
|
||||
}, Platform::runLater)
|
||||
.exceptionally(handleUncaught);
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.ListPageBase;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.PageAware;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
@@ -45,10 +44,10 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> implements VersionPage.VersionLoadable, PageAware {
|
||||
@@ -65,7 +64,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
try {
|
||||
modManager.addMod(it.toPath());
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to parse mod file " + it, e);
|
||||
LOG.warning("Unable to parse mod file " + it, e);
|
||||
}
|
||||
});
|
||||
loadMods(modManager);
|
||||
@@ -135,7 +134,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
||||
modManager.addMod(file.toPath());
|
||||
succeeded.add(file.getName());
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to add mod " + file, e);
|
||||
LOG.warning("Unable to add mod " + file, e);
|
||||
failed.add(file.getName());
|
||||
|
||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||
|
||||
@@ -61,14 +61,13 @@ import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.StringUtils.isNotBlank;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -218,7 +217,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
Pattern pattern = Pattern.compile(queryString.substring("regex:".length()));
|
||||
predicate = s -> pattern.matcher(s).find();
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Illegal regular expression", e);
|
||||
LOG.warning("Illegal regular expression", e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -391,15 +390,6 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
getActions().add(officialPageButton);
|
||||
}
|
||||
|
||||
// if (modInfo.getMod() != null && StringUtils.isNotBlank(modInfo.getMod().getMcbbs())) {
|
||||
// JFXHyperlink mcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
|
||||
// mcbbsButton.setOnAction(e -> {
|
||||
// fireEvent(new DialogCloseEvent());
|
||||
// FXUtils.openLink(ModManager.getMcbbsUrl(modInfo.getMod().getMcbbs()));
|
||||
// });
|
||||
// getActions().add(mcbbsButton);
|
||||
// }
|
||||
|
||||
if (modInfo.getMod() == null || StringUtils.isBlank(modInfo.getMod().getMcmod())) {
|
||||
JFXHyperlink searchButton = new JFXHyperlink(i18n("mods.mcmod.search"));
|
||||
searchButton.setOnAction(e -> {
|
||||
@@ -490,10 +480,8 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
||||
content.getTags().add(i18n("install.installer.quilt"));
|
||||
break;
|
||||
}
|
||||
if (dataItem.getMod() != null) {
|
||||
if (I18n.getCurrentLocale().getLocale() == Locale.CHINA) {
|
||||
content.getTags().add(dataItem.getMod().getDisplayName());
|
||||
}
|
||||
if (dataItem.getMod() != null && I18n.isUseChinese()) {
|
||||
content.getTags().add(dataItem.getMod().getDisplayName());
|
||||
}
|
||||
content.setSubtitle(dataItem.getSubtitle());
|
||||
if (booleanProperty != null) {
|
||||
|
||||
@@ -24,10 +24,9 @@ import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
/**
|
||||
@@ -127,7 +126,7 @@ public enum ModTranslations {
|
||||
mods = Arrays.stream(modData.split("\n")).filter(line -> !line.startsWith("#")).map(Mod::new).collect(Collectors.toList());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.WARNING, "Failed to load " + resourceName, e);
|
||||
LOG.warning("Failed to load " + resourceName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -198,10 +197,9 @@ public enum ModTranslations {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class Mod {
|
||||
public static final class Mod {
|
||||
private final String curseforge;
|
||||
private final String mcmod;
|
||||
private final String mcbbs;
|
||||
private final List<String> modIds;
|
||||
private final String name;
|
||||
private final String subname;
|
||||
@@ -209,23 +207,21 @@ public enum ModTranslations {
|
||||
|
||||
public Mod(String line) {
|
||||
String[] items = line.split(";", -1);
|
||||
if (items.length != 7) {
|
||||
throw new IllegalArgumentException("Illegal mod data line, 7 items expected " + line);
|
||||
if (items.length != 6) {
|
||||
throw new IllegalArgumentException("Illegal mod data line, 6 items expected " + line);
|
||||
}
|
||||
|
||||
curseforge = items[0];
|
||||
mcmod = items[1];
|
||||
mcbbs = items[2];
|
||||
modIds = Collections.unmodifiableList(Arrays.asList(items[3].split(",")));
|
||||
name = items[4];
|
||||
subname = items[5];
|
||||
abbr = items[6];
|
||||
modIds = Collections.unmodifiableList(Arrays.asList(items[2].split(",")));
|
||||
name = items[3];
|
||||
subname = items[4];
|
||||
abbr = items[5];
|
||||
}
|
||||
|
||||
public Mod(String curseforge, String mcmod, String mcbbs, List<String> modIds, String name, String subname, String abbr) {
|
||||
public Mod(String curseforge, String mcmod, List<String> modIds, String name, String subname, String abbr) {
|
||||
this.curseforge = curseforge;
|
||||
this.mcmod = mcmod;
|
||||
this.mcbbs = mcbbs;
|
||||
this.modIds = modIds;
|
||||
this.name = name;
|
||||
this.subname = subname;
|
||||
@@ -252,10 +248,6 @@ public enum ModTranslations {
|
||||
return mcmod;
|
||||
}
|
||||
|
||||
public String getMcbbs() {
|
||||
return mcbbs;
|
||||
}
|
||||
|
||||
public List<String> getModIds() {
|
||||
return modIds;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,11 @@ import org.jackhuang.hmcl.game.LocalizedRemoteModRepository;
|
||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
||||
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
|
||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class ResourcePackDownloadListPage extends DownloadListPage {
|
||||
@@ -66,10 +70,18 @@ public class ResourcePackDownloadListPage extends DownloadListPage {
|
||||
|
||||
@Override
|
||||
protected String getLocalizedCategory(String category) {
|
||||
String key;
|
||||
if ("mods.modrinth".equals(downloadSource.get())) {
|
||||
return i18n("modrinth.category." + category);
|
||||
key = "modrinth.category." + category;
|
||||
} else {
|
||||
return i18n("curse.category." + category);
|
||||
key = "curse.category." + category;
|
||||
}
|
||||
|
||||
try {
|
||||
return I18n.getResourceBundle().getString(key);
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.warning("Cannot find key " + key + " in resource bundle", e);
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,11 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class VersionIconDialog extends DialogPane {
|
||||
@@ -74,20 +72,19 @@ public class VersionIconDialog extends DialogPane {
|
||||
|
||||
private void exploreIcon() {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.png"), "*.png"));
|
||||
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.png"), "*.png", "*.jpg", "*.bmp", "*.gif"));
|
||||
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
|
||||
if (selectedFile != null) {
|
||||
File iconFile = profile.getRepository().getVersionIconFile(versionId);
|
||||
try {
|
||||
FileUtils.copyFile(selectedFile, iconFile);
|
||||
profile.getRepository().setVersionIconFile(versionId, selectedFile);
|
||||
|
||||
if (vs != null) {
|
||||
vs.setVersionIcon(VersionIconType.DEFAULT);
|
||||
}
|
||||
|
||||
onAccept();
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to copy icon file from " + selectedFile + " to " + iconFile, e);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
LOG.error("Failed to set icon file: " + selectedFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,9 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
}
|
||||
|
||||
private void clearAssets() {
|
||||
FileUtils.deleteDirectoryQuietly(new File(getProfile().getRepository().getBaseDirectory(), "assets"));
|
||||
File baseDirectory = getProfile().getRepository().getBaseDirectory();
|
||||
FileUtils.deleteDirectoryQuietly(new File(baseDirectory, "assets"));
|
||||
FileUtils.deleteDirectoryQuietly(new File(baseDirectory, "resources"));
|
||||
}
|
||||
|
||||
private void clearJunkFiles() {
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
@@ -642,9 +641,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
||||
if (versionId == null)
|
||||
return;
|
||||
|
||||
File iconFile = profile.getRepository().getVersionIconFile(versionId);
|
||||
if (iconFile.exists())
|
||||
iconFile.delete();
|
||||
profile.getRepository().deleteIconFile(versionId);
|
||||
VersionSetting localVersionSetting = profile.getRepository().getLocalVersionSettingOrCreate(versionId);
|
||||
if (localVersionSetting != null) {
|
||||
localVersionSetting.setVersionIcon(VersionIconType.DEFAULT);
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
||||
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
@@ -55,8 +54,8 @@ import java.nio.file.Path;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class Versions {
|
||||
@@ -84,7 +83,7 @@ public final class Versions {
|
||||
} catch (IOException e) {
|
||||
Controllers.dialog(
|
||||
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
||||
i18n("download.failed"), MessageDialogPane.MessageType.ERROR);
|
||||
i18n("download.failed.no_code", file.getFile().getUrl()), MessageDialogPane.MessageType.ERROR);
|
||||
return;
|
||||
}
|
||||
Controllers.taskDialog(
|
||||
@@ -97,7 +96,7 @@ public final class Versions {
|
||||
} else {
|
||||
Controllers.dialog(
|
||||
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
||||
i18n("download.failed"), MessageDialogPane.MessageType.ERROR);
|
||||
i18n("download.failed.no_code", file.getFile().getUrl()), MessageDialogPane.MessageType.ERROR);
|
||||
}
|
||||
}).executor(true),
|
||||
i18n("message.downloading"),
|
||||
@@ -184,7 +183,7 @@ public final class Versions {
|
||||
try {
|
||||
profile.getRepository().clean(id);
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to clean game directory", e);
|
||||
LOG.warning("Unable to clean game directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@ import java.text.DecimalFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.formatDateTime;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -421,7 +420,7 @@ public final class WorldInfoPage extends StackPane implements DecoratorPage {
|
||||
try {
|
||||
this.world.writeLevelDat(levelDat);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to save level.dat of world " + world.getWorldName(), e);
|
||||
LOG.warning("Failed to save level.dat of world " + world.getWorldName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.*;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -39,10 +38,10 @@ import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class WorldListPage extends ListPageBase<WorldListItem> implements VersionPage.VersionLoadable {
|
||||
@@ -139,7 +138,7 @@ public class WorldListPage extends ListPageBase<WorldListItem> implements Versio
|
||||
}).start();
|
||||
}, world.getWorldName());
|
||||
}, e -> {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to parse world file " + zipFile, e);
|
||||
LOG.warning("Unable to parse world file " + zipFile, e);
|
||||
Controllers.dialog(i18n("world.import.invalid"));
|
||||
}).start();
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public class WizardController implements Navigation {
|
||||
private final WizardDisplayer displayer;
|
||||
private WizardProvider provider = null;
|
||||
@@ -71,7 +72,7 @@ public class WizardController implements Navigation {
|
||||
|
||||
displayer.onStart();
|
||||
|
||||
Logging.LOG.info("Navigating to " + page + ", pages: " + pages);
|
||||
LOG.info("Navigating to " + page + ", pages: " + pages);
|
||||
displayer.navigateTo(page, NavigationDirection.START);
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ public class WizardController implements Navigation {
|
||||
if (page instanceof WizardPage)
|
||||
((WizardPage) page).onNavigate(settings);
|
||||
|
||||
Logging.LOG.info("Navigating to " + page + ", pages: " + pages);
|
||||
LOG.info("Navigating to " + page + ", pages: " + pages);
|
||||
displayer.navigateTo(page, NavigationDirection.NEXT);
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ public class WizardController implements Navigation {
|
||||
if (prevPage instanceof WizardPage)
|
||||
((WizardPage) prevPage).onNavigate(settings);
|
||||
|
||||
Logging.LOG.info("Navigating to " + prevPage + ", pages: " + pages);
|
||||
LOG.info("Navigating to " + prevPage + ", pages: " + pages);
|
||||
displayer.navigateTo(prevPage, NavigationDirection.PREVIOUS);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,11 @@ import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* A class that checks the integrity of HMCL.
|
||||
@@ -132,7 +131,7 @@ public final class IntegrityChecker {
|
||||
LOG.info("Successfully verified current JAR");
|
||||
selfVerified = true;
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to verify myself, is the JAR corrupt?", e);
|
||||
LOG.warning("Failed to verify myself, is the JAR corrupt?", e);
|
||||
selfVerified = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +38,11 @@ public class RemoteVersion {
|
||||
String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null);
|
||||
String packXZUrl = Optional.ofNullable(response.get("packxz")).map(JsonElement::getAsString).orElse(null);
|
||||
String packXZHash = Optional.ofNullable(response.get("packxzsha1")).map(JsonElement::getAsString).orElse(null);
|
||||
boolean force = Optional.ofNullable(response.get("force")).map(JsonElement::getAsBoolean).orElse(false);
|
||||
if (Pack200Utils.isSupported() && packXZUrl != null && packXZHash != null) {
|
||||
return new RemoteVersion(channel, version, packXZUrl, Type.PACK_XZ, new IntegrityCheck("SHA-1", packXZHash));
|
||||
return new RemoteVersion(channel, version, packXZUrl, Type.PACK_XZ, new IntegrityCheck("SHA-1", packXZHash), force);
|
||||
} else if (jarUrl != null && jarHash != null) {
|
||||
return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash));
|
||||
return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), force);
|
||||
} else {
|
||||
throw new IOException("No download url is available");
|
||||
}
|
||||
@@ -55,13 +56,15 @@ public class RemoteVersion {
|
||||
private final String url;
|
||||
private final Type type;
|
||||
private final IntegrityCheck integrityCheck;
|
||||
private final boolean force;
|
||||
|
||||
public RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck) {
|
||||
public RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck, boolean force) {
|
||||
this.channel = channel;
|
||||
this.version = version;
|
||||
this.url = url;
|
||||
this.type = type;
|
||||
this.integrityCheck = integrityCheck;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
public UpdateChannel getChannel() {
|
||||
@@ -84,6 +87,10 @@ public class RemoteVersion {
|
||||
return integrityCheck;
|
||||
}
|
||||
|
||||
public boolean isForce() {
|
||||
return force;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + version + " from " + url + "]";
|
||||
|
||||
@@ -24,13 +24,13 @@ import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import org.jackhuang.hmcl.Metadata;
|
||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.VersionNumber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
public final class UpdateChecker {
|
||||
@@ -42,10 +42,13 @@ public final class UpdateChecker {
|
||||
RemoteVersion latest = latestVersion.get();
|
||||
if (latest == null || isDevelopmentVersion(Metadata.VERSION)) {
|
||||
return false;
|
||||
} else {
|
||||
// We can update from development version to stable version,
|
||||
// which can be downgrading.
|
||||
} else if (latest.isForce()
|
||||
|| Metadata.isNightly()
|
||||
|| latest.getChannel() == UpdateChannel.NIGHTLY
|
||||
|| latest.getChannel() != UpdateChannel.getChannel()) {
|
||||
return !latest.getVersion().equals(Metadata.VERSION);
|
||||
} else {
|
||||
return VersionNumber.compare(Metadata.VERSION, latest.getVersion()) < 0;
|
||||
}
|
||||
},
|
||||
latestVersion);
|
||||
@@ -108,7 +111,7 @@ public final class UpdateChecker {
|
||||
result = checkUpdate(channel);
|
||||
LOG.info("Latest version (" + channel + ") is " + result);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to check for update", e);
|
||||
LOG.warning("Failed to check for update", e);
|
||||
}
|
||||
|
||||
RemoteVersion finalResult = result;
|
||||
|
||||
@@ -42,13 +42,12 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.checkFxUserThread;
|
||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public final class UpdateHandler {
|
||||
@@ -66,7 +65,7 @@ public final class UpdateHandler {
|
||||
try {
|
||||
performMigration();
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to perform migration", e);
|
||||
LOG.warning("Failed to perform migration", e);
|
||||
SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e));
|
||||
}
|
||||
return true;
|
||||
@@ -81,7 +80,7 @@ public final class UpdateHandler {
|
||||
try {
|
||||
applyUpdate(Paths.get(args[1]));
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to apply update", e);
|
||||
LOG.warning("Failed to apply update", e);
|
||||
SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e));
|
||||
}
|
||||
return true;
|
||||
@@ -108,7 +107,7 @@ public final class UpdateHandler {
|
||||
try {
|
||||
downloaded = Files.createTempFile("hmcl-update-", ".jar");
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to create temp file", e);
|
||||
LOG.warning("Failed to create temp file", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,15 +126,15 @@ public final class UpdateHandler {
|
||||
}
|
||||
|
||||
requestUpdate(downloaded, getCurrentLocation());
|
||||
System.exit(0);
|
||||
Main.exit(0);
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to update to " + version, e);
|
||||
LOG.warning("Failed to update to " + version, e);
|
||||
Platform.runLater(() -> Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageType.ERROR));
|
||||
}
|
||||
|
||||
} else {
|
||||
Exception e = executor.getException();
|
||||
LOG.log(Level.WARNING, "Failed to update to " + version, e);
|
||||
LOG.warning("Failed to update to " + version, e);
|
||||
if (e instanceof CancellationException) {
|
||||
Platform.runLater(() -> Controllers.showToast(i18n("message.cancelled")));
|
||||
} else {
|
||||
@@ -162,7 +161,7 @@ public final class UpdateHandler {
|
||||
Files.move(target, newFilename.get());
|
||||
target = newFilename.get();
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to move target", e);
|
||||
LOG.warning("Failed to move target", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +272,7 @@ public final class UpdateHandler {
|
||||
LOG.info("Successfully broke the force update feature");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to break the force update feature", e);
|
||||
LOG.warning("Failed to break the force update feature", e);
|
||||
} catch (JsonParseException e) {
|
||||
hmclVersionJson.toFile().delete();
|
||||
}
|
||||
|
||||
@@ -29,9 +29,8 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
@@ -67,14 +66,14 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
if (s.contains(entry.getKey())) {
|
||||
if (StringUtils.isNotBlank(entry.getValue())) {
|
||||
String info = entry.getValue();
|
||||
LOG.severe(info);
|
||||
LOG.error(info);
|
||||
try {
|
||||
Alert alert = new Alert(AlertType.INFORMATION, info);
|
||||
alert.setTitle(i18n("message.info"));
|
||||
alert.setHeaderText(i18n("message.info"));
|
||||
alert.showAndWait();
|
||||
} catch (Throwable t) {
|
||||
LOG.log(Level.SEVERE, "Unable to show message", t);
|
||||
LOG.error("Unable to show message", t);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -90,14 +89,14 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
LOG.log(Level.SEVERE, "Uncaught exception in thread " + t.getName(), e);
|
||||
LOG.error("Uncaught exception in thread " + t.getName(), e);
|
||||
|
||||
try {
|
||||
CrashReport report = new CrashReport(t, e);
|
||||
if (!report.shouldBeReport())
|
||||
return;
|
||||
|
||||
LOG.log(Level.SEVERE, report.getDisplayText());
|
||||
LOG.error(report.getDisplayText());
|
||||
Platform.runLater(() -> {
|
||||
if (checkThrowable(e)) {
|
||||
if (showCrashWindow) {
|
||||
@@ -109,8 +108,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
}
|
||||
});
|
||||
} catch (Throwable handlingException) {
|
||||
LOG.log(Level.SEVERE, "Unable to handle uncaught exception", handlingException);
|
||||
LOG.error("Unable to handle uncaught exception", handlingException);
|
||||
}
|
||||
|
||||
LOG.shutdown();
|
||||
}
|
||||
|
||||
private void reportToServer(CrashReport crashReport) {
|
||||
@@ -118,13 +119,13 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("crash_report", crashReport.getDisplayText());
|
||||
map.put("version", Metadata.VERSION);
|
||||
map.put("log", Logging.getLogs());
|
||||
map.put("log", LOG.getLogs());
|
||||
try {
|
||||
String response = NetworkUtils.doPost(NetworkUtils.toURL("https://hmcl.huangyuhui.net/hmcl/crash.php"), map);
|
||||
if (StringUtils.isNotBlank(response))
|
||||
LOG.log(Level.SEVERE, "Crash server response: " + response);
|
||||
LOG.error("Crash server response: " + response);
|
||||
} catch (IOException ex) {
|
||||
LOG.log(Level.SEVERE, "Unable to post HMCL server.", ex);
|
||||
LOG.error("Unable to post HMCL server.", ex);
|
||||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
public final class HMCLService {
|
||||
private HMCLService() {
|
||||
}
|
||||
|
||||
public static void openRedirectLink(String id) {
|
||||
FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/" + id);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.util;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/**
|
||||
* Utility for Adding JavaFX to module path.
|
||||
|
||||
@@ -15,10 +15,9 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class NativePatcher {
|
||||
private NativePatcher() {
|
||||
@@ -37,7 +36,7 @@ public final class NativePatcher {
|
||||
|
||||
return natives.getOrDefault(p.toString(), Collections.emptyMap());
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.WARNING, "Failed to load native library list", e);
|
||||
LOG.warning("Failed to load native library list", e);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,6 +43,7 @@ package org.jackhuang.hmcl.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.ui.SwingUtils;
|
||||
import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
|
||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||
@@ -62,12 +63,11 @@ import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
|
||||
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.logging.Logger.LOG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
import static org.jackhuang.hmcl.util.platform.JavaVersion.CURRENT_JAVA;
|
||||
|
||||
@@ -121,7 +121,7 @@ public final class SelfDependencyPatcher {
|
||||
Class.forName("netscape.javascript.JSObject", false, classLoader);
|
||||
Class.forName("org.w3c.dom.html.HTMLDocument", false, classLoader);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.WARNING, "Disable javafx.web because JRE is incomplete", e);
|
||||
LOG.warning("Disable javafx.web because JRE is incomplete", e);
|
||||
dependencies.removeIf(it -> "javafx.web".equals(it.module) || "javafx.media".equals(it.module));
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ public final class SelfDependencyPatcher {
|
||||
}
|
||||
} else {
|
||||
LOG.info("User choose not to download JavaFX");
|
||||
System.exit(0);
|
||||
Main.exit(0);
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@@ -17,44 +17,49 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.i18n;
|
||||
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales.SupportedLocale;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
public final class I18n {
|
||||
|
||||
private I18n() {
|
||||
}
|
||||
|
||||
public static SupportedLocale getCurrentLocale() {
|
||||
try {
|
||||
return ConfigHolder.config().getLocalization();
|
||||
} catch (IllegalStateException e) {
|
||||
// e is thrown by ConfigHolder.config(), indicating the config hasn't been loaded
|
||||
// fallback to use default locale
|
||||
return Locales.DEFAULT;
|
||||
}
|
||||
private static volatile SupportedLocale locale = Locales.DEFAULT;
|
||||
private static volatile ResourceBundle resourceBundle = locale.getResourceBundle();
|
||||
private static volatile DateTimeFormatter dateTimeFormatter;
|
||||
|
||||
public static void setLocale(SupportedLocale locale) {
|
||||
I18n.locale = locale;
|
||||
resourceBundle = locale.getResourceBundle();
|
||||
dateTimeFormatter = null;
|
||||
}
|
||||
|
||||
public static boolean isUseChinese() {
|
||||
return locale.getLocale() == Locale.CHINA;
|
||||
}
|
||||
|
||||
public static ResourceBundle getResourceBundle() {
|
||||
return getCurrentLocale().getResourceBundle();
|
||||
return resourceBundle;
|
||||
}
|
||||
|
||||
public static String getName(SupportedLocale locale) {
|
||||
return locale == Locales.DEFAULT ? resourceBundle.getString("lang.default") : locale.getResourceBundle().getString("lang");
|
||||
}
|
||||
|
||||
public static String i18n(String key, Object... formatArgs) {
|
||||
try {
|
||||
return String.format(getResourceBundle().getString(key), formatArgs);
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
|
||||
LOG.error("Cannot find key " + key + " in resource bundle", e);
|
||||
} catch (IllegalFormatException e) {
|
||||
LOG.log(Level.SEVERE, "Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs), e);
|
||||
LOG.error("Illegal format string, key=" + key + ", args=" + Arrays.toString(formatArgs), e);
|
||||
}
|
||||
|
||||
return key + Arrays.toString(formatArgs);
|
||||
@@ -64,13 +69,18 @@ public final class I18n {
|
||||
try {
|
||||
return getResourceBundle().getString(key);
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
|
||||
LOG.error("Cannot find key " + key + " in resource bundle", e);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatDateTime(Instant instant) {
|
||||
return getCurrentLocale().getDateTimeFormatter().format(instant);
|
||||
DateTimeFormatter formatter = dateTimeFormatter;
|
||||
if (formatter == null) {
|
||||
formatter = dateTimeFormatter = DateTimeFormatter.ofPattern(getResourceBundle().getString("world.time")).withZone(ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
return formatter.format(instant);
|
||||
}
|
||||
|
||||
public static boolean hasKey(String key) {
|
||||
|
||||
@@ -24,8 +24,6 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.platform.JavaVersion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -34,7 +32,7 @@ public final class Locales {
|
||||
private Locales() {
|
||||
}
|
||||
|
||||
public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault(), "lang.default");
|
||||
public static final SupportedLocale DEFAULT = new SupportedLocale(Locale.getDefault());
|
||||
|
||||
/**
|
||||
* English
|
||||
@@ -102,23 +100,10 @@ public final class Locales {
|
||||
@JsonAdapter(SupportedLocale.TypeAdapter.class)
|
||||
public static final class SupportedLocale {
|
||||
private final Locale locale;
|
||||
private final String name;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private DateTimeFormatter dateTimeFormatter;
|
||||
private ResourceBundle resourceBundle;
|
||||
|
||||
SupportedLocale(Locale locale) {
|
||||
this(locale, null);
|
||||
}
|
||||
|
||||
SupportedLocale(Locale locale, String name) {
|
||||
this.locale = locale;
|
||||
this.name = name;
|
||||
if (JavaVersion.CURRENT_JAVA.getParsedVersion() == JavaVersion.JAVA_8) {
|
||||
resourceBundle = ResourceBundle.getBundle("assets.lang.I18N", locale, UTF8Control.INSTANCE);
|
||||
} else {
|
||||
// UTF-8 is supported in Java 9+
|
||||
resourceBundle = ResourceBundle.getBundle("assets.lang.I18N", locale);
|
||||
}
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
@@ -126,20 +111,22 @@ public final class Locales {
|
||||
}
|
||||
|
||||
public ResourceBundle getResourceBundle() {
|
||||
return resourceBundle;
|
||||
}
|
||||
ResourceBundle bundle = resourceBundle;
|
||||
|
||||
public DateTimeFormatter getDateTimeFormatter() {
|
||||
if (dateTimeFormatter == null) {
|
||||
dateTimeFormatter = DateTimeFormatter.ofPattern(resourceBundle.getString("world.time")).withZone(ZoneId.systemDefault());
|
||||
if (resourceBundle == null) {
|
||||
if (this != DEFAULT && this.locale == DEFAULT.locale) {
|
||||
bundle = DEFAULT.getResourceBundle();
|
||||
} else if (JavaVersion.CURRENT_JAVA.getParsedVersion() < 9) {
|
||||
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale, UTF8Control.INSTANCE);
|
||||
} else {
|
||||
// Java 9+ uses UTF-8 as the default encoding for resource bundles
|
||||
bundle = ResourceBundle.getBundle("assets.lang.I18N", locale);
|
||||
}
|
||||
|
||||
resourceBundle = bundle;
|
||||
}
|
||||
|
||||
return dateTimeFormatter;
|
||||
}
|
||||
|
||||
public String getName(ResourceBundle newResourceBundle) {
|
||||
if (name == null) return resourceBundle.getString("lang");
|
||||
else return newResourceBundle.getString(name);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static final class TypeAdapter extends com.google.gson.TypeAdapter<SupportedLocale> {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.jackhuang.hmcl.util.logging;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
final class CallerFinder {
|
||||
private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
private static final String PACKAGE_PREFIX = CallerFinder.class.getPackageName() + ".";
|
||||
private static final Predicate<StackWalker.StackFrame> PREDICATE = stackFrame -> !stackFrame.getClassName().startsWith(PACKAGE_PREFIX);
|
||||
private static final Function<Stream<StackWalker.StackFrame>, Optional<StackWalker.StackFrame>> FUNCTION = stream -> stream.filter(PREDICATE).findFirst();
|
||||
|
||||
static String getCaller() {
|
||||
return WALKER.walk(FUNCTION).map(it -> it.getClassName() + "." + it.getMethodName()).orElse(null);
|
||||
}
|
||||
|
||||
private CallerFinder() {
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 999 B |
|
Before Width: | Height: | Size: 912 B |
|
Before Width: | Height: | Size: 1001 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 861 B |
|
Before Width: | Height: | Size: 958 B |