Merge branch 'main' into pojav
|
@ -1,74 +1,110 @@
|
||||||
name: Bug Feedback
|
name: Bug Feedback / 错误反馈
|
||||||
description:
|
description:
|
||||||
Report a bug.
|
Report a bug. 报告错误。
|
||||||
labels: bug
|
labels: bug
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Please ensure you are using the **latest bata version** before you submit, you can get the latest beta version by checking update in launcher setting page.
|
Please ensure you are using the **latest bata version** before you submit, you can get the latest beta version by checking update in launcher setting page.
|
||||||
|
请确认在你提交之前你使用的是最新版本,你可以在启动器的设置页内通过检测更新获取最新版本。
|
||||||
|
Please confirm that the question you are asking is not related to modpacks.If you ask about modpacks, we will directly delete it.
|
||||||
|
请确认你询问的不是整合包相关的问题。如果你询问整合包相关问题,我们会直接删除。
|
||||||
To improve communication efficiency, we suggest you join our community to report bugs.
|
To improve communication efficiency, we suggest you join our community to report bugs.
|
||||||
|
为了提高交流效率,我们建议你加入我们的社区报告错误。
|
||||||
You can find our community in community page of the launcher.
|
You can find our community in community page of the launcher.
|
||||||
|
你可以在启动器的社区页内找到我们的社区。
|
||||||
|
- type: checkboxes
|
||||||
|
id: checks
|
||||||
|
attributes:
|
||||||
|
label: Before making issue / 在发布提问之前...
|
||||||
|
options:
|
||||||
|
- label: I have read the content above.我已经阅读了上方的内容。
|
||||||
|
required: true
|
||||||
|
- label: I understand what I'm asking is not about modpack.我明白我询问的不是整合包相关的问题。
|
||||||
|
required: true
|
||||||
|
- label: I have searched for existing issues.我已经搜索了现有的 issues。
|
||||||
|
required: true
|
||||||
|
- label: I have tried switching renderer.我已经尝试过切换渲染器。
|
||||||
- type: input
|
- type: input
|
||||||
id: fcl-version
|
id: fcl-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Launcher Version
|
label: Launcher Version / 启动器版本
|
||||||
description: Please enter the Fold Craft Launcher version you are using.
|
description: |
|
||||||
placeholder: e.g. 1.0.1
|
Please enter the Fold Craft Launcher version you are using.
|
||||||
|
请输入你正在使用的Fold Craft Launcher版本。
|
||||||
|
placeholder: e.g. 1.0.1 / 例如1.0.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: device
|
id: device
|
||||||
attributes:
|
attributes:
|
||||||
label: Device Information
|
label: Device Information / 设备信息
|
||||||
description: Please enter the information of the device on which you encountered the bug.
|
description: |
|
||||||
|
Please enter the information of the device on which you encountered the bug.
|
||||||
|
请输入你遇到错误的设备的信息。
|
||||||
placeholder: e.g. OPPO Find X3 Pro, Android 13, arm64
|
placeholder: e.g. OPPO Find X3 Pro, Android 13, arm64
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: bug-report
|
id: bug-report
|
||||||
attributes:
|
attributes:
|
||||||
label: Problem Description
|
label: Problem Description / 问题描述
|
||||||
description: Please describe the problem in as much detail as possible, especially how you encountered this bug.
|
description: |
|
||||||
|
Please describe the problem in as much detail as possible, especially how you encountered this bug.
|
||||||
|
请尽可能详细地描述问题,特别是你是如何遇到这个bug的。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: java-version
|
id: java-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Java
|
label: Java
|
||||||
description: Please enter the version of Java you are using.
|
description: |
|
||||||
|
Please enter the version of Java you are using.
|
||||||
|
请输入你正在使用的Java版本。
|
||||||
placeholder: e.g. Java 17
|
placeholder: e.g. Java 17
|
||||||
- type: input
|
- type: input
|
||||||
id: renderer
|
id: renderer
|
||||||
attributes:
|
attributes:
|
||||||
label: Renderer
|
label: Renderer / 渲染器
|
||||||
description: Please enter the renderer you are using.
|
description: |
|
||||||
|
Please enter the renderer you are using.
|
||||||
|
请输入你正在使用的渲染器。
|
||||||
placeholder: e.g. Holy-GL4ES
|
placeholder: e.g. Holy-GL4ES
|
||||||
- type: input
|
- type: input
|
||||||
id: game-version
|
id: game-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Game Version
|
label: Game Version / 游戏版本
|
||||||
description: If you are having trouble launching a game that was automatically downloaded through the Fold Craft Launcher, please enter the version of the game you are launching.
|
description: |
|
||||||
|
If you are having trouble launching a game that was automatically downloaded through the Fold Craft Launcher, please enter the version of the game you are launching.
|
||||||
|
如果你在启动通过Fold Craft Launcher自动下载的游戏时遇到问题,请输入你要启动的游戏版本。
|
||||||
placeholder: e.g. 1.19
|
placeholder: e.g. 1.19
|
||||||
- type: input
|
- type: input
|
||||||
id: game-modify
|
id: game-modify
|
||||||
attributes:
|
attributes:
|
||||||
label: Modifications Made to the Game
|
label: Modifications Made to the Game / 对游戏所做的修改
|
||||||
description: If you have OptiFine, Forge, or other mods installed for the game, please enter your modifications below.
|
description: |
|
||||||
|
If you have OptiFine, Forge, or other mods installed for the game, please enter your modifications below.
|
||||||
|
如果你为游戏安装了OptiFine、Forge或其他模组,请在下面输入你的修改。
|
||||||
placeholder: e.g. OptiFine HD_U_G9, Rift
|
placeholder: e.g. OptiFine HD_U_G9, Rift
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: game-crash-report
|
id: game-crash-report
|
||||||
attributes:
|
attributes:
|
||||||
label: Game Crash Report
|
label: Game Crash Report / 游戏崩溃报告
|
||||||
description: If there is a game crash report, please upload it below.
|
description: |
|
||||||
|
If there is a game crash report, please upload it(file) below.
|
||||||
|
如果有游戏崩溃报告,请在下面上传(文件)。
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: fcl-crash-report
|
id: fcl-crash-report
|
||||||
attributes:
|
attributes:
|
||||||
label: Launcher Crash Report
|
label: Launcher Crash Report / 启动器崩溃报告
|
||||||
description: If there is a launcher crash report, please upload it below.
|
description: |
|
||||||
|
If there is a launcher crash report, please upload it(file) below.
|
||||||
|
如果有启动器崩溃报告,请在下面上传(文件)。
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: fcl-logs
|
id: fcl-logs
|
||||||
attributes:
|
attributes:
|
||||||
label: Launcher Log File
|
label: Launcher Log File / 启动器日志文件
|
||||||
description: After encountering a problem, please click "Export Launcher Log" in the launcher setting page, and upload the exported log to the input field below.
|
description: |
|
||||||
|
After encountering a problem, please click "Export Launcher Log" in the launcher setting page, and upload the exported log to the input field below.
|
||||||
|
遇到问题后,请在启动器设置页面中单击“导出启动器日志”,并将导出的日志上传到下面的输入字段。
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
name: "Feature Request"
|
name: Feature Request / 功能请求
|
||||||
description: "Propose new features for Fold Craft Launcher"
|
description: |
|
||||||
|
Propose new features for Fold Craft Launcher.
|
||||||
|
为Fold Craft Launcher提出新功能。
|
||||||
labels: feature request
|
labels: feature request
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Make sure to check the issues list for duplicates before submitting!"
|
value: |
|
||||||
|
Make sure to check the issues list for duplicates before submitting!
|
||||||
|
提交前,请务必检查问题列表中的重复项!
|
||||||
|
- type: checkboxes
|
||||||
|
id: checks
|
||||||
|
attributes:
|
||||||
|
label: Before making issue / 在发布提问之前...
|
||||||
|
options:
|
||||||
|
- label: I understand this is a request, not a wish.我明白这是在提出请求而不是在许愿。
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "feature"
|
id: "feature"
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description / 描述
|
||||||
description: "Describe in detail the new features you would like to add."
|
description: |
|
||||||
|
Describe in detail the new features you would like to add.
|
||||||
|
详细描述你要添加的新功能。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: "reason"
|
id: "reason"
|
||||||
attributes:
|
attributes:
|
||||||
label: Why
|
label: Why / 原因
|
||||||
description: "Describe the benefits of this feature and why."
|
description: |
|
||||||
|
Describe the benefits of this feature and why.
|
||||||
|
描述此功能的好处及其原因。
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -44,8 +44,8 @@ android {
|
||||||
applicationId "com.tungsten.fcl"
|
applicationId "com.tungsten.fcl"
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 1175
|
versionCode 1176
|
||||||
versionName "1.1.7.5"
|
versionName "1.1.7.6"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ fov:0.0
|
||||||
gamma:0.5070422
|
gamma:0.5070422
|
||||||
saturation:0.0
|
saturation:0.0
|
||||||
renderDistance:2
|
renderDistance:2
|
||||||
guiScale:0
|
guiScale:4
|
||||||
particles:1
|
particles:1
|
||||||
bobView:false
|
bobView:false
|
||||||
anaglyph3d:false
|
anaglyph3d:false
|
||||||
|
|
|
@ -11,6 +11,8 @@ import android.util.Log;
|
||||||
|
|
||||||
import com.tungsten.fcl.activity.JVMActivity;
|
import com.tungsten.fcl.activity.JVMActivity;
|
||||||
import com.tungsten.fcl.control.MenuType;
|
import com.tungsten.fcl.control.MenuType;
|
||||||
|
import com.tungsten.fcl.setting.Profile;
|
||||||
|
import com.tungsten.fcl.setting.Profiles;
|
||||||
import com.tungsten.fcl.util.AndroidUtils;
|
import com.tungsten.fcl.util.AndroidUtils;
|
||||||
import com.tungsten.fcl.util.RequestCodes;
|
import com.tungsten.fcl.util.RequestCodes;
|
||||||
import com.tungsten.fclauncher.FCLConfig;
|
import com.tungsten.fclauncher.FCLConfig;
|
||||||
|
@ -59,6 +61,13 @@ public class JarExecutorHelper {
|
||||||
private static void launchJarExecutor(Context context, File file) {
|
private static void launchJarExecutor(Context context, File file) {
|
||||||
int version = getJavaVersion(file);
|
int version = getJavaVersion(file);
|
||||||
int javaVersion = getNearestJavaVersion(version);
|
int javaVersion = getNearestJavaVersion(version);
|
||||||
|
Profile profile = Profiles.getSelectedProfile();
|
||||||
|
if (profile != null) {
|
||||||
|
String java = profile.getGlobal().getJava();
|
||||||
|
if (!java.equals(JavaVersion.JAVA_AUTO.getVersionName())) {
|
||||||
|
javaVersion = JavaVersion.getJavaFromVersionName(java).getVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
exec(context, file, javaVersion, null);
|
exec(context, file, javaVersion, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,7 +207,7 @@ public final class LauncherHelper {
|
||||||
launchingStepsPane.dismiss();
|
launchingStepsPane.dismiss();
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Exception ex = executor.getException();
|
Exception ex = executor.getException();
|
||||||
if (!(ex instanceof CancellationException)) {
|
if (ex != null && !(ex instanceof CancellationException)) {
|
||||||
Schedulers.androidUIThread().execute(() -> {
|
Schedulers.androidUIThread().execute(() -> {
|
||||||
String message;
|
String message;
|
||||||
if (ex instanceof ModpackCompletionException) {
|
if (ex instanceof ModpackCompletionException) {
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
*/
|
*/
|
||||||
package com.tungsten.fcl.game;
|
package com.tungsten.fcl.game;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
import com.tungsten.fcl.setting.Profile;
|
import com.tungsten.fcl.setting.Profile;
|
||||||
import com.tungsten.fclcore.task.Task;
|
import com.tungsten.fclcore.task.Task;
|
||||||
import com.tungsten.fclcore.util.io.CompressingUtils;
|
import com.tungsten.fclcore.util.io.CompressingUtils;
|
||||||
|
@ -48,7 +50,7 @@ public class ManuallyCreatedModpackInstallTask extends Task<Path> {
|
||||||
subdirectory = ModpackHelper.findMinecraftDirectoryInManuallyCreatedModpack(zipFile.toString(), fs);
|
subdirectory = ModpackHelper.findMinecraftDirectoryInManuallyCreatedModpack(zipFile.toString(), fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Path dest = Paths.get("externalgames").resolve(name);
|
Path dest = Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath() + "/FCL").resolve(name);
|
||||||
|
|
||||||
setResult(dest);
|
setResult(dest);
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,13 @@ import static com.tungsten.fclcore.util.Lang.mapOf;
|
||||||
import static com.tungsten.fclcore.util.Lang.toIterable;
|
import static com.tungsten.fclcore.util.Lang.toIterable;
|
||||||
import static com.tungsten.fclcore.util.Pair.pair;
|
import static com.tungsten.fclcore.util.Pair.pair;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.tungsten.fcl.setting.Profile;
|
import com.tungsten.fcl.setting.Profile;
|
||||||
import com.tungsten.fcl.setting.Profiles;
|
import com.tungsten.fcl.setting.Profiles;
|
||||||
import com.tungsten.fcl.setting.VersionSetting;
|
import com.tungsten.fcl.setting.VersionSetting;
|
||||||
import com.tungsten.fclcore.game.GameDirectoryType;
|
|
||||||
import com.tungsten.fclcore.mod.MismatchedModpackTypeException;
|
import com.tungsten.fclcore.mod.MismatchedModpackTypeException;
|
||||||
import com.tungsten.fclcore.mod.Modpack;
|
import com.tungsten.fclcore.mod.Modpack;
|
||||||
import com.tungsten.fclcore.mod.ModpackCompletionException;
|
import com.tungsten.fclcore.mod.ModpackCompletionException;
|
||||||
|
@ -181,7 +182,7 @@ public final class ModpackHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExternalGameNameConflicts(String name) {
|
public static boolean isExternalGameNameConflicts(String name) {
|
||||||
return Files.exists(Paths.get("externalgames").resolve(name));
|
return Files.exists(Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath() + "/FCL").resolve(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<?> getInstallManuallyCreatedModpackTask(Profile profile, File zipFile, String name, Charset charset) {
|
public static Task<?> getInstallManuallyCreatedModpackTask(Profile profile, File zipFile, String name, Charset charset) {
|
||||||
|
|
|
@ -143,9 +143,9 @@ public class GameOption {
|
||||||
String str = get("guiScale");
|
String str = get("guiScale");
|
||||||
int guiScale;
|
int guiScale;
|
||||||
try {
|
try {
|
||||||
guiScale = (str == null ? 0 : Integer.parseInt(str));
|
guiScale = (str == null ? 4 : Integer.parseInt(str));
|
||||||
} catch (NumberFormatException ignore) {
|
} catch (NumberFormatException ignore) {
|
||||||
guiScale = 0;
|
guiScale = 4;
|
||||||
}
|
}
|
||||||
int scale = Math.max(Math.min(width / 320, height / 240), 1);
|
int scale = Math.max(Math.min(width / 320, height / 240), 1);
|
||||||
if (scale < guiScale || guiScale == 0) {
|
if (scale < guiScale || guiScale == 0) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
</shape>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/white"/>
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/white"/>
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Theme.FoldCraftLauncher" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/default_theme_color</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/default_theme_color</item>
|
||||||
|
<item name="colorOnPrimary">@color/default_theme_color</item>
|
||||||
|
<!-- Secondary brand color. -->
|
||||||
|
<item name="colorSecondary">@color/default_theme_color</item>
|
||||||
|
<item name="colorSecondaryVariant">@color/default_theme_color</item>
|
||||||
|
<item name="colorOnSecondary">@color/default_theme_color</item>
|
||||||
|
<!-- Status bar color. -->
|
||||||
|
<item name="android:statusBarColor">@color/default_theme_color</item>
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
|
<item name="android:windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
|
@ -270,7 +270,7 @@
|
||||||
|
|
||||||
<string name="download_provider_mcbbs">我的世界中文论坛 (https://www.mcbbs.net/)</string>
|
<string name="download_provider_mcbbs">我的世界中文论坛 (https://www.mcbbs.net/)</string>
|
||||||
<string name="download_provider_bmclapi">BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)</string>
|
<string name="download_provider_bmclapi">BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)</string>
|
||||||
<string name="download_provider_mojang">官方 (OptiFine 自动安装使用 BMCLAPI 下载源)</string>
|
<string name="download_provider_mojang">官方 (OptiFine 自动安装使用 OF-302 下载源)</string>
|
||||||
<string name="download_provider_official">尽量使用官方源</string>
|
<string name="download_provider_official">尽量使用官方源</string>
|
||||||
<string name="download_provider_balanced">选择加载速度快的下载源</string>
|
<string name="download_provider_balanced">选择加载速度快的下载源</string>
|
||||||
<string name="download_provider_mirror">尽量使用镜像源</string>
|
<string name="download_provider_mirror">尽量使用镜像源</string>
|
||||||
|
|
|
@ -293,7 +293,7 @@
|
||||||
|
|
||||||
<string name="download_provider_mcbbs">MCBBS (https://www.mcbbs.net/)</string>
|
<string name="download_provider_mcbbs">MCBBS (https://www.mcbbs.net/)</string>
|
||||||
<string name="download_provider_bmclapi">BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)</string>
|
<string name="download_provider_bmclapi">BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)</string>
|
||||||
<string name="download_provider_mojang">Mojang (OptiFine is provided by BMCLAPI)</string>
|
<string name="download_provider_mojang">Mojang (OptiFine is provided by OF-302)</string>
|
||||||
<string name="download_provider_official">From Official Sources</string>
|
<string name="download_provider_official">From Official Sources</string>
|
||||||
<string name="download_provider_balanced">From Fastest Available</string>
|
<string name="download_provider_balanced">From Fastest Available</string>
|
||||||
<string name="download_provider_mirror">From Mirror</string>
|
<string name="download_provider_mirror">From Mirror</string>
|
||||||
|
|
|
@ -38,7 +38,6 @@ import com.tungsten.fclcore.auth.authlibinjector.AuthlibInjectorArtifactInfo;
|
||||||
import com.tungsten.fclcore.auth.authlibinjector.AuthlibInjectorArtifactProvider;
|
import com.tungsten.fclcore.auth.authlibinjector.AuthlibInjectorArtifactProvider;
|
||||||
import com.tungsten.fclcore.auth.authlibinjector.AuthlibInjectorDownloadException;
|
import com.tungsten.fclcore.auth.authlibinjector.AuthlibInjectorDownloadException;
|
||||||
import com.tungsten.fclcore.auth.yggdrasil.Texture;
|
import com.tungsten.fclcore.auth.yggdrasil.Texture;
|
||||||
import com.tungsten.fclcore.auth.yggdrasil.TextureModel;
|
|
||||||
import com.tungsten.fclcore.auth.yggdrasil.TextureType;
|
import com.tungsten.fclcore.auth.yggdrasil.TextureType;
|
||||||
import com.tungsten.fclcore.fakefx.beans.binding.Bindings;
|
import com.tungsten.fclcore.fakefx.beans.binding.Bindings;
|
||||||
import com.tungsten.fclcore.fakefx.beans.binding.ObjectBinding;
|
import com.tungsten.fclcore.fakefx.beans.binding.ObjectBinding;
|
||||||
|
@ -100,14 +99,7 @@ public class OfflineAccount extends Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean loadAuthlibInjector(Skin skin) {
|
protected boolean loadAuthlibInjector(Skin skin) {
|
||||||
if (skin == null) return false;
|
return skin != null && skin.getType() != Skin.Type.DEFAULT;
|
||||||
if (skin.getType() == Skin.Type.DEFAULT) return false;
|
|
||||||
TextureModel defaultModel = TextureModel.detectUUID(getUUID());
|
|
||||||
if (skin.getType() == Skin.Type.ALEX && defaultModel == TextureModel.ALEX ||
|
|
||||||
skin.getType() == Skin.Type.STEVE && defaultModel == TextureModel.STEVE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,11 +19,11 @@ package com.tungsten.fclcore.download;
|
||||||
|
|
||||||
import com.tungsten.fclcore.download.fabric.FabricAPIVersionList;
|
import com.tungsten.fclcore.download.fabric.FabricAPIVersionList;
|
||||||
import com.tungsten.fclcore.download.fabric.FabricVersionList;
|
import com.tungsten.fclcore.download.fabric.FabricVersionList;
|
||||||
import com.tungsten.fclcore.download.forge.ForgeBMCLVersionList;
|
import com.tungsten.fclcore.download.forge.ForgeVersionList;
|
||||||
import com.tungsten.fclcore.download.game.GameVersionList;
|
import com.tungsten.fclcore.download.game.GameVersionList;
|
||||||
import com.tungsten.fclcore.download.liteloader.LiteLoaderVersionList;
|
import com.tungsten.fclcore.download.liteloader.LiteLoaderVersionList;
|
||||||
import com.tungsten.fclcore.download.neoforge.NeoForgeOfficialVersionList;
|
import com.tungsten.fclcore.download.neoforge.NeoForgeOfficialVersionList;
|
||||||
import com.tungsten.fclcore.download.optifine.OptiFineBMCLVersionList;
|
import com.tungsten.fclcore.download.optifine.OptiFine302VersionList;
|
||||||
import com.tungsten.fclcore.download.quilt.QuiltAPIVersionList;
|
import com.tungsten.fclcore.download.quilt.QuiltAPIVersionList;
|
||||||
import com.tungsten.fclcore.download.quilt.QuiltVersionList;
|
import com.tungsten.fclcore.download.quilt.QuiltVersionList;
|
||||||
|
|
||||||
|
@ -34,10 +34,10 @@ public class MojangDownloadProvider implements DownloadProvider {
|
||||||
private final GameVersionList game;
|
private final GameVersionList game;
|
||||||
private final FabricVersionList fabric;
|
private final FabricVersionList fabric;
|
||||||
private final FabricAPIVersionList fabricApi;
|
private final FabricAPIVersionList fabricApi;
|
||||||
private final ForgeBMCLVersionList forge;
|
private final ForgeVersionList forge;
|
||||||
private final NeoForgeOfficialVersionList neoforge;
|
private final NeoForgeOfficialVersionList neoforge;
|
||||||
private final LiteLoaderVersionList liteLoader;
|
private final LiteLoaderVersionList liteLoader;
|
||||||
private final OptiFineBMCLVersionList optifine;
|
private final OptiFine302VersionList optifine;
|
||||||
private final QuiltVersionList quilt;
|
private final QuiltVersionList quilt;
|
||||||
private final QuiltAPIVersionList quiltApi;
|
private final QuiltAPIVersionList quiltApi;
|
||||||
|
|
||||||
|
@ -47,10 +47,10 @@ public class MojangDownloadProvider implements DownloadProvider {
|
||||||
this.game = new GameVersionList(this);
|
this.game = new GameVersionList(this);
|
||||||
this.fabric = new FabricVersionList(this);
|
this.fabric = new FabricVersionList(this);
|
||||||
this.fabricApi = new FabricAPIVersionList(this);
|
this.fabricApi = new FabricAPIVersionList(this);
|
||||||
this.forge = new ForgeBMCLVersionList(apiRoot);
|
this.forge = new ForgeVersionList(this);
|
||||||
this.neoforge = new NeoForgeOfficialVersionList(this);
|
this.neoforge = new NeoForgeOfficialVersionList(this);
|
||||||
this.liteLoader = new LiteLoaderVersionList(this);
|
this.liteLoader = new LiteLoaderVersionList(this);
|
||||||
this.optifine = new OptiFineBMCLVersionList(apiRoot);
|
this.optifine = new OptiFine302VersionList("https://zkitefly.github.io/optifine-download-list/index.json");
|
||||||
this.quilt = new QuiltVersionList(this);
|
this.quilt = new QuiltVersionList(this);
|
||||||
this.quiltApi = new QuiltAPIVersionList(this);
|
this.quiltApi = new QuiltAPIVersionList(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,9 +129,14 @@ public abstract class VersionList<T extends RemoteVersion> {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
T result = null;
|
T result = null;
|
||||||
for (T it : versions.get(gameVersion))
|
TreeSet<T> remoteVersions = versions.get(gameVersion);
|
||||||
|
for (T it : remoteVersions)
|
||||||
if (remoteVersion.equals(it.getSelfVersion()))
|
if (remoteVersion.equals(it.getSelfVersion()))
|
||||||
result = it;
|
result = it;
|
||||||
|
if (result == null)
|
||||||
|
for (T it : remoteVersions)
|
||||||
|
if (remoteVersion.equals(it.getFullVersion()))
|
||||||
|
result = it;
|
||||||
return Optional.ofNullable(result);
|
return Optional.ofNullable(result);
|
||||||
} finally {
|
} finally {
|
||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
|
|
|
@ -25,6 +25,7 @@ import static com.tungsten.fclcore.util.Pair.pair;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.tungsten.fclcore.download.VersionList;
|
import com.tungsten.fclcore.download.VersionList;
|
||||||
|
import com.tungsten.fclcore.util.Lang;
|
||||||
import com.tungsten.fclcore.util.StringUtils;
|
import com.tungsten.fclcore.util.StringUtils;
|
||||||
import com.tungsten.fclcore.util.gson.Validation;
|
import com.tungsten.fclcore.util.gson.Validation;
|
||||||
import com.tungsten.fclcore.util.io.HttpRequest;
|
import com.tungsten.fclcore.util.io.HttpRequest;
|
||||||
|
@ -35,7 +36,10 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@ -64,10 +68,27 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||||
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
|
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String toLookupVersion(String gameVersion) {
|
||||||
|
return "1.7.10-pre4".equals(gameVersion) ? "1.7.10_pre4" : gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fromLookupVersion(String lookupVersion) {
|
||||||
|
return "1.7.10_pre4".equals(lookupVersion) ? "1.7.10-pre4" : lookupVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toLookupBranch(String gameVersion, String branch) {
|
||||||
|
if ("1.7.10-pre4".equals(gameVersion)) {
|
||||||
|
return "prerelease";
|
||||||
|
}
|
||||||
|
return Lang.requireNonNullElse(branch, "");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
public CompletableFuture<?> refreshAsync(String gameVersion) {
|
||||||
|
String lookupVersion = toLookupVersion(gameVersion);
|
||||||
|
|
||||||
return CompletableFuture.completedFuture(null)
|
return CompletableFuture.completedFuture(null)
|
||||||
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + gameVersion).<List<ForgeVersion>>getJson(new TypeToken<List<ForgeVersion>>() {
|
.thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + lookupVersion).<List<ForgeVersion>>getJson(new TypeToken<List<ForgeVersion>>() {
|
||||||
}.getType())))
|
}.getType())))
|
||||||
.thenAcceptAsync(forgeVersions -> {
|
.thenAcceptAsync(forgeVersions -> {
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
@ -81,16 +102,17 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||||
List<String> urls = new ArrayList<>();
|
List<String> urls = new ArrayList<>();
|
||||||
for (ForgeVersion.File file : version.getFiles())
|
for (ForgeVersion.File file : version.getFiles())
|
||||||
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
|
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
|
||||||
String classifier = gameVersion + "-" + version.getVersion()
|
String branch = toLookupBranch(gameVersion, version.getBranch());
|
||||||
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
|
|
||||||
|
String classifier = lookupVersion + "-" + version.getVersion() + (branch.isEmpty() ? "" : '-' + branch);
|
||||||
String fileName1 = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat();
|
String fileName1 = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat();
|
||||||
String fileName2 = "forge-" + classifier + "-" + gameVersion + "-" + file.getCategory() + "." + file.getFormat();
|
String fileName2 = "forge-" + classifier + "-" + lookupVersion + "-" + file.getCategory() + "." + file.getFormat();
|
||||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1);
|
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1);
|
||||||
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "-" + gameVersion + "/" + fileName2);
|
urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "-" + lookupVersion + "/" + fileName2);
|
||||||
urls.add(NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf(
|
urls.add(NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf(
|
||||||
pair("mcversion", version.getGameVersion()),
|
pair("mcversion", version.getGameVersion()),
|
||||||
pair("version", version.getVersion()),
|
pair("version", version.getVersion()),
|
||||||
pair("branch", version.getBranch()),
|
pair("branch", branch),
|
||||||
pair("category", file.getCategory()),
|
pair("category", file.getCategory()),
|
||||||
pair("format", file.getFormat())
|
pair("format", file.getFormat())
|
||||||
)));
|
)));
|
||||||
|
@ -109,7 +131,7 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
|
||||||
}
|
}
|
||||||
|
|
||||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||||
version.getGameVersion(), version.getVersion(), releaseDate, urls));
|
fromLookupVersion(version.getGameVersion()), version.getVersion(), releaseDate, urls));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
lock.writeLock().unlock();
|
lock.writeLock().unlock();
|
||||||
|
|
|
@ -27,7 +27,7 @@ public final class ForgeVersion implements Validation {
|
||||||
private final String jobver;
|
private final String jobver;
|
||||||
private final String version;
|
private final String version;
|
||||||
private final int build;
|
private final int build;
|
||||||
private final double modified;
|
private final String modified;
|
||||||
private final String[][] files;
|
private final String[][] files;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,10 +35,10 @@ public final class ForgeVersion implements Validation {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public ForgeVersion() {
|
public ForgeVersion() {
|
||||||
this(null, null, null, null, 0, 0, null);
|
this(null, null, null, null, 0, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForgeVersion(String branch, String mcversion, String jobver, String version, int build, double modified, String[][] files) {
|
public ForgeVersion(String branch, String mcversion, String jobver, String version, int build, String modified, String[][] files) {
|
||||||
this.branch = branch;
|
this.branch = branch;
|
||||||
this.mcversion = mcversion;
|
this.mcversion = mcversion;
|
||||||
this.jobver = jobver;
|
this.jobver = jobver;
|
||||||
|
@ -68,7 +68,7 @@ public final class ForgeVersion implements Validation {
|
||||||
return build;
|
return build;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getModified() {
|
public String getModified() {
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,13 @@ import com.tungsten.fclcore.util.StringUtils;
|
||||||
import com.tungsten.fclcore.util.io.HttpRequest;
|
import com.tungsten.fclcore.util.io.HttpRequest;
|
||||||
import com.tungsten.fclcore.util.versioning.VersionNumber;
|
import com.tungsten.fclcore.util.versioning.VersionNumber;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static com.tungsten.fclcore.util.Logging.LOG;
|
||||||
|
|
||||||
public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||||
private final DownloadProvider downloadProvider;
|
private final DownloadProvider downloadProvider;
|
||||||
|
@ -39,9 +43,17 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String toLookupVersion(String gameVersion) {
|
||||||
|
return "1.7.10-pre4".equals(gameVersion) ? "1.7.10_pre4" : gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fromLookupVersion(String lookupVersion) {
|
||||||
|
return "1.7.10_pre4".equals(lookupVersion) ? "1.7.10-pre4" : lookupVersion;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<?> refreshAsync() {
|
public CompletableFuture<?> refreshAsync() {
|
||||||
return HttpRequest.GET(downloadProvider.injectURL(FORGE_LIST)).getJsonAsync(ForgeVersionRoot.class)
|
return HttpRequest.GET(FORGE_LIST).getJsonAsync(ForgeVersionRoot.class)
|
||||||
.thenAcceptAsync(root -> {
|
.thenAcceptAsync(root -> {
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
|
||||||
|
@ -51,7 +63,7 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||||
versions.clear();
|
versions.clear();
|
||||||
|
|
||||||
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
for (Map.Entry<String, int[]> entry : root.getGameVersions().entrySet()) {
|
||||||
String gameVersion = VersionNumber.normalize(entry.getKey());
|
String gameVersion = fromLookupVersion(VersionNumber.normalize(entry.getKey()));
|
||||||
for (int v : entry.getValue()) {
|
for (int v : entry.getValue()) {
|
||||||
ForgeVersion version = root.getNumber().get(v);
|
ForgeVersion version = root.getNumber().get(v);
|
||||||
if (version == null)
|
if (version == null)
|
||||||
|
@ -67,8 +79,19 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||||
|
|
||||||
if (jar == null)
|
if (jar == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Instant releaseDate = null;
|
||||||
|
if (version.getModified() != null) {
|
||||||
|
try {
|
||||||
|
long timestamp = Long.parseLong(version.getModified());
|
||||||
|
releaseDate = Instant.ofEpochSecond(timestamp);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
LOG.log(Level.WARNING, "Failed to parse instant " + version.getModified(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
versions.put(gameVersion, new ForgeRemoteVersion(
|
versions.put(gameVersion, new ForgeRemoteVersion(
|
||||||
version.getGameVersion(), version.getVersion(), null, Collections.singletonList(jar)
|
toLookupVersion(version.getGameVersion()), version.getVersion(), releaseDate, Collections.singletonList(jar)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,5 +101,5 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String FORGE_LIST = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/json";
|
public static final String FORGE_LIST = "https://zkitefly.github.io/forge-maven-metadata/list.json";
|
||||||
}
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.tungsten.fclcore.download.optifine;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.tungsten.fclcore.download.VersionList;
|
||||||
|
import com.tungsten.fclcore.util.io.HttpRequest;
|
||||||
|
import com.tungsten.fclcore.util.versioning.VersionNumber;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
public final class OptiFine302VersionList extends VersionList<OptiFineRemoteVersion> {
|
||||||
|
private final String versionListURL;
|
||||||
|
|
||||||
|
public OptiFine302VersionList(String versionListURL) {
|
||||||
|
this.versionListURL = versionListURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasType() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<?> refreshAsync() {
|
||||||
|
return HttpRequest.GET(versionListURL).<OptiFine302VersionList.VersionList>getJsonAsync(new TypeToken<OptiFine302VersionList.VersionList>() {
|
||||||
|
}.getType()).thenAcceptAsync(root -> {
|
||||||
|
lock.writeLock().lock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
versions.clear();
|
||||||
|
for (OptiFineVersion element : root.versions) {
|
||||||
|
String gameVersion = VersionNumber.normalize(element.gameVersion);
|
||||||
|
versions.put(gameVersion, new OptiFineRemoteVersion(
|
||||||
|
gameVersion, element.version,
|
||||||
|
root.downloadBases.stream().map(u -> u + element.fileName).collect(Collectors.toList()),
|
||||||
|
element.fileName.startsWith("pre")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class VersionList {
|
||||||
|
@SerializedName("file")
|
||||||
|
private final List<OptiFineVersion> versions;
|
||||||
|
|
||||||
|
@SerializedName("download")
|
||||||
|
private final List<String> downloadBases;
|
||||||
|
|
||||||
|
public VersionList(List<OptiFineVersion> versions, List<String> downloadBases) {
|
||||||
|
this.versions = versions;
|
||||||
|
this.downloadBases = downloadBases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class OptiFineVersion {
|
||||||
|
@SerializedName("name")
|
||||||
|
private final String version;
|
||||||
|
@SerializedName("filename")
|
||||||
|
private final String fileName;
|
||||||
|
@SerializedName("mcversion")
|
||||||
|
private final String gameVersion;
|
||||||
|
|
||||||
|
public OptiFineVersion(String version, String fileName, String gameVersion) {
|
||||||
|
this.version = version;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package com.tungsten.fclcore.download.optifine;
|
package com.tungsten.fclcore.download.optifine;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.tungsten.fclcore.download.VersionList;
|
import com.tungsten.fclcore.download.VersionList;
|
||||||
import com.tungsten.fclcore.util.StringUtils;
|
import com.tungsten.fclcore.util.StringUtils;
|
||||||
|
@ -47,25 +48,24 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<?> refreshAsync() {
|
public CompletableFuture<?> refreshAsync() {
|
||||||
return HttpRequest.GET(apiRoot + "/optifine/versionlist").<List<OptiFineVersion>>getJsonAsync(new TypeToken<List<OptiFineVersion>>() {
|
return HttpRequest.GET(apiRoot + "/optifine/versionlist").<List<OptiFineVersion>>getJsonAsync(new TypeToken<List<OptiFineVersion>>() {
|
||||||
}.getType())
|
}.getType()).thenAcceptAsync(root -> {
|
||||||
.thenAcceptAsync(root -> {
|
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
versions.clear();
|
versions.clear();
|
||||||
Set<String> duplicates = new HashSet<>();
|
Set<String> duplicates = new HashSet<>();
|
||||||
for (OptiFineVersion element : root) {
|
for (OptiFineVersion element : root) {
|
||||||
String version = element.getType() + "_" + element.getPatch();
|
String version = element.type + "_" + element.patch;
|
||||||
String mirror = "https://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
String mirror = apiRoot + "/optifine/" + element.gameVersion + "/" + element.type + "/" + element.patch;
|
||||||
if (!duplicates.add(mirror))
|
if (!duplicates.add(mirror))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
boolean isPre = element.getPatch() != null && (element.getPatch().startsWith("pre") || element.getPatch().startsWith("alpha"));
|
boolean isPre = element.patch != null && (element.patch.startsWith("pre") || element.patch.startsWith("alpha"));
|
||||||
|
|
||||||
if (StringUtils.isBlank(element.getGameVersion()))
|
if (StringUtils.isBlank(element.gameVersion))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
String gameVersion = VersionNumber.normalize(element.getGameVersion());
|
String gameVersion = VersionNumber.normalize(element.gameVersion);
|
||||||
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Collections.singletonList(mirror), isPre));
|
versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Collections.singletonList(mirror), isPre));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -74,4 +74,20 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class OptiFineVersion {
|
||||||
|
@SerializedName("type")
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
@SerializedName("patch")
|
||||||
|
private final String patch;
|
||||||
|
|
||||||
|
@SerializedName("mcversion")
|
||||||
|
private final String gameVersion;
|
||||||
|
|
||||||
|
public OptiFineVersion(String type, String patch, String gameVersion) {
|
||||||
|
this.type = type;
|
||||||
|
this.patch = patch;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -31,6 +31,11 @@ public class OptiFineRemoteVersion extends RemoteVersion {
|
||||||
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, null, snapshot ? Type.SNAPSHOT : Type.RELEASE, urls);
|
super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, null, snapshot ? Type.SNAPSHOT : Type.RELEASE, urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullVersion() {
|
||||||
|
return getGameVersion() + "_" + getSelfVersion();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task<Version> getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) {
|
public Task<Version> getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) {
|
||||||
return new OptiFineInstallTask(dependencyManager, baseVersion, this);
|
return new OptiFineInstallTask(dependencyManager, baseVersion, this);
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Hello Minecraft! Launcher
|
|
||||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.tungsten.fclcore.download.optifine;
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
|
|
||||||
public final class OptiFineVersion {
|
|
||||||
|
|
||||||
@SerializedName("dl")
|
|
||||||
private final String downloadLink;
|
|
||||||
|
|
||||||
@SerializedName("ver")
|
|
||||||
private final String version;
|
|
||||||
|
|
||||||
@SerializedName("date")
|
|
||||||
private final String date;
|
|
||||||
|
|
||||||
@SerializedName("type")
|
|
||||||
private final String type;
|
|
||||||
|
|
||||||
@SerializedName("patch")
|
|
||||||
private final String patch;
|
|
||||||
|
|
||||||
@SerializedName("mirror")
|
|
||||||
private final String mirror;
|
|
||||||
|
|
||||||
@SerializedName("mcversion")
|
|
||||||
private final String gameVersion;
|
|
||||||
|
|
||||||
public OptiFineVersion() {
|
|
||||||
this(null, null, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OptiFineVersion(String downloadLink, String version, String date, String type, String patch, String mirror, String gameVersion) {
|
|
||||||
this.downloadLink = downloadLink;
|
|
||||||
this.version = version;
|
|
||||||
this.date = date;
|
|
||||||
this.type = type;
|
|
||||||
this.patch = patch;
|
|
||||||
this.mirror = mirror;
|
|
||||||
this.gameVersion = gameVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDownloadLink() {
|
|
||||||
return downloadLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDate() {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPatch() {
|
|
||||||
return patch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMirror() {
|
|
||||||
return mirror;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGameVersion() {
|
|
||||||
return gameVersion;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,6 +48,10 @@ public final class AssetIndex {
|
||||||
return virtual || mapToResources;
|
return virtual || mapToResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean needMapToResources() {
|
||||||
|
return mapToResources;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, AssetObject> getObjects() {
|
public Map<String, AssetObject> getObjects() {
|
||||||
return Collections.unmodifiableMap(objects);
|
return Collections.unmodifiableMap(objects);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,7 @@ import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.tungsten.fclcore.download.MaintainTask;
|
import com.tungsten.fclcore.download.MaintainTask;
|
||||||
import com.tungsten.fclcore.download.game.VersionJsonSaveTask;
|
import com.tungsten.fclcore.download.game.VersionJsonSaveTask;
|
||||||
import com.tungsten.fclcore.event.Event;
|
import com.tungsten.fclcore.event.*;
|
||||||
import com.tungsten.fclcore.event.EventBus;
|
|
||||||
import com.tungsten.fclcore.event.GameJsonParseFailedEvent;
|
|
||||||
import com.tungsten.fclcore.event.LoadedOneVersionEvent;
|
|
||||||
import com.tungsten.fclcore.event.RefreshedVersionsEvent;
|
|
||||||
import com.tungsten.fclcore.event.RefreshingVersionsEvent;
|
|
||||||
import com.tungsten.fclcore.event.RemoveVersionEvent;
|
|
||||||
import com.tungsten.fclcore.event.RenameVersionEvent;
|
|
||||||
import com.tungsten.fclcore.game.tlauncher.TLauncherVersion;
|
import com.tungsten.fclcore.game.tlauncher.TLauncherVersion;
|
||||||
import com.tungsten.fclcore.mod.ModManager;
|
import com.tungsten.fclcore.mod.ModManager;
|
||||||
import com.tungsten.fclcore.mod.ModpackConfiguration;
|
import com.tungsten.fclcore.mod.ModpackConfiguration;
|
||||||
|
@ -47,13 +40,7 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -125,9 +112,12 @@ public class DefaultGameRepository implements GameRepository {
|
||||||
@Override
|
@Override
|
||||||
public File getRunDirectory(String id) {
|
public File getRunDirectory(String id) {
|
||||||
switch (getGameDirectoryType(id)) {
|
switch (getGameDirectoryType(id)) {
|
||||||
case VERSION_FOLDER: return getVersionRoot(id);
|
case VERSION_FOLDER:
|
||||||
case ROOT_FOLDER: return getBaseDirectory();
|
return getVersionRoot(id);
|
||||||
default: throw new IllegalStateException();
|
case ROOT_FOLDER:
|
||||||
|
return getBaseDirectory();
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,6 +438,8 @@ public class DefaultGameRepository implements GameRepository {
|
||||||
return assetsDir;
|
return assetsDir;
|
||||||
|
|
||||||
if (index.isVirtual()) {
|
if (index.isVirtual()) {
|
||||||
|
Path resourcesDir = getRunDirectory(version).toPath().resolve("resources");
|
||||||
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
int tot = index.getObjects().entrySet().size();
|
int tot = index.getObjects().entrySet().size();
|
||||||
for (Map.Entry<String, AssetObject> entry : index.getObjects().entrySet()) {
|
for (Map.Entry<String, AssetObject> entry : index.getObjects().entrySet()) {
|
||||||
|
@ -457,6 +449,12 @@ public class DefaultGameRepository implements GameRepository {
|
||||||
cnt++;
|
cnt++;
|
||||||
if (!Files.isRegularFile(target))
|
if (!Files.isRegularFile(target))
|
||||||
FileUtils.copyFile(original, target);
|
FileUtils.copyFile(original, target);
|
||||||
|
|
||||||
|
if (index.needMapToResources()) {
|
||||||
|
target = resourcesDir.resolve(entry.getKey());
|
||||||
|
if (!Files.isRegularFile(target))
|
||||||
|
FileUtils.copyFile(original, target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,6 +487,7 @@ public class DefaultGameRepository implements GameRepository {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read modpack configuration for a version.
|
* read modpack configuration for a version.
|
||||||
|
*
|
||||||
* @param version version installed as modpack
|
* @param version version installed as modpack
|
||||||
* @param <M> manifest type of ModpackConfiguration
|
* @param <M> manifest type of ModpackConfiguration
|
||||||
* @return modpack configuration object, or null if this version is not a modpack.
|
* @return modpack configuration object, or null if this version is not a modpack.
|
||||||
|
@ -500,7 +499,8 @@ public class DefaultGameRepository implements GameRepository {
|
||||||
if (!hasVersion(version)) throw new VersionNotFoundException(version);
|
if (!hasVersion(version)) throw new VersionNotFoundException(version);
|
||||||
File file = getModpackConfiguration(version);
|
File file = getModpackConfiguration(version);
|
||||||
if (!file.exists()) return null;
|
if (!file.exists()) return null;
|
||||||
return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<M>>(){}.getType());
|
return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken<ModpackConfiguration<M>>() {
|
||||||
|
}.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isModpack(String version) {
|
public boolean isModpack(String version) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package com.tungsten.fclcore.game;
|
package com.tungsten.fclcore.game;
|
||||||
|
|
||||||
import static com.tungsten.fclcore.util.Lang.tryCast;
|
|
||||||
import static com.tungsten.fclcore.util.Logging.LOG;
|
import static com.tungsten.fclcore.util.Logging.LOG;
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
@ -47,8 +46,11 @@ public final class GameVersion {
|
||||||
private static Optional<String> getVersionFromJson(InputStream versionJson) {
|
private static Optional<String> getVersionFromJson(InputStream versionJson) {
|
||||||
try {
|
try {
|
||||||
Map<?, ?> version = JsonUtils.fromNonNullJsonFully(versionJson, Map.class);
|
Map<?, ?> version = JsonUtils.fromNonNullJsonFully(versionJson, Map.class);
|
||||||
return tryCast(version.get("id"), String.class);
|
String id = (String) version.get("id");
|
||||||
} catch (IOException | JsonParseException e) {
|
if (id != null && id.contains(" / "))
|
||||||
|
id = id.substring(0, id.indexOf(" / "));
|
||||||
|
return Optional.ofNullable(id);
|
||||||
|
} catch (IOException | JsonParseException | ClassCastException e) {
|
||||||
LOG.log(Level.WARNING, "Failed to parse version.json", e);
|
LOG.log(Level.WARNING, "Failed to parse version.json", e);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||||
break download;
|
break download;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> redirects = null;
|
||||||
try {
|
try {
|
||||||
beforeDownload(url);
|
beforeDownload(url);
|
||||||
|
|
||||||
|
@ -103,7 +104,9 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||||
if (checkETag) repository.injectConnection(conn);
|
if (checkETag) repository.injectConnection(conn);
|
||||||
|
|
||||||
if (conn instanceof HttpURLConnection) {
|
if (conn instanceof HttpURLConnection) {
|
||||||
conn = NetworkUtils.resolveConnection((HttpURLConnection) conn);
|
redirects = new ArrayList<>();
|
||||||
|
|
||||||
|
conn = NetworkUtils.resolveConnection((HttpURLConnection) conn, redirects);
|
||||||
int responseCode = ((HttpURLConnection) conn).getResponseCode();
|
int responseCode = ((HttpURLConnection) conn).getResponseCode();
|
||||||
|
|
||||||
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
|
@ -164,13 +167,13 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||||
} catch (FileNotFoundException ex) {
|
} catch (FileNotFoundException ex) {
|
||||||
failedURL = url;
|
failedURL = url;
|
||||||
exception = ex;
|
exception = ex;
|
||||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", not found", ex);
|
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", not found" + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
||||||
|
|
||||||
break; // we will not try this URL again
|
break; // we will not try this URL again
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
failedURL = url;
|
failedURL = url;
|
||||||
exception = ex;
|
exception = ex;
|
||||||
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + (++repeat), ex);
|
Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + (++repeat) + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,8 +294,12 @@ public class CacheRepository {
|
||||||
ETagIndex indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
|
ETagIndex indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class);
|
||||||
Map<String, ETagItem> newIndex = joinETagIndexes(indexOnDisk == null ? null : indexOnDisk.eTag, index.values());
|
Map<String, ETagItem> newIndex = joinETagIndexes(indexOnDisk == null ? null : indexOnDisk.eTag, index.values());
|
||||||
channel.truncate(0);
|
channel.truncate(0);
|
||||||
ETagIndex writeTo = new ETagIndex(newIndex.values());
|
ByteBuffer writeTo = ByteBuffer.wrap(JsonUtils.GSON.toJson(new ETagIndex(newIndex.values())).getBytes(UTF_8));
|
||||||
channel.write(ByteBuffer.wrap(JsonUtils.GSON.toJson(writeTo).getBytes(UTF_8)));
|
while (writeTo.hasRemaining()) {
|
||||||
|
if (channel.write(writeTo) == 0) {
|
||||||
|
throw new IOException("No value is written");
|
||||||
|
}
|
||||||
|
}
|
||||||
this.index = newIndex;
|
this.index = newIndex;
|
||||||
} finally {
|
} finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
|
|
|
@ -144,6 +144,10 @@ public final class NetworkUtils {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static HttpURLConnection resolveConnection(HttpURLConnection conn) throws IOException {
|
||||||
|
return resolveConnection(conn, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is a work-around that aims to solve problem when "Location" in
|
* This method is a work-around that aims to solve problem when "Location" in
|
||||||
* stupid server's response is not encoded.
|
* stupid server's response is not encoded.
|
||||||
|
@ -153,7 +157,7 @@ public final class NetworkUtils {
|
||||||
* @return manually redirected http connection.
|
* @return manually redirected http connection.
|
||||||
* @throws IOException if an I/O error occurs.
|
* @throws IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
public static HttpURLConnection resolveConnection(HttpURLConnection conn) throws IOException {
|
public static HttpURLConnection resolveConnection(HttpURLConnection conn, List<String> redirects) throws IOException {
|
||||||
int redirect = 0;
|
int redirect = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
|
@ -168,6 +172,9 @@ public final class NetworkUtils {
|
||||||
String newURL = conn.getHeaderField("Location");
|
String newURL = conn.getHeaderField("Location");
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
|
|
||||||
|
if (redirects != null) {
|
||||||
|
redirects.add(newURL);
|
||||||
|
}
|
||||||
if (redirect > 20) {
|
if (redirect > 20) {
|
||||||
throw new IOException("Too much redirects");
|
throw new IOException("Too much redirects");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "release",
|
"type": "release",
|
||||||
"versionCode": 1175,
|
"versionCode": 1176,
|
||||||
"versionName": "1.1.7.5",
|
"versionName": "1.1.7.6",
|
||||||
"date": "2024.08.16",
|
"date": "2024.08.23",
|
||||||
"description": [
|
"description": [
|
||||||
{
|
{
|
||||||
"lang": "en",
|
"lang": "en",
|
||||||
|
@ -11,10 +11,10 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"lang": "zh_CN",
|
"lang": "zh_CN",
|
||||||
"text": "FCL 1.1.7.5更新内容(若启动器内下载缓慢请使用网盘下载,64位的请下载arm64,不知道该下哪个的请下载all)\n1.更新赞助链接\n2.更新turnip驱动\n3.允许使用外部应用选择文件\n4.移除xhook改为bytehook\n5.修复少量bug"
|
"text": "FCL 1.1.7.6更新内容(若启动器内下载缓慢请使用网盘下载,64位的请下载arm64,不知道该下哪个的请下载all)\n1.修复微软登录时的错误提示\n2.修复neoforge整合包版本错误\n3.修复手动选择整合包时的整合包为非正常格式时无法安装的bug\n4.可手动选择执行jar文件使用的java\n5.同步部分HMCL Core,修复些许bug"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"netdiskUrl": "https://pan.quark.cn/s/b65dd99d5555",
|
"netdiskUrl": "https://pan.quark.cn/s/04bc07022a7d",
|
||||||
"url": "https://github.com/FCL-Team/FoldCraftLauncher/releases/download/1.1.7.5/FCL-release-1.1.7.5-all.apk"
|
"url": "https://github.com/FCL-Team/FoldCraftLauncher/releases/download/1.1.7.6/FCL-release-1.1.7.6-all.apk"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|