2
0

ModJava.vb 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  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. End If
  236. 'LiteLoader 检测
  237. If Instance.Version.HasLiteLoader AndAlso Instance.Version.Vaild Then '不管非标准版本
  238. '最高 Java 8
  239. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  240. End If
  241. '统一通行证检测
  242. If Settings.Get(Of McLoginType)("LoginType") = McLoginType.Nide Then
  243. '至少 Java 8u101
  244. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0, 141)))
  245. End If
  246. Return (JavaRange, RecommendedComponent)
  247. End Function
  248. ''' <summary>
  249. ''' 根据版本或 Java 范围要求寻找适合的 Java。
  250. ''' 若指定了 <paramref name="TryFixOrHint"/>,则会在找不到时会尝试下载 Java 或提示用户下载,否则只会静默运行。
  251. ''' </summary>
  252. ''' <returns>最适合的 Java。若找不到或失败则返回 null,除非取消否则不会抛出异常。</returns>
  253. 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
  254. Try
  255. If Target.Is(Of McInstance) Then MigrateInstanceJavaSettings(Target.As(Of McInstance), c, p?.SplitTo(0.2)) '迁移
  256. Dim Instance As McInstance = If(Target.Is(Of McInstance), Target.As(Of McInstance), Nothing)
  257. Select Case If(Instance Is Nothing, 0, Settings.Get(Of Integer)("VersionArgumentJavaV2", Instance))
  258. Case 2 '============================= 使用版本文件夹中的 Java =============================
  259. '查找版本文件夹下的 Java
  260. Logger.Info($"版本设置要求使用版本文件夹中的 Java:{Instance.PathVersion}")
  261. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run().FirstOrDefault()
  262. If JavaFound IsNot Nothing Then
  263. Logger.Info($"已发现版本文件夹中的 Java:{JavaFound}")
  264. Return JavaFound
  265. End If
  266. '未能找到
  267. If TryFixOrHint Then
  268. SwitchToInstanceSetup(Instance)
  269. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用版本文件夹中的 Java{vbRQ},但版本文件夹里没能找到 Java。{vbCrLf}请先修改启动选项中的 Java 设置,或在版本文件夹中放一个 Java,然后再试。",
  270. "未找到 Java", IsWarn:=True, ForceWait:=True)
  271. End If
  272. Return Nothing
  273. Case 3 '=============================== 强制指定特定 Java ===============================
  274. Dim ChosenJava = Configs.JavaForced.Get(Instance.Config)
  275. Logger.Info($"版本设置中强制指定的 Java:{Instance.PathVersion} → {ChosenJava}")
  276. If ChosenJava Is Nothing Then
  277. If TryFixOrHint Then
  278. SwitchToInstanceSetup(Instance)
  279. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用指定的 Java{vbRQ},但还没有指定任何 Java。{vbCrLf}请先选择你想使用的 Java,然后再试。",
  280. "未选择 Java", IsWarn:=True, ForceWait:=True)
  281. End If
  282. p?.Skip() : Return Nothing '<==== 设置中未指定 Java
  283. End If
  284. If Not Configs.JavaList.Get().Contains(ChosenJava) Then
  285. Configs.JavaForced.Reset(Instance.Config)
  286. UpdateJavaLists()
  287. If TryFixOrHint Then
  288. SwitchToInstanceSetup(Instance)
  289. Logger.Error("版本设置中指定的 Java 不在 Java 列表中,请重新选择一个 Java!", LogBehavior.Alert)
  290. End If
  291. p?.Skip() : Return Nothing '<==== 设置中指定的 Java 不在列表中
  292. End If
  293. If Not ChosenJava.CheckAsync(c).Run() Then
  294. Configs.JavaForced.Reset(Instance.Config)
  295. UpdateJavaLists()
  296. If TryFixOrHint Then
  297. SwitchToInstanceSetup(Instance)
  298. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java,然后再试。", LogBehavior.Alert)
  299. Else
  300. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java!", LogBehavior.Toast)
  301. End If
  302. p?.Finish() : Return Nothing '<==== 指定的 Java 存在异常
  303. End If
  304. p?.Finish() : Return ChosenJava '<==== 成功找到并检查通过
  305. Case Else '======================== 根据版本范围自动选择 JavaList 中的 Java ========================
  306. '获取范围
  307. Dim Range As ValueRange(Of Version) =
  308. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).Range, Target.As(Of ValueRange(Of Version)))
  309. Dim RecommendedComponent As String =
  310. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).RecommendedComponent, Nothing)
  311. Logger.Info($"自动选择以下范围的 Java:{Range} {RecommendedComponent}")
  312. If Range.IsEmpty Then Throw New Exception("Java 版本需求存在冲突,导致没有任何可能适配的 Java,请查看 PCL 的日志了解详细信息!")
  313. '搜索 Java
  314. Dim SelectJavaFromJavaList =
  315. Function() As Java
  316. JavaListRefreshWorker.WaitIfRunning() '等待进行中的搜索结束
  317. For Each ChosenJava In Configs.JavaList.Get()
  318. If Not ChosenJava.CheckAsync(c).Run() Then '检查
  319. Logger.Warn($"Java 检查失败,将尝试列表中的下一个 Java:{ChosenJava}")
  320. Continue For
  321. End If
  322. If ChosenJava.Version Is Nothing OrElse Not Range.Contains(ChosenJava.Version) Then Continue For '版本范围
  323. Logger.Info($"已选定 Java:{ChosenJava}")
  324. Return ChosenJava
  325. Next
  326. Return Nothing
  327. End Function
  328. p?.Set(0.25)
  329. Dim Result = SelectJavaFromJavaList()
  330. If Result IsNot Nothing Then p?.Skip() : Return Result
  331. SyncLock JavaLock '#3797
  332. '既有列表中没有合适的 Java,重新触发搜索
  333. Logger.Warn($"既有列表中没有合适的 Java,重新触发搜索")
  334. JavaListRefreshWorker.Start(c, p?.SplitTo(0.3))
  335. Result = SelectJavaFromJavaList()
  336. If Result IsNot Nothing Then p?.Skip() : Return Result
  337. If Not TryFixOrHint Then Return Nothing
  338. '电脑中没有合适的 Java,获取 Mojang 提供的 Java 下载列表
  339. Logger.Warn($"电脑中没有合适的 Java,开始获取 Mojang 提供的 Java 下载列表")
  340. Static DownloadListWorker As New RedoableWorker(Of List(Of JavaDownloadInfo))(
  341. Function(cancellationToken)
  342. '下载 Mojang 提供的 Java 列表
  343. Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
  344. {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
  345. {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
  346. ), IsJson:=True)
  347. cancellationToken?.ThrowIfCancellationRequested()
  348. '解析 Java 列表
  349. Dim Results = New List(Of JavaDownloadInfo)
  350. For Each Prop As JProperty In IndexFileStr.DeserializeJson(Of JObject)()("windows-x64")
  351. cancellationToken?.ThrowIfCancellationRequested()
  352. If Prop.Name = "minecraft-java-exe" Then Continue For '不是完整 Java
  353. Try
  354. Dim Entry = Prop.Value.FirstOrDefault
  355. If Entry Is Nothing Then Continue For
  356. Dim JavaName = Entry("version")("name").ToString '可能的名称格式:"12"、"8u51-cacert462b08"、"17.0.8.1"、"17.0.1.12.1"、"21.0.7"
  357. If JavaName.Contains(".") Then
  358. Dim Parts As String() = JavaName.Split(".")
  359. 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))})
  360. ElseIf JavaName.StartsWithF("8u51-") Then
  361. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(8, 0, 51, 0)})
  362. Else
  363. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(JavaName.BeforeFirstOfAny({"u", "."})), 0, 0, 0)})
  364. End If
  365. Catch ex As Exception
  366. Logger.Warn(ex, $"解析 Mojang 提供的 Java 列表项失败,已跳过({Prop.Name})")
  367. End Try
  368. Next
  369. Return Results
  370. End Function)
  371. If Not DownloadListWorker.HasSucceeded Then DownloadListWorker.Start(c, p?.SplitTo(0.4))
  372. DownloadListWorker.WaitIfRunning(cancellationToken:=c)
  373. p?.Set(0.4, skiped:=True)
  374. Dim Components = DownloadListWorker.LastResult
  375. '确定要下载的目标 Java
  376. Dim JavaToDownload As JavaDownloadInfo = If(
  377. Components.FirstOrDefault(Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent), '优先选择指定的 Component
  378. Components.FirstOrDefault(Function(Comp) Range.Contains(Comp.JavaVersion)))
  379. If JavaToDownload Is Nothing Then
  380. Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载")
  381. '提示需要手动下载 Java
  382. Dim Description As String
  383. Dim HasPureLower As Boolean = Range.HasLower AndAlso Range.IsLowerInclusive AndAlso
  384. Range.Lower.Minor <= 0 AndAlso Range.Lower.Build <= 0 AndAlso Range.Lower.Revision <= 0
  385. Dim HasPureUpper As Boolean = Range.HasUpper AndAlso Not Range.IsUpperInclusive AndAlso
  386. Range.Upper.Minor <= 0 AndAlso Range.Upper.Build <= 0 AndAlso Range.Upper.Revision <= 0
  387. If Range.HasLower AndAlso Range.HasUpper Then '有上下界
  388. If HasPureLower AndAlso HasPureUpper Then
  389. If Range.Lower.Major = Range.Upper.Major - 1 Then
  390. Description = $" Java {Range.Lower.Major}"
  391. Else
  392. Description = $" Java {Range.Lower.Major} ~ {Range.Upper.Major - 1}"
  393. End If
  394. Else
  395. Description = $" Java {Range}" '摆烂
  396. End If
  397. ElseIf Range.HasLower Then '只有下界
  398. If HasPureLower Then
  399. Description = $" Java {Range.Lower.Major} 或更高版本的 Java"
  400. ElseIf Range.IsLowerInclusive Then
  401. Description = $" Java {Range.Lower} 或更高版本的 Java"
  402. Else
  403. Description = $"版本高于 {Range.Lower} 的 Java"
  404. End If
  405. Else '只有上界
  406. If HasPureUpper Then
  407. Description = $" Java {Range.Upper.Major - 1} 或更低版本的 Java"
  408. ElseIf Range.IsUpperInclusive Then
  409. Description = $" Java {Range.Upper} 或更低版本的 Java"
  410. Else
  411. Description = $"版本低于 {Range.Upper} 的 Java"
  412. End If
  413. End If
  414. MyMsgBox($"PCL 没能找到合适的 Java。{vbCrLf}请在网上搜索并手动安装{Description}, 然后在【设置 → 启动 → 高级选项 → Java 列表】中导入它。", "需要手动安装 Java")
  415. Return Nothing
  416. End If
  417. '自动下载 Java
  418. Dim JavaLoader = CreateJavaDownloadLoader(JavaToDownload)
  419. Try
  420. JavaLoader.WaitForExit(IsForceRestart:=True, c:=c, p:=p?.SplitTo(0.95))
  421. Finally
  422. JavaLoader.Cancel() '确保取消时中止 Java 下载
  423. End Try
  424. Result = SelectJavaFromJavaList()
  425. If Result IsNot Nothing Then p?.Finish() : Return Result
  426. Throw New Exception($"下载 Java {JavaToDownload.ComponentName} 后仍然无法找到合适的 Java")
  427. End SyncLock
  428. End Select
  429. Catch ex As Exception
  430. ex.ThrowIfCanceled()
  431. Logger.Error(ex, "选择 Java 时出现异常")
  432. p?.Skip() : Return Nothing
  433. End Try
  434. End Function
  435. Private Sub SwitchToInstanceSetup(Instance As McInstance)
  436. RunInUi(
  437. Sub()
  438. PageInstanceLeft.Instance = Instance
  439. FrmMain.PageChange(FormMain.PageType.InstanceSetup, FormMain.PageSubType.InstanceSetup)
  440. End Sub)
  441. End Sub
  442. Private ReadOnly JavaLock As New Object
  443. ''' <summary>
  444. ''' 从老版本 PCL 迁移版本独立的 Java 设置。
  445. ''' </summary>
  446. Private Sub MigrateInstanceJavaSettings(Instance As McInstance, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing)
  447. If Configs.JavaMigrated.Get(Instance.Config) Then p?.Skip() : Return
  448. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(Sub() FrmInstanceSetup.ComboArgumentJava.IsEnabled = False)
  449. Try
  450. Logger.Info($"正在尝试迁移版本独立的 Java 设置({Instance.Name})")
  451. '强制使用指定 Java
  452. Dim ForcedJavaSetting = Settings.Get(Of String)("VersionArgumentJavaSelect", Instance)
  453. If Not ForcedJavaSetting.StartsWithF("{") Then
  454. p?.Set(0.4, True) : GoTo NoForcedJava
  455. End If
  456. Dim ForcedJava = New Java(ForcedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString)
  457. If Not ForcedJava.CheckAsync(c).Run() Then
  458. Hint("该版本的设置中强制指定的 Java 存在异常,该设置已重置", HintType.Red)
  459. p?.Set(0.4) : GoTo NoForcedJava
  460. End If
  461. Logger.Info($"将版本独立设置改为老版本中强制指定的 Java:{ForcedJava}")
  462. If Not Configs.JavaList.Get().Contains(ForcedJava) Then
  463. Configs.JavaList.Edit(Sub(ByRef List) List.Add(ForcedJava)) '加入列表底部
  464. Logger.Info($"已将强制指定的 Java 加入 Java 列表底部:{ForcedJava}")
  465. End If
  466. Configs.JavaForced.Set(ForcedJava, Instance.Config)
  467. Settings.Set("VersionArgumentJavaV2", 3, Instance)
  468. UpdateJavaLists()
  469. Configs.JavaMigrated.Set(True, Instance.Config) '完成
  470. p?.Finish() : Return
  471. NoForcedJava:
  472. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run()
  473. If JavaFound.Any() Then
  474. '从版本文件夹中发现了 Java
  475. Logger.Info($"在版本文件夹中发现了 Java,将版本独立设置改为优先使用版本文件夹的 Java:{JavaFound}")
  476. Settings.Set("VersionArgumentJavaV2", 2, Instance)
  477. Else
  478. '自动选择
  479. Logger.Info("没有需要迁移的内容,自动选择 Java")
  480. Settings.Set("VersionArgumentJavaV2", 0, Instance)
  481. End If
  482. Configs.JavaMigrated.Set(True, Instance.Config) '完成
  483. Finally
  484. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(
  485. Sub()
  486. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJava)
  487. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJavaSelect)
  488. FrmInstanceSetup.ComboArgumentJava.IsEnabled = True
  489. End Sub)
  490. End Try
  491. End Sub
  492. #End Region
  493. #Region "下载(均为内部工具方法,无外部调用)"
  494. Private Class JavaDownloadInfo
  495. Public Property ComponentName As String
  496. Public Property Java As JObject
  497. Public Property JavaVersion As Version
  498. End Class
  499. ''' <summary>
  500. ''' 构建下载指定 Java 的加载器。
  501. ''' </summary>
  502. Private Function CreateJavaDownloadLoader(Target As JavaDownloadInfo) As LoaderCombo(Of Integer)
  503. '分析需要下载的 Java 文件列表
  504. Dim Address As String = Target.Java("manifest")("url")
  505. Logger.Info($"准备下载 Java {Target.Java("version")("name")}({Target.ComponentName}):{Address}")
  506. Dim ListFileStr As String = NetRequestByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
  507. Dim BaseDir As String = $"{Paths.AppData}.minecraft\runtime\{Target.ComponentName}\"
  508. Dim FilesToDownload As New List(Of NetFile)
  509. For Each File As JProperty In CType(ListFileStr.DeserializeJson(), JObject)("files")
  510. If CType(File.Value, JObject)("downloads")?("raw") Is Nothing Then Continue For
  511. Dim Info As JObject = CType(File.Value, JObject)("downloads")("raw")
  512. Dim Checker As New FileChecker With {.ActualSize = Info("size"), .Hash = Info("sha1")}
  513. If Checker.Hash = "12976a6c2b227cbac58969c1455444596c894656" OrElse Checker.Hash = "c80e4bab46e34d02826eab226a4441d0970f2aba" OrElse Checker.Hash = "84d2102ad171863db04e7ee22a259d1f6c5de4a5" Then
  514. Continue For '跳过 3 个无意义大量重复文件(#3827)
  515. End If
  516. Dim Url As String = Info("url")
  517. FilesToDownload.Add(New NetFile(
  518. DlSourceOrder({Url}, {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com")}),
  519. BaseDir & File.Name, Checker))
  520. Next
  521. Logger.Info($"该 Java 需要下载 {FilesToDownload.Count} 个文件,目标文件夹:{BaseDir}")
  522. '构建加载器
  523. Dim Loader = New LoaderCombo(Of Integer)("下载 Java", {
  524. New LoaderDownload("下载 Java 文件", FilesToDownload) With {.ProgressWeight = 10},
  525. New LoaderWorker("刷新 Java 列表", JavaListRefreshWorker)
  526. }) With {.OnStateChanged =
  527. Sub(RawLoader As LoaderCombo(Of Integer))
  528. If RawLoader.State = LoadState.Failed OrElse RawLoader.State = LoadState.Canceled Then
  529. Logger.Warn($"由于 Java 下载未完成,清理未下载完成的 Java 文件夹:{BaseDir}")
  530. DirectoryUtils.Delete(BaseDir)
  531. End If
  532. End Sub}
  533. Return Loader
  534. End Function
  535. #End Region
  536. End Module