ModJava.vb 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. Public Module ModJava
  2. ''' <summary>
  3. ''' 初始化 Java 列表。
  4. ''' 若没有任何可用的 Java,则触发搜索。
  5. ''' </summary>
  6. Public Sub JavaInit()
  7. Try
  8. '从老版本迁移
  9. If Configs.JavaConfigVersion.Get() = 0 Then
  10. Try
  11. 'Java 列表
  12. Logger.Warn("正在从老版本迁移 Java 列表")
  13. Dim OldJavaList = Settings.Get(Of String)("LaunchArgumentJavaAll").DeserializeJson(Of JArray).
  14. Select(Function(JavaObject) New Java(JavaObject("Path").ToString)).ToList()
  15. OldJavaList = JavaUtils.SortAsync(OldJavaList).Run()
  16. Logger.Info($"已从老版本设置中发现 {OldJavaList.Count} 个 Java")
  17. '强制选择的 Java
  18. Dim SelectedJavaSetting = Settings.Get(Of String)("LaunchArgumentJavaSelect")
  19. Dim SelectedJava = If(SelectedJavaSetting.StartsWithF("{"), SelectedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString, "")
  20. Dim Match = OldJavaList.FirstOrDefault(Function(j) j.Folder.Equals(SelectedJava, StringComparison.OrdinalIgnoreCase))
  21. If Match IsNot Nothing Then
  22. OldJavaList.Remove(Match)
  23. OldJavaList.Insert(0, Match)
  24. Logger.Info($"已将老版本设置中强制选择的 Java 置顶:{Match}")
  25. End If
  26. Configs.JavaList.Set(New ConcurrentList(Of Java)(OldJavaList))
  27. '完成迁移
  28. Configs.JavaConfigVersion.Set(Versions.JavaConfigVersion)
  29. Catch ex As Exception
  30. Logger.Error(ex, "从老版本 PCL 迁移 Java 列表失败")
  31. End Try
  32. End If
  33. '读取缓存
  34. Dim List = Configs.JavaList.Get()
  35. If List.Any Then
  36. Logger.Info($"共有 {List.Count} 个可用的 Java:{vbCrLf}- {List.Select(Function(j) j.ToString).Join(vbCrLf + "- ")}")
  37. Return
  38. End If
  39. '如果没有 Java,则触发搜索
  40. Logger.Warn("初始化未找到可用的 Java,将自动触发搜索")
  41. JavaListRefreshWorker.Start()
  42. Catch ex As Exception
  43. Logger.Error(ex, "初始化 Java 列表失败")
  44. Configs.JavaList.Reset()
  45. End Try
  46. End Sub
  47. ''' <summary>
  48. ''' 搜索并获取所有可用的 Java,检查所有 Java 的可用性,写入设置,并在结束后更新设置页面显示。
  49. ''' </summary>
  50. Public WithEvents JavaListRefreshWorker As New RedoableWorker(Sub(c, p) JavaUtils.RefreshListAsync(c, p).Run())
  51. #Region "UI 事件"
  52. ''' <summary>
  53. ''' 更新设置页面的 Java 列表显示。
  54. ''' </summary>
  55. Public Sub UpdateJavaLists() Handles JavaListRefreshWorker.Started, JavaListRefreshWorker.Stopped
  56. RunInUi(
  57. Sub()
  58. FrmSetupLaunch?.UpdateJavaList()
  59. FrmInstanceSetup?.UpdateJavaList()
  60. End Sub)
  61. End Sub
  62. ''' <summary>
  63. ''' 手动触发导入 Java。
  64. ''' </summary>
  65. Public Sub ManuallyImportJava()
  66. '选择
  67. Dim NewJavaPath As String = Dialogs.SelectFile("选择 java.exe(可能在 bin 文件夹中)", False, filter:="java.exe|java.exe").FirstOrDefault()
  68. If String.IsNullOrEmpty(NewJavaPath) Then Return
  69. '验证 Java 可用
  70. Dim NewJava As New Java(PathUtils.RemoveLastPart(NewJavaPath))
  71. If Not NewJava.CheckAsync().Run() Then
  72. Hint("该 Java 存在异常,无法使用", HintType.Red)
  73. Return
  74. End If
  75. '不再移除它
  76. Configs.JavaRemovedList.Edit(Sub(ByRef List) List.RemoveIf(Function(f) f.Equals(NewJava.Folder, StringComparison.OrdinalIgnoreCase)))
  77. '加入列表顶部
  78. Configs.JavaList.Edit(
  79. Sub(ByRef List)
  80. List.Remove(NewJava)
  81. List.Insert(0, NewJava)
  82. End Sub)
  83. Hint($"已将 Java 加入列表:{NewJavaPath}", HintType.Green)
  84. UpdateJavaLists()
  85. End Sub
  86. ''' <summary>
  87. ''' 手动从 Java 列表中移除指定的 Java,并记录到已移除列表中。
  88. ''' </summary>
  89. Public Sub ManuallyRemoveJava(JavaEntry As Java)
  90. Configs.JavaList.Edit(Sub(ByRef List) List.Remove(JavaEntry)) '从 Java 列表中移除
  91. Configs.JavaRemovedList.Edit(Function(List) List.Append(JavaEntry.Folder).DistinctBy(Function(s) s.Lower()).ToList()) '记录已移除
  92. Hint($"已将 Java 移除出列表:{JavaEntry}", HintType.Green)
  93. UpdateJavaLists()
  94. PageInstanceLeft.ReloadCurrentJava() '如果强制指定了该 Java,这会把它自动重置掉
  95. End Sub
  96. ''' <summary>
  97. ''' 手动触发搜索 Java,强制检查所有 Java。
  98. ''' </summary>
  99. Public Sub ManuallySearchJava(Optional CancellationToken As CancellationToken = Nothing)
  100. RunInThread(
  101. Sub()
  102. Configs.JavaList.Get().ForAll(Sub(java) java.InvalidateChecked()) '重置已检查的状态
  103. JavaListRefreshWorker.Start(cancellationToken:=CancellationToken)
  104. JavaListRefreshWorker.WaitIfRunningAsync(cancellationToken:=CancellationToken).Run() 'worker 开始/结束时会自动重载 UI
  105. If Configs.JavaList.Get().Any Then
  106. Hint("已找到 " & Configs.JavaList.Get().Count & " 个 Java!", HintType.Green)
  107. Else
  108. Hint("未找到可用的 Java!", HintType.Red)
  109. End If
  110. End Sub)
  111. End Sub
  112. #End Region
  113. #Region "选取"
  114. ''' <summary>
  115. ''' 分析指定 Minecraft 版本的基础 Java 版本要求。不受设置影响。
  116. ''' 如果由于 Java 版本要求冲突而导致没有可用的 Java,则返回 null。
  117. ''' </summary>
  118. Public Function GetJavaRequirement(Instance As McInstance) As (Range As ValueRange(Of Version), RecommendedComponent As String)
  119. '版本设置已经强制指定了 Java 版本要求
  120. If Settings.Get(Of Integer)("VersionArgumentJavaV2", Instance) = 1 Then
  121. Try
  122. Dim Range = ValueRange(Of Version).FromString(Settings.Get(Of String)("VersionArgumentJavaRange", Instance), Function(s) StringUtils.ParseVersionWithDefaults(s))
  123. Return (Range, Nothing)
  124. Catch ex As Exception
  125. Logger.Warn($"版本设置中指定的 Java 版本范围有误({Settings.Get(Of String)("VersionArgumentJavaRange", Instance)}),这可能是因为首次修改此设置,尚未初始化")
  126. End Try
  127. End If
  128. '----------------------------------- 从 Minecraft 版本信息获取需求 -----------------------------------
  129. Dim JavaRange = ValueRange(Of Version).All()
  130. Dim AddConstraint =
  131. Sub(Constraint As ValueRange(Of Version))
  132. JavaRange = JavaRange?.Intersect(Constraint)
  133. If JavaRange.IsEmpty Then
  134. Logger.Warn($"Java 版本要求冲突,若启动失败请在版本设置手动指定 Java:当前追加的要求为 {Constraint},游戏版本为 {Instance.VersionDisplayName}", LogBehavior.Toast)
  135. JavaRange = Constraint
  136. End If
  137. End Sub
  138. '原版 26-:通过版本号判断
  139. If (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse
  140. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then
  141. '1.20.5+(24w14a+):至少 Java 21
  142. AddConstraint(ValueRange(Of Version).AtLeast(New Version(21, 0)))
  143. ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse
  144. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 18) Then
  145. '1.18 pre2+:至少 Java 17
  146. AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
  147. ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse
  148. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 17) Then
  149. '1.17+ (21w19a+):至少 Java 16
  150. AddConstraint(ValueRange(Of Version).AtLeast(New Version(16, 0)))
  151. ElseIf Instance.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了
  152. '1.12+:至少 Java 8
  153. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
  154. ElseIf Instance.ReleaseTime <= New Date(2013, 5, 1) AndAlso Instance.ReleaseTime.Year >= 2001 Then '避免某些版本写个 1960 年
  155. '1.5.2-:最高 Java 8
  156. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  157. End If
  158. '原版 26+:获取 Mojang 要求的 Java 版本
  159. Dim RecommendedComponent As String = Nothing
  160. Dim RecommendedCode As Integer =
  161. If(Instance.JsonObject?("javaVersion")?("majorVersion")?.ToObject(Of Integer),
  162. If(Instance.JsonVersion?("java_version")?.ToObject(Of Integer), 0))
  163. If RecommendedCode >= 22 Then
  164. Logger.Info("Mojang 要求至少使用 Java " & RecommendedCode)
  165. AddConstraint(ValueRange(Of Version).AtLeast(New Version(RecommendedCode, 0)))
  166. RecommendedComponent =
  167. If(Instance.JsonObject?("javaVersion")?("component")?.ToString,
  168. Instance.JsonVersion?("java_component")?.ToString)
  169. If RecommendedComponent = "" Then RecommendedComponent = Nothing
  170. End If
  171. 'OptiFine 检测
  172. If Instance.Version.HasOptiFine AndAlso Instance.Version.Vaild Then '不管非标准版本
  173. If Instance.Version.Vanilla.Major < 7 Then
  174. '<1.7:至多 Java 8
  175. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  176. ElseIf Instance.Version.Vanilla.Major >= 8 AndAlso Instance.Version.Vanilla.Major < 12 Then
  177. '1.8 - 1.11:必须恰好 Java 8
  178. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(9, 0)))
  179. ElseIf Instance.Version.Vanilla.Major = 12 Then
  180. '1.12:最高 Java 8
  181. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  182. End If
  183. End If
  184. 'Forge 检测(部分测试结果见 #8432)
  185. If Instance.Version.HasForge Then
  186. If Instance.Version.Vanilla >= New Version(6, 0, 1) AndAlso Instance.Version.Vanilla <= New Version(7, 0, 2) Then
  187. '1.6.1 - 1.7.2:必须 Java 7
  188. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(7, 0), New Version(8, 0)))
  189. ElseIf Instance.Version.Vanilla.Major <= 12 OrElse Not Instance.Version.Vaild Then '非标准版本
  190. '<=1.12:Java 8
  191. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  192. ElseIf Instance.Version.Vanilla.Major <= 14 Then
  193. '1.13 - 1.14:Java 8 - 10
  194. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(11, 0)))
  195. ElseIf Instance.Version.Vanilla.Major = 15 Then
  196. '1.15:Java 8 - 15
  197. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(16, 0)))
  198. ElseIf CompareVersionGE(Instance.Version.Forge, "34.0.0") AndAlso CompareVersionGE("36.2.25", Instance.Version.Forge) Then
  199. '1.16.3~5,Forge 34.0.0 ~ 36.2.25:最高 Java 8u320
  200. AddConstraint(ValueRange(Of Version).AtMost(New Version(8, 0, 320)))
  201. ElseIf CompareVersionGE(Instance.Version.Forge, "36.2.26") AndAlso CompareVersionGE("36.999999.999999", Instance.Version.Forge) Then
  202. '1.16.5,Forge 36.2.26+,<37.0.0:最高 Java 23
  203. AddConstraint(ValueRange(Of Version).LessThan(New Version(24, 0)))
  204. ElseIf CompareVersionGE(Instance.Version.Forge, "37.0.0") AndAlso CompareVersionGE("37.0.79", Instance.Version.Forge) Then
  205. '1.17.1,Forge 37.0.0 ~ 37.0.79:最高 Java 16
  206. AddConstraint(ValueRange(Of Version).LessThan(New Version(17, 0)))
  207. ElseIf Instance.Version.Vanilla.Major = 18 AndAlso Instance.Version.HasOptiFine Then '#305
  208. '1.18:若安装了 OptiFine,最高 Java 18
  209. AddConstraint(ValueRange(Of Version).LessThan(New Version(19, 0)))
  210. ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.21") AndAlso CompareVersionGE("45.0.65", Instance.Version.Forge) Then
  211. '1.19.4,Forge 45.0.21 ~ 45.0.65:最高 Java 19
  212. AddConstraint(ValueRange(Of Version).LessThan(New Version(20, 0)))
  213. ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.66") AndAlso CompareVersionGE("47.4.8", Instance.Version.Forge) Then
  214. '1.19.4~1.20.1,Forge 45.0.66 ~ 47.4.8:最高 Java 21
  215. AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
  216. End If
  217. End If
  218. 'NeoForge 检测
  219. If Instance.Version.HasNeoForge Then
  220. If Instance.Version.Vanilla = New Version(20, 0, 1) OrElse
  221. (CompareVersionGE("20.2.62-beta", Instance.Version.NeoForge) AndAlso Not Instance.Version.NeoForge.Contains("25w14craftmine")) Then
  222. '1.20.1,以及 1.20.2 的 20.2.62-beta 之前:最高 Java 21
  223. AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
  224. End If
  225. End If
  226. 'Fabric 检测
  227. If Instance.Version.HasFabric AndAlso Instance.Version.Vaild Then '不管非标准版本
  228. If Instance.Version.Vanilla.Major >= 15 AndAlso Instance.Version.Vanilla.Major <= 16 Then
  229. '1.15 - 1.16:Java 8+
  230. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
  231. ElseIf Instance.Version.Vanilla.Major >= 18 Then
  232. '1.18+:Java 17+
  233. AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
  234. End If
  235. If Instance.Version.Fabric <> "未知版本" AndAlso
  236. CompareVersion(Instance.Version.Fabric, "0.17.0") < 0 Then
  237. 'Fabric Loader 0.16.x 及更早版本的 Mixin/ASM 不兼容 Java 25
  238. AddConstraint(ValueRange(Of Version).LessThan(New Version(25, 0)))
  239. End If
  240. End If
  241. 'LiteLoader 检测
  242. If Instance.Version.HasLiteLoader AndAlso Instance.Version.Vaild Then '不管非标准版本
  243. '最高 Java 8
  244. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  245. End If
  246. '统一通行证检测
  247. If Settings.Get(Of McLoginType)("LoginType") = McLoginType.Nide Then
  248. '至少 Java 8u101
  249. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0, 141)))
  250. End If
  251. Return (JavaRange, RecommendedComponent)
  252. End Function
  253. ''' <summary>
  254. ''' 根据版本或 Java 范围要求寻找适合的 Java。
  255. ''' 若指定了 <paramref name="TryFixOrHint"/>,则会在找不到时会尝试下载 Java 或提示用户下载,否则只会静默运行。
  256. ''' </summary>
  257. ''' <returns>最适合的 Java。若找不到或失败则返回 null,除非取消否则不会抛出异常。</returns>
  258. Public Function SelectOrDownloadJava(Target As OneOf(Of ValueRange(Of Version), McInstance), TryFixOrHint As Boolean, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing) As Java
  259. Try
  260. If Target.Is(Of McInstance) Then MigrateInstanceJavaSettings(Target.As(Of McInstance), c, p?.SplitTo(0.2)) '迁移
  261. Dim Instance As McInstance = If(Target.Is(Of McInstance), Target.As(Of McInstance), Nothing)
  262. Select Case If(Instance Is Nothing, 0, Settings.Get(Of Integer)("VersionArgumentJavaV2", Instance))
  263. Case 2 '============================= 使用版本文件夹中的 Java =============================
  264. '查找版本文件夹下的 Java
  265. Logger.Info($"版本设置要求使用版本文件夹中的 Java:{Instance.PathVersion}")
  266. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run().FirstOrDefault()
  267. If JavaFound IsNot Nothing Then
  268. Logger.Info($"已发现版本文件夹中的 Java:{JavaFound}")
  269. Return JavaFound
  270. End If
  271. '未能找到
  272. If TryFixOrHint Then
  273. SwitchToInstanceSetup(Instance)
  274. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用版本文件夹中的 Java{vbRQ},但版本文件夹里没能找到 Java。{vbCrLf}请先修改启动选项中的 Java 设置,或在版本文件夹中放一个 Java,然后再试。",
  275. "未找到 Java", IsWarn:=True, ForceWait:=True)
  276. End If
  277. Return Nothing
  278. Case 3 '=============================== 强制指定特定 Java ===============================
  279. Dim ChosenJava = Configs.JavaForced.Get(Instance.Config)
  280. Logger.Info($"版本设置中强制指定的 Java:{Instance.PathVersion} → {ChosenJava}")
  281. If ChosenJava Is Nothing Then
  282. If TryFixOrHint Then
  283. SwitchToInstanceSetup(Instance)
  284. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用指定的 Java{vbRQ},但还没有指定任何 Java。{vbCrLf}请先选择你想使用的 Java,然后再试。",
  285. "未选择 Java", IsWarn:=True, ForceWait:=True)
  286. End If
  287. p?.Skip() : Return Nothing '<==== 设置中未指定 Java
  288. End If
  289. If Not Configs.JavaList.Get().Contains(ChosenJava) Then
  290. Configs.JavaForced.Reset(Instance.Config)
  291. UpdateJavaLists()
  292. If TryFixOrHint Then
  293. SwitchToInstanceSetup(Instance)
  294. Logger.Error("版本设置中指定的 Java 不在 Java 列表中,请重新选择一个 Java!", LogBehavior.Alert)
  295. End If
  296. p?.Skip() : Return Nothing '<==== 设置中指定的 Java 不在列表中
  297. End If
  298. If Not ChosenJava.CheckAsync(c).Run() Then
  299. Configs.JavaForced.Reset(Instance.Config)
  300. UpdateJavaLists()
  301. If TryFixOrHint Then
  302. SwitchToInstanceSetup(Instance)
  303. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java,然后再试。", LogBehavior.Alert)
  304. Else
  305. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java!", LogBehavior.Toast)
  306. End If
  307. p?.Finish() : Return Nothing '<==== 指定的 Java 存在异常
  308. End If
  309. p?.Finish() : Return ChosenJava '<==== 成功找到并检查通过
  310. Case Else '======================== 根据版本范围自动选择 JavaList 中的 Java ========================
  311. '获取范围
  312. Dim Range As ValueRange(Of Version) =
  313. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).Range, Target.As(Of ValueRange(Of Version)))
  314. Dim RecommendedComponent As String =
  315. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).RecommendedComponent, Nothing)
  316. Logger.Info($"自动选择以下范围的 Java:{Range} {RecommendedComponent}")
  317. If Range.IsEmpty Then Throw New Exception("Java 版本需求存在冲突,导致没有任何可能适配的 Java,请查看 PCL 的日志了解详细信息!")
  318. '搜索 Java
  319. Dim SelectJavaFromJavaList =
  320. Function() As Java
  321. JavaListRefreshWorker.WaitIfRunning() '等待进行中的搜索结束
  322. For Each ChosenJava In Configs.JavaList.Get()
  323. If Not ChosenJava.CheckAsync(c).Run() Then '检查
  324. Logger.Warn($"Java 检查失败,将尝试列表中的下一个 Java:{ChosenJava}")
  325. Continue For
  326. End If
  327. If ChosenJava.Version Is Nothing OrElse Not Range.Contains(ChosenJava.Version) Then Continue For '版本范围
  328. Logger.Info($"已选定 Java:{ChosenJava}")
  329. Return ChosenJava
  330. Next
  331. Return Nothing
  332. End Function
  333. p?.Set(0.25)
  334. Dim Result = SelectJavaFromJavaList()
  335. If Result IsNot Nothing Then p?.Skip() : Return Result
  336. SyncLock JavaLock '#3797
  337. '既有列表中没有合适的 Java,重新触发搜索
  338. Logger.Warn($"既有列表中没有合适的 Java,重新触发搜索")
  339. JavaListRefreshWorker.Start(c, p?.SplitTo(0.3))
  340. Result = SelectJavaFromJavaList()
  341. If Result IsNot Nothing Then p?.Skip() : Return Result
  342. If Not TryFixOrHint Then Return Nothing
  343. '电脑中没有合适的 Java,获取 Mojang 提供的 Java 下载列表
  344. Logger.Warn($"电脑中没有合适的 Java,开始获取 Mojang 提供的 Java 下载列表")
  345. Static DownloadListWorker As New RedoableWorker(Of List(Of JavaDownloadInfo))(
  346. Function(cancellationToken)
  347. '下载 Mojang 提供的 Java 列表
  348. Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
  349. {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
  350. {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
  351. ), IsJson:=True)
  352. cancellationToken?.ThrowIfCancellationRequested()
  353. '解析 Java 列表
  354. Dim Results = New List(Of JavaDownloadInfo)
  355. For Each Prop As JProperty In IndexFileStr.DeserializeJson(Of JObject)()("windows-x64")
  356. cancellationToken?.ThrowIfCancellationRequested()
  357. If Prop.Name = "minecraft-java-exe" Then Continue For '不是完整 Java
  358. Try
  359. Dim Entry = Prop.Value.FirstOrDefault
  360. If Entry Is Nothing Then Continue For
  361. Dim JavaName = Entry("version")("name").ToString '可能的名称格式:"12"、"8u51-cacert462b08"、"17.0.8.1"、"17.0.1.12.1"、"21.0.7"
  362. If JavaName.Contains(".") Then
  363. Dim Parts As String() = JavaName.Split(".")
  364. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(Parts(0)), Val(Parts(1)), If(Parts.Length >= 3, Val(Parts(2)), 0), If(Parts.Length >= 4, Val(Parts(3)), 0))})
  365. ElseIf JavaName.StartsWithF("8u51-") Then
  366. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(8, 0, 51, 0)})
  367. Else
  368. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(JavaName.BeforeFirstOfAny({"u", "."})), 0, 0, 0)})
  369. End If
  370. Catch ex As Exception
  371. Logger.Warn(ex, $"解析 Mojang 提供的 Java 列表项失败,已跳过({Prop.Name})")
  372. End Try
  373. Next
  374. Return Results
  375. End Function)
  376. If Not DownloadListWorker.HasSucceeded Then DownloadListWorker.Start(c, p?.SplitTo(0.4))
  377. DownloadListWorker.WaitIfRunning(cancellationToken:=c)
  378. p?.Set(0.4, skiped:=True)
  379. Dim Components = DownloadListWorker.LastResult
  380. '确定要下载的目标 Java
  381. Dim JavaToDownload As JavaDownloadInfo = If(
  382. Components.FirstOrDefault(Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent), '优先选择指定的 Component
  383. Components.FirstOrDefault(Function(Comp) Range.Contains(Comp.JavaVersion)))
  384. If JavaToDownload Is Nothing Then
  385. Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载")
  386. '提示需要手动下载 Java
  387. Dim Description As String
  388. Dim HasPureLower As Boolean = Range.HasLower AndAlso Range.IsLowerInclusive AndAlso
  389. Range.Lower.Minor <= 0 AndAlso Range.Lower.Build <= 0 AndAlso Range.Lower.Revision <= 0
  390. Dim HasPureUpper As Boolean = Range.HasUpper AndAlso Not Range.IsUpperInclusive AndAlso
  391. Range.Upper.Minor <= 0 AndAlso Range.Upper.Build <= 0 AndAlso Range.Upper.Revision <= 0
  392. If Range.HasLower AndAlso Range.HasUpper Then '有上下界
  393. If HasPureLower AndAlso HasPureUpper Then
  394. If Range.Lower.Major = Range.Upper.Major - 1 Then
  395. Description = $" Java {Range.Lower.Major}"
  396. Else
  397. Description = $" Java {Range.Lower.Major} ~ {Range.Upper.Major - 1}"
  398. End If
  399. Else
  400. Description = $" Java {Range}" '摆烂
  401. End If
  402. ElseIf Range.HasLower Then '只有下界
  403. If HasPureLower Then
  404. Description = $" Java {Range.Lower.Major} 或更高版本的 Java"
  405. ElseIf Range.IsLowerInclusive Then
  406. Description = $" Java {Range.Lower} 或更高版本的 Java"
  407. Else
  408. Description = $"版本高于 {Range.Lower} 的 Java"
  409. End If
  410. Else '只有上界
  411. If HasPureUpper Then
  412. Description = $" Java {Range.Upper.Major - 1} 或更低版本的 Java"
  413. ElseIf Range.IsUpperInclusive Then
  414. Description = $" Java {Range.Upper} 或更低版本的 Java"
  415. Else
  416. Description = $"版本低于 {Range.Upper} 的 Java"
  417. End If
  418. End If
  419. MyMsgBox($"PCL 没能找到合适的 Java。{vbCrLf}请在网上搜索并手动安装{Description}, 然后在【设置 → 启动 → 高级选项 → Java 列表】中导入它。", "需要手动安装 Java")
  420. Return Nothing
  421. End If
  422. '自动下载 Java
  423. Dim JavaLoader = CreateJavaDownloadLoader(JavaToDownload)
  424. Try
  425. JavaLoader.WaitForExit(IsForceRestart:=True, c:=c, p:=p?.SplitTo(0.95))
  426. Finally
  427. JavaLoader.Cancel() '确保取消时中止 Java 下载
  428. End Try
  429. Result = SelectJavaFromJavaList()
  430. If Result IsNot Nothing Then p?.Finish() : Return Result
  431. Throw New Exception($"下载 Java {JavaToDownload.ComponentName} 后仍然无法找到合适的 Java")
  432. End SyncLock
  433. End Select
  434. Catch ex As Exception
  435. ex.ThrowIfCanceled()
  436. Logger.Error(ex, "选择 Java 时出现异常")
  437. p?.Skip() : Return Nothing
  438. End Try
  439. End Function
  440. Private Sub SwitchToInstanceSetup(Instance As McInstance)
  441. RunInUi(
  442. Sub()
  443. PageInstanceLeft.Instance = Instance
  444. FrmMain.PageChange(FormMain.PageType.InstanceSetup, FormMain.PageSubType.InstanceSetup)
  445. End Sub)
  446. End Sub
  447. Private ReadOnly JavaLock As New Object
  448. ''' <summary>
  449. ''' 从老版本 PCL 迁移版本独立的 Java 设置。
  450. ''' </summary>
  451. Private Sub MigrateInstanceJavaSettings(Instance As McInstance, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing)
  452. If Configs.JavaMigrated.Get(Instance.Config) Then p?.Skip() : Return
  453. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(Sub() FrmInstanceSetup.ComboArgumentJava.IsEnabled = False)
  454. Try
  455. Logger.Info($"正在尝试迁移版本独立的 Java 设置({Instance.Name})")
  456. '强制使用指定 Java
  457. Dim ForcedJavaSetting = Settings.Get(Of String)("VersionArgumentJavaSelect", Instance)
  458. If Not ForcedJavaSetting.StartsWithF("{") Then
  459. p?.Set(0.4, True) : GoTo NoForcedJava
  460. End If
  461. Dim ForcedJava = New Java(ForcedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString)
  462. If Not ForcedJava.CheckAsync(c).Run() Then
  463. Hint("该版本的设置中强制指定的 Java 存在异常,该设置已重置", HintType.Red)
  464. p?.Set(0.4) : GoTo NoForcedJava
  465. End If
  466. Logger.Info($"将版本独立设置改为老版本中强制指定的 Java:{ForcedJava}")
  467. If Not Configs.JavaList.Get().Contains(ForcedJava) Then
  468. Configs.JavaList.Edit(Sub(ByRef List) List.Add(ForcedJava)) '加入列表底部
  469. Logger.Info($"已将强制指定的 Java 加入 Java 列表底部:{ForcedJava}")
  470. End If
  471. Configs.JavaForced.Set(ForcedJava, Instance.Config)
  472. Settings.Set("VersionArgumentJavaV2", 3, Instance)
  473. UpdateJavaLists()
  474. Configs.JavaMigrated.Set(True, Instance.Config) '完成
  475. p?.Finish() : Return
  476. NoForcedJava:
  477. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run()
  478. If JavaFound.Any() Then
  479. '从版本文件夹中发现了 Java
  480. Logger.Info($"在版本文件夹中发现了 Java,将版本独立设置改为优先使用版本文件夹的 Java:{JavaFound}")
  481. Settings.Set("VersionArgumentJavaV2", 2, Instance)
  482. Else
  483. '自动选择
  484. Logger.Info("没有需要迁移的内容,自动选择 Java")
  485. Settings.Set("VersionArgumentJavaV2", 0, Instance)
  486. End If
  487. Configs.JavaMigrated.Set(True, Instance.Config) '完成
  488. Finally
  489. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(
  490. Sub()
  491. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJava)
  492. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJavaSelect)
  493. FrmInstanceSetup.ComboArgumentJava.IsEnabled = True
  494. End Sub)
  495. End Try
  496. End Sub
  497. #End Region
  498. #Region "下载(均为内部工具方法,无外部调用)"
  499. Private Class JavaDownloadInfo
  500. Public Property ComponentName As String
  501. Public Property Java As JObject
  502. Public Property JavaVersion As Version
  503. End Class
  504. ''' <summary>
  505. ''' 构建下载指定 Java 的加载器。
  506. ''' </summary>
  507. Private Function CreateJavaDownloadLoader(Target As JavaDownloadInfo) As LoaderCombo(Of Integer)
  508. '分析需要下载的 Java 文件列表
  509. Dim Address As String = Target.Java("manifest")("url")
  510. Logger.Info($"准备下载 Java {Target.Java("version")("name")}({Target.ComponentName}):{Address}")
  511. Dim ListFileStr As String = NetRequestByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
  512. Dim BaseDir As String = $"{Paths.AppData}.minecraft\runtime\{Target.ComponentName}\"
  513. Dim FilesToDownload As New List(Of NetFile)
  514. For Each File As JProperty In CType(ListFileStr.DeserializeJson(), JObject)("files")
  515. If CType(File.Value, JObject)("downloads")?("raw") Is Nothing Then Continue For
  516. Dim Info As JObject = CType(File.Value, JObject)("downloads")("raw")
  517. Dim Checker As New FileChecker With {.ActualSize = Info("size"), .Hash = Info("sha1")}
  518. If Checker.Hash = "12976a6c2b227cbac58969c1455444596c894656" OrElse Checker.Hash = "c80e4bab46e34d02826eab226a4441d0970f2aba" OrElse Checker.Hash = "84d2102ad171863db04e7ee22a259d1f6c5de4a5" Then
  519. Continue For '跳过 3 个无意义大量重复文件(#3827)
  520. End If
  521. Dim Url As String = Info("url")
  522. FilesToDownload.Add(New NetFile(
  523. DlSourceOrder({Url}, {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com")}),
  524. BaseDir & File.Name, Checker))
  525. Next
  526. Logger.Info($"该 Java 需要下载 {FilesToDownload.Count} 个文件,目标文件夹:{BaseDir}")
  527. '构建加载器
  528. Dim Loader = New LoaderCombo(Of Integer)("下载 Java", {
  529. New LoaderDownload("下载 Java 文件", FilesToDownload) With {.ProgressWeight = 10},
  530. New LoaderWorker("刷新 Java 列表", JavaListRefreshWorker)
  531. }) With {.OnStateChanged =
  532. Sub(RawLoader As LoaderCombo(Of Integer))
  533. If RawLoader.State = LoadState.Failed OrElse RawLoader.State = LoadState.Canceled Then
  534. Logger.Warn($"由于 Java 下载未完成,清理未下载完成的 Java 文件夹:{BaseDir}")
  535. DirectoryUtils.Delete(BaseDir)
  536. End If
  537. End Sub}
  538. Return Loader
  539. End Function
  540. #End Region
  541. End Module