Merge branch 'main' into pojav

This commit is contained in:
ShirosakiMio 2024-08-24 18:19:22 +08:00
commit b2508d1702
47 changed files with 400 additions and 220 deletions

View File

@ -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.
遇到问题后,请在启动器设置页面中单击“导出启动器日志”,并将导出的日志上传到下面的输入字段。

View File

@ -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

View File

@ -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"
} }

View File

@ -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

View File

@ -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);
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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>

View File

@ -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>

View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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);
} }

View File

@ -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();

View File

@ -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();

View File

@ -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;
} }

View File

@ -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";
} }

View File

@ -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;
}
}
}

View File

@ -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;
}
}
} }

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
} }

View File

@ -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) {

View File

@ -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();
} }

View File

@ -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);
} }
} }
} }

View File

@ -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();

View File

@ -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");
} }

View File

@ -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"
} }
] ]