龙腾猫跃 3 лет назад
Родитель
Сommit
f4448eddf4
28 измененных файлов с 1166 добавлено и 1092 удалено
  1. 2 0
      Plain Craft Launcher 2/Controls/MyCard.vb
  2. 5 0
      Plain Craft Launcher 2/Controls/MyExtraButton.xaml
  3. 18 0
      Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb
  4. 7 104
      Plain Craft Launcher 2/FormMain.xaml.vb
  5. 7 3
      Plain Craft Launcher 2/Modules/Base/ModBase.vb
  6. 1 0
      Plain Craft Launcher 2/Modules/Base/ModLoader.vb
  7. 38 12
      Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
  8. 4 0
      Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
  9. 747 0
      Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
  10. 48 29
      Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
  11. 19 713
      Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
  12. 12 2
      Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
  13. 6 6
      Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml
  14. 4 1
      Plain Craft Launcher 2/Modules/ModMusic.vb
  15. 2 2
      Plain Craft Launcher 2/My Project/AssemblyInfo.vb
  16. 22 4
      Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
  17. 1 1
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml.vb
  18. 1 3
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml
  19. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
  20. 40 34
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml
  21. 2 1
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb
  22. 34 28
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml
  23. 3 2
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb
  24. 6 10
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb
  25. 2 2
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
  26. 1 0
      Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
  27. 0 1
      Plain Craft Launcher 2/Resources/Custom.xaml
  28. 129 129
      Plain Craft Launcher 2/Resources/ModData.txt

+ 2 - 0
Plain Craft Launcher 2/Controls/MyCard.vb

@@ -155,6 +155,8 @@
                     Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf LiteLoaderSave_Click, True))
                 Case 11
                     Stack.Children.Add(CType(Data, HelpEntry).ToListItem)
+                Case 12
+                    Stack.Children.Add(FabricDownloadListItem(CType(Data, JObject), AddressOf FrmDownloadInstall.Fabric_Selected))
                 Case Else
                     Log("未知的虚拟化种类:" & Type, LogLevel.Feedback)
             End Select

+ 5 - 0
Plain Craft Launcher 2/Controls/MyExtraButton.xaml

@@ -16,6 +16,11 @@
                 <DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" />
             </Border.Effect>
         </Border>
+        <Border x:Name="PanProgress" CornerRadius="1000" BorderThickness="0.001" Background="#2AFFFFFF" ClipToBounds="True" Visibility="Collapsed">
+            <Border.Clip>
+                <RectangleGeometry x:Name="RectProgress" Rect="0,0,40,25" />
+            </Border.Clip>
+        </Border>
         <Path x:Name="Path" Stretch="Uniform" Margin="12" Fill="{DynamicResource ColorBrush8}" RenderTransformOrigin="0.5,0.5">
             <Path.RenderTransform>
                 <ScaleTransform />

+ 18 - 0
Plain Craft Launcher 2/Controls/MyExtraButton.xaml.vb

@@ -4,6 +4,24 @@
     Public Event Click(sender As Object, e As MouseButtonEventArgs) '自定义事件
     Public Event RightClick(sender As Object, e As MouseButtonEventArgs)
 
+    '进度条
+    Private _Progress As Double = 0
+    Public Property Progress As Double
+        Get
+            Return _Progress
+        End Get
+        Set(value As Double)
+            If _Progress = value Then Exit Property
+            _Progress = value
+            If value < 0.0001 Then
+                PanProgress.Visibility = Visibility.Collapsed
+            Else
+                PanProgress.Visibility = Visibility.Visible
+                RectProgress.Rect = New Rect(0, 0, 40, 40 * (1 - value))
+            End If
+        End Set
+    End Property
+
     '自定义属性
     Public Uuid As Integer = GetUuid()
     Private _Logo As String = ""

+ 7 - 104
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -89,52 +89,19 @@ Public Class FormMain
             FeatureCount += 8
             BugCount += 33
         End If
-        If LastVersion < 246 Then 'Release 2.2.9
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 下载支持筛选 Forge Mod 与 Fabric Mod"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "Mod、整合包下载的综合优化"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复使用离线登录进入 Forge 的存档可能导致游戏崩溃"))
-            FeatureCount += 13
-            BugCount += 11
-        End If
-        If LastVersion < 242 Then 'Release 2.2.7
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复 MC 所包含的一个严重安全漏洞"))
-            If LastVersion >= 236 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复可能无法更改登录信息的 Bug"))
-        End If
-        If LastVersion < 238 Then 'Release 2.2.6
-            If LastVersion = 236 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复无法使用联机模块的 Bug"))
-            FeatureCount += 1
-            BugCount += 2
-        End If
-        If LastVersion < 236 Then 'Release 2.2.5
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "网络状态检测支持检测 Windows 防火墙与网络延迟"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "重制右侧页面的切换动画,卡片与提示条将逐个进入退出,并具有独特的位移动画"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 MC 1.18 中要求 Java 17 的检测与提醒"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化页面切换、下载的性能"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "允许拖拽按钮设置第三方登录"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "修复无法下载新版本的 Forge 的 Bug"))
-            FeatureCount += 25
-            BugCount += 22
-        End If
-        If LastVersion < 233 Then 'Release 2.2.3
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "微软账号支持更换皮肤与披风"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL 进行快捷安装"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod 下载支持显示对应的 MC 版本与 Mod 加载器,且可以跳转到 MCBBS 介绍页"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持安装 1.17 OptiFine + Forge"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "使用 Mojang 账号登录将会显示迁移提醒"))
-            FeatureCount += 12
-            BugCount += 26
-        End If
-        If LastVersion < 231 Then 'Release 2.2.1, Also 229
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "添加 1.18 实验性快照 5 的特供下载,且支持列表的联网更新"))
-            FeatureCount += 11
-            BugCount += 3
-        End If
 #Else
         '5:          FEAT+
         '4:     IMP+ FEAT*
         '3:BUG+ IMP* FEAT-
         '2:BUG* IMP-
         '1:BUG-
+        If LastVersion < 287 Then 'Snapshot 2.5.3
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "在缺少 Java 时会自动下载所需的 Java"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 CurseForge / Modrinth 来源筛选"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "为下载管理和音乐播放按钮添加进度条"))
+            FeatureCount += 8
+            BugCount += 8
+        End If
         If LastVersion < 285 Then 'Snapshot 2.5.2
             FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "重做资源下载的资源项界面"))
             If LastVersion = 284 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复打开部分帮助报错的 Bug"))
@@ -260,70 +227,6 @@ Public Class FormMain
             FeatureCount += 2
             BugCount += 16
         End If
-        If LastVersion < 249 Then 'Snapshot 2.2.10
-            If LastVersion = 247 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复无法使用中文搜索 Mod 的 Bug"))
-            FeatureCount += 6
-            BugCount += 17
-        End If
-        If LastVersion < 247 Then 'Snapshot 2.2.9
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 下载支持筛选 Forge Mod 与 Fabric Mod"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod、整合包下载的综合优化"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复使用离线登录进入 Forge 的存档可能导致游戏崩溃"))
-            FeatureCount += 8
-            BugCount += 4
-        End If
-        If LastVersion < 245 Then 'Snapshot 2.2.8
-            FeatureCount += 5
-            BugCount += 7
-        End If
-        If LastVersion < 243 Then 'Snapshot 2.2.7
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复 MC 所包含的一个严重安全漏洞"))
-            If LastVersion >= 236 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复可能无法更改登录信息的 Bug"))
-        End If
-        If LastVersion < 239 Then 'Snapshot 2.2.6
-            If LastVersion >= 235 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复自动更新由于路径错误失败的 Bug"))
-            If LastVersion = 237 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "修复无法使用联机模块的 Bug"))
-            FeatureCount += 1
-            BugCount += 1
-        End If
-        If LastVersion < 237 Then 'Snapshot 2.2.5
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "允许拖拽按钮设置第三方登录"))
-            If LastVersion = 235 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复在启动游戏时有可能卡死的 Bug"))
-            FeatureCount += 2
-            BugCount += 3
-        End If
-        If LastVersion < 235 Then 'Snapshot 2.2.4
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "网络状态检测支持检测 Windows 防火墙与网络延迟"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "重制右侧页面的切换动画,卡片与提示条将逐个进入退出,并具有独特的位移动画"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 MC 1.18 中要求 Java 17 的检测与提醒"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化页面切换、下载的性能"))
-            FeatureCount += 23
-            BugCount += 19
-        End If
-        If LastVersion < 234 Then 'Snapshot 2.2.3
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持拖拽整合包文件到 PCL 进行快捷安装"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持安装 1.17 OptiFine + Forge"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "Mod 详情页面支持跳转 MCBBS 介绍页"))
-            FeatureCount += 10
-            BugCount += 17
-        End If
-        If LastVersion < 232 Then 'Snapshot 2.2.2
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "微软账号支持更换皮肤与披风"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持显示 Mod 与整合包对应的 MC 版本与 Mod 加载器名称"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "使用 Mojang 账号登录将会显示迁移提醒"))
-            FeatureCount += 2
-            BugCount += 9
-        End If
-        If LastVersion < 230 Then 'Snapshot 2.2.1, Also 228
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 1.18 实验性快照 5 的特供下载,且支持列表的联网更新"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复 Win7 上 IOI 联机导致日志文件极大,占用硬盘空间的 Bug"))
-            FeatureCount += 12
-            BugCount += 4
-        End If
-        If LastVersion < 227 Then 'Snapshot 2.2.0
-            FeatureCount += 2
-            BugCount += 2
-        End If
 #End If
         '整理更新日志文本
         Dim ContentList As New List(Of String)

+ 7 - 3
Plain Craft Launcher 2/Modules/Base/ModBase.vb

@@ -11,10 +11,10 @@ Public Module ModBase
 #Region "声明"
 
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.5.2" '不含分支前缀的显示用版本名
-    Public Const VersionStandardCode As String = "2.5.2." & VersionBranchCode '标准格式的四段式版本号
+    Public Const VersionBaseName As String = "2.5.3" '不含分支前缀的显示用版本名
+    Public Const VersionStandardCode As String = "2.5.3." & VersionBranchCode '标准格式的四段式版本号
 #If BETA Then
-    Public Const VersionCode As Integer = 286 'Release
+    Public Const VersionCode As Integer = 287 'Release
 #Else
     Public Const VersionCode As Integer = 285 'Snapshot
 #End If
@@ -1360,6 +1360,8 @@ Re:
             CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。"
         ElseIf TypeOf InnerEx Is OutOfMemoryException Then
             CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。"
+        ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
+            CommonReason = "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
         ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
                 "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then
             CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。"
@@ -1406,6 +1408,8 @@ Re:
             CommonReason = "PCL 的权限不足。请尝试右键 PCL,选择以管理员身份运行。"
         ElseIf TypeOf InnerEx Is OutOfMemoryException Then
             CommonReason = "你的电脑运行内存不足,导致 PCL 无法继续运行。请在关闭一部分不需要的程序后再试。"
+        ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
+            CommonReason = "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
         ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
                 "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then
             CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。"

+ 1 - 0
Plain Craft Launcher 2/Modules/Base/ModLoader.vb

@@ -602,6 +602,7 @@ Restart:
             Else
                 LoaderTaskbarProgress = LoaderTaskbarProgress * 0.9 + NewProgress * 0.1
             End If
+            RunInUi(Sub() FrmMain.BtnExtraDownload.Progress = LoaderTaskbarProgress)
             '更新任务栏信息
             If LoaderTaskbar.Count = 0 OrElse LoaderTaskbarProgress = 1 Then
                 NewState = Shell.TaskbarItemProgressState.None

+ 38 - 12
Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb

@@ -21,6 +21,11 @@
         Fabric = 4
         Quilt = 5
     End Enum
+    <Flags> Public Enum CompSourceType
+        CurseForge = 1
+        Modrinth = 2
+        Any = CurseForge Or Modrinth
+    End Enum
 
 #Region "CompDatabase | Mod 数据库"
 
@@ -383,7 +388,7 @@
             '获取版本描述
             Dim GameVersionDescription As String
             If GameVersions Is Nothing OrElse GameVersions.Count = 0 Then
-                GameVersionDescription = ""
+                GameVersionDescription = "未知"
             Else
                 Dim SpaVersions As New List(Of String)
                 For i = 0 To GameVersions.Count - 1
@@ -414,20 +419,24 @@
             '实例化 UI
             Dim NewItem As New MyCompItem With {.Tag = Me}
             '标题
-            Dim FinalName As String = TranslatedName.Split(" | ").First.Split(" - ").First.Split(" (").First.Split(" [").First
+            Dim FinalName As String = TranslatedName.Split(" | ").First.Split(" - ").First.Split("(").First.Split("[").First
+            Dim ExNameList As List(Of String) = TranslatedName.Split({" | ", " - ", "(", ")", "[", "]", "{", "}"}, StringSplitOptions.RemoveEmptyEntries).
+                Select(Function(s) s.Trim(" /".ToCharArray)).Where(Function(w) Not String.IsNullOrEmpty(w)).ToList
+            ExNameList.RemoveAt(0)
+            If TranslatedName = RawName AndAlso '没有中文翻译
+               Not ExNameList.Any(Function(s) s.Contains("orge") OrElse s.Contains("abric") OrElse s.Contains("uilt")) AndAlso '不是标注 XX 版
+               Not (ExNameList.Count = 1 AndAlso ExNameList.First.ToUpper <> ExNameList.First) Then '不是缩写
+                FinalName = RawName '使用原名
+                ExNameList.Clear()
+            End If
             NewItem.LabTitle.Text = FinalName
-            Dim ExNameList As New List(Of String)
-            If TranslatedName.Contains(" (") Then ExNameList.Add(TranslatedName.Split(" (").Last.Split(")").First)
-            If TranslatedName.Contains(" [") Then ExNameList.Add(TranslatedName.Split(" [").Last.Split("]").First)
-            If TranslatedName.Contains(" - ") Then ExNameList.Add(TranslatedName.Split(" - ").Last.Split(" | ").First.Split(" (").First.Split(" [").First)
-            If TranslatedName.Contains(" | ") Then ExNameList.Add(TranslatedName.Split(" | ").Last.Split(" - ").First.Split(" (").First.Split(" [").First)
             If ExNameList.Count = 0 Then
 NoExName:
                 CType(NewItem.LabTitleRaw.Parent, StackPanel).Children.Remove(NewItem.LabTitleRaw)
             Else
                 Dim ExName As String = ""
                 For Each Ex In ExNameList
-                    '去除 “Forge/Fabric” 一类的无意义提示
+                    '去除 “Forge/Fabric” 这一无意义提示
                     If Ex.Length < 19 AndAlso Ex.ToLower.Contains("fabric") AndAlso Ex.ToLower.Contains("forge") Then Continue For
                     '将 “Forge” 等提示改为 “Forge 版”
                     If (Ex.ToLower.Contains("forge") OrElse Ex.ToLower.Contains("fabric") OrElse Ex.ToLower.Contains("quilt")) AndAlso
@@ -452,7 +461,9 @@ NoExName:
                 '全部隐藏
                 CType(NewItem.PathVersion.Parent, Grid).Children.Remove(NewItem.PathVersion)
                 CType(NewItem.LabVersion.Parent, Grid).Children.Remove(NewItem.LabVersion)
-                NewItem.ColumnVersionMargin.Width = New GridLength(0)
+                NewItem.ColumnVersion1.Width = New GridLength(0)
+                NewItem.ColumnVersion2.MaxWidth = 0
+                NewItem.ColumnVersion3.Width = New GridLength(0)
             ElseIf ShowMcVersionDesc AndAlso ShowMcVersionDesc Then
                 '全部显示
                 Dim ModLoaderDesc As String = If(ModLoaders.Count > 0 AndAlso ModLoaders.Count < 3 AndAlso
@@ -491,6 +502,11 @@ NoExName:
             NewItem.LabSource.Text = If(FromCurseForge, "CurseForge", "Modrinth")
             If LastUpdate IsNot Nothing Then
                 NewItem.LabTime.Text = GetTimeSpanString(LastUpdate - Date.Now, True)
+            Else
+                NewItem.LabTime.Visibility = Visibility.Collapsed
+                NewItem.ColumnTime1.Width = New GridLength(0)
+                NewItem.ColumnTime2.Width = New GridLength(0)
+                NewItem.ColumnTime3.Width = New GridLength(0)
             End If
             NewItem.LabDownload.Text =
                 If(DownloadCount > 100000000, Math.Round(DownloadCount / 100000000, 2) & " 亿",
@@ -610,6 +626,10 @@ NoExName:
         ''' </summary>
         Public SearchText As String = Nothing
         ''' <summary>
+        ''' 允许的来源。
+        ''' </summary>
+        Public Source As CompSourceType = CompSourceType.Any
+        ''' <summary>
         ''' 构造函数。
         ''' </summary>
         Public Sub New(Type As CompType, Storage As CompProjectStorage, TargetResultCount As Integer)
@@ -624,6 +644,7 @@ NoExName:
         ''' 获取对应的 CurseForge API 请求链接。若返回 Nothing 则为不进行 CurseForge 请求。
         ''' </summary>
         Public Function GetCurseForgeAddress() As String
+            If Not Source.HasFlag(CompSourceType.CurseForge) Then Return Nothing
             If Tag.StartsWith("/") Then Storage.CurseForgeTotal = 0
             If Storage.CurseForgeTotal > -1 AndAlso Storage.CurseForgeTotal <= Storage.CurseForgeOffset Then Return Nothing
             '应用筛选参数
@@ -647,6 +668,7 @@ NoExName:
         ''' 获取对应的 Modrinth API 请求链接。若返回 Nothing 则为不进行 Modrinth 请求。
         ''' </summary>
         Public Function GetModrinthAddress() As String
+            If Not Source.HasFlag(CompSourceType.Modrinth) Then Return Nothing
             If Tag.EndsWith("/") Then Storage.ModrinthTotal = 0
             If Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset Then Return Nothing
             '应用筛选参数
@@ -668,7 +690,7 @@ NoExName:
             Dim request = TryCast(obj, CompProjectRequest)
             Return request IsNot Nothing AndAlso
                 Type = request.Type AndAlso TargetResultCount = request.TargetResultCount AndAlso
-                Tag = request.Tag AndAlso ModLoader = request.ModLoader AndAlso
+                Tag = request.Tag AndAlso ModLoader = request.ModLoader AndAlso Source = request.Source AndAlso
                 GameVersion = request.GameVersion AndAlso SearchText = request.SearchText
         End Function
         Public Shared Operator =(left As CompProjectRequest, right As CompProjectRequest) As Boolean
@@ -888,6 +910,10 @@ Retry:
                 Else
                     If IsChineseSearch AndAlso Task.Input.Type <> CompType.Mod Then
                         Throw New Exception($"{If(Task.Input.Type = CompType.ModPack, "整合包", "资源包")}搜索仅支持英文")
+                    ElseIf Task.Input.Source = CompSourceType.CurseForge AndAlso Task.Input.Tag.StartsWith("/") Then
+                        Throw New Exception("CurseForge 不兼容所选的类型")
+                    ElseIf Task.Input.Source = CompSourceType.Modrinth AndAlso Task.Input.Tag.EndsWith("/") Then
+                        Throw New Exception("Modrinth 不兼容所选的类型")
                     Else
                         Throw New Exception("没有符合条件的结果")
                     End If
@@ -1127,7 +1153,7 @@ Retry:
                 '可能为空
                 If CType(Data("files"), JArray).Count > 0 Then
                     FileName = Data("files")(0)("filename")
-                    DownloadAddress = {Data("files")(0)("url").ToString.Replace(FileName, Net.WebUtility.UrlEncode(FileName))}
+                    DownloadAddress = {Data("files")(0)("url").ToString.Replace(FileName, FileName)}
                 End If
                 'Dependencies
                 If Type = CompType.Mod Then
@@ -1311,7 +1337,7 @@ Retry:
         Stack.Children.Add(New TextBlock With {.Text = "前置 Mod", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
         '添加前置 Mod 列表
         For Each Dep In Deps
-            Dim Item = CompProjectCache(Dep).ToCompItem(True, True, AddressOf FrmDownloadMod.ProjectClick)
+            Dim Item = CompProjectCache(Dep).ToCompItem(False, False, AddressOf FrmDownloadMod.ProjectClick)
             Stack.Children.Add(Item)
         Next
         '添加结尾间隔

+ 4 - 0
Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb

@@ -330,6 +330,7 @@
         使用OpenJ9
         Java版本过高
         Java版本不兼容
+        Mod名称包含特殊字符
         显卡驱动不支持导致无法设置像素格式
         极短的程序输出
         Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-32606
@@ -482,6 +483,7 @@ Done:
             If LogMc.Contains("Maybe try a lower resolution resourcepack?") Then AppendReason(CrashReason.材质过大或显卡配置不足)
             If LogMc.Contains("java.lang.NoSuchMethodError: net.minecraft.world.server.ChunkManager$ProxyTicketManager.shouldForceTicks(J)Z") AndAlso LogMc.Contains("OptiFine") Then AppendReason(CrashReason.OptiFine导致无法加载世界)
             If LogMc.Contains("Unsupported class file major version") Then AppendReason(CrashReason.Java版本不兼容)
+            If LogMc.Contains("Invalid module name: '' is not a Java identifier") Then AppendReason(CrashReason.Mod名称包含特殊字符)
             If LogMc.Contains("java.lang.UnsupportedClassVersionError: net/fabricmc/loader/impl/launch/knot/KnotClient : Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
             If LogMc.Contains("Could not reserve enough space") Then
                 If LogMc.Contains("for 1048576KB object heap") Then
@@ -799,6 +801,8 @@ NextStack:
                     Results.Add("游戏似乎因为你所使用的 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从网络中下载、安装一个。")
                 Case CrashReason.Java版本不兼容
                     Results.Add("游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。")
+                Case CrashReason.Mod名称包含特殊字符
+                    Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。")
                 Case CrashReason.使用32位Java导致JVM无法分配足够多的内存
                     If Environment.Is64BitOperatingSystem Then
                         Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")

+ 747 - 0
Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb

@@ -0,0 +1,747 @@
+Public Module ModJava
+    Public JavaListCacheVersion As Integer = 4
+
+    ''' <summary>
+    ''' 目前所有可用的 Java。
+    ''' </summary>
+    Public JavaList As New List(Of JavaEntry)
+
+    Public Class JavaEntry
+
+        '路径
+        ''' <summary>
+        ''' Java.exe 文件的完整路径。
+        ''' </summary>
+        Public ReadOnly Property PathJava As String
+            Get
+                Return PathFolder & "java.exe"
+            End Get
+        End Property
+        ''' <summary>
+        ''' Javaw.exe 文件的完整路径。
+        ''' </summary>
+        Public ReadOnly Property PathJavaw As String
+            Get
+                Return PathFolder & "javaw.exe"
+            End Get
+        End Property
+        ''' <summary>
+        ''' Javaw.exe 文件所在文件夹的路径,以 \ 结尾。
+        ''' </summary>
+        Public PathFolder As String
+        ''' <summary>
+        ''' 是否为用户手动导入的 Java。
+        ''' </summary>
+        Public IsUserImport As Boolean
+
+        '版本信息
+        ''' <summary>
+        ''' Java 的详细版本。若不足 4 位会在前方补 1,例如 1.16.0.1。
+        ''' 其大版本号为 Minor。
+        ''' </summary>
+        Public Version As Version
+        ''' <summary>
+        ''' Java 的大版本号。
+        ''' </summary>
+        Public ReadOnly Property VersionCode As Integer
+            Get
+                Return Version.Minor
+            End Get
+        End Property
+        ''' <summary>
+        ''' 是否为 Java Runtime Environment。
+        ''' </summary>
+        Public IsJre As Boolean
+        ''' <summary>
+        ''' 是否为 64 位 Java。
+        ''' </summary>
+        Public Is64Bit As Boolean
+        ''' <summary>
+        ''' 是否已设置环境变量。
+        ''' </summary>
+        Public ReadOnly Property HasEnvironment As Boolean
+            Get
+                If PathFolder Is Nothing OrElse PathEnv Is Nothing Then Return False
+                Return PathEnv.Replace("\", "").Replace("/", "").ToLower.Contains(PathFolder.Replace("\", "").ToLower)
+            End Get
+        End Property
+
+        '序列化
+        Public Function ToJson() As JObject
+            Return New JObject({New JProperty("Path", PathFolder), New JProperty("VersionString", Version.ToString), New JProperty("IsJre", IsJre), New JProperty("Is64Bit", Is64Bit), New JProperty("IsUserImport", IsUserImport)})
+        End Function
+        Public Shared Function FromJson(Data As JObject) As JavaEntry
+            Return New JavaEntry(Data("Path"), Data("IsUserImport")) With {.Version = New Version(Data("VersionString")), .IsJre = Data("IsJre"), .Is64Bit = Data("Is64Bit")}
+        End Function
+        ''' <summary>
+        ''' 转化为用户友好的字符串输出。
+        ''' </summary>
+        Public Overrides Function ToString() As String
+            Dim VersionString = Version.ToString
+            If VersionString.StartsWith("1.") Then VersionString = Mid(VersionString, 3)
+            Return If(IsJre, "JRE ", "JDK ") & VersionCode & " (" & VersionString & ")," & If(Is64Bit, "64", "32") & " 位" & If(IsUserImport, ",手动导入", "") & ":" & PathFolder
+        End Function
+
+        '构造
+        ''' <summary>
+        ''' 输入 javaw.exe 文件所在文件夹的路径,不限制结尾。
+        ''' </summary>
+        Public Sub New(Folder As String, IsUserImport As Boolean)
+            If Not Folder.EndsWith("\") Then Folder += "\"
+            PathFolder = Folder.Replace("/", "\")
+            Me.IsUserImport = IsUserImport
+        End Sub
+
+        '方法
+        Private IsChecked As Boolean = False
+        ''' <summary>
+        ''' 检查并获取 Java 详细信息。在 Java 存在异常时抛出错误。
+        ''' </summary>
+        Public Sub Check()
+            If IsChecked Then Exit Sub
+            Dim Output As String = Nothing
+            Try
+                '确定文件存在
+                If Not File.Exists(PathJavaw) Then
+                    Throw New FileNotFoundException("未找到 javaw.exe 文件", PathJavaw)
+                End If
+                If Not File.Exists(PathFolder & "java.exe") Then
+                    Throw New FileNotFoundException("未找到 java.exe 文件", PathFolder & "java.exe")
+                End If
+                IsJre = Not File.Exists(PathFolder & "javac.exe")
+                '运行 -version
+                Output = ShellAndGetOutput(PathFolder & "java.exe", "-version", 15000).ToLower
+                If Output = "" Then Throw New Exception("尝试运行该 Java 失败")
+                If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
+                '获取详细信息
+                Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
+                Do While VersionString.Split(".").Count < 4
+                    If VersionString.StartsWith("1.") Then
+                        VersionString = VersionString & ".0"
+                    Else
+                        VersionString = "1." & VersionString
+                    End If
+                Loop
+                Version = New Version(VersionString)
+                If Version.Minor = 0 Then
+                    Log("[Java] 疑似 X.0.X.X 格式版本号:" & Version.ToString)
+                    Version = New Version(1, Version.Major, Version.Build, Version.Revision)
+                End If
+                Is64Bit = Output.Contains("64-bit")
+                If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New Exception("分析详细信息失败,获取的版本为 " & Version.ToString)
+                '无论如何不允许使用 JRE 16,它完全是炸的,安 OptiFine、Forge 和启动 MC 都会炸
+                If IsJre AndAlso VersionCode = 16 Then Throw New Exception("由于 JRE 16 对 Minecraft 的兼容性很差,因此不再允许使用")
+            Catch ex As Exception
+                Log("[Java] 检查失败的 Java 输出:" & PathFolder & "java.exe" & vbCrLf & If(Output, "无程序输出"))
+                Throw New Exception("检查 Java 失败(" & If(PathJavaw, "Nothing") & ")", ex)
+            End Try
+            IsChecked = True
+        End Sub
+
+    End Class
+
+    ''' <summary>
+    ''' Path 环境变量。
+    ''' </summary>
+    Private ReadOnly Property PathEnv As String
+        Get
+            If _PathEnv Is Nothing Then _PathEnv = If(Environment.GetEnvironmentVariable("Path"), "")
+            Return _PathEnv
+        End Get
+    End Property
+    Private _PathEnv As String = Nothing
+
+    ''' <summary>
+    ''' JAVA_HOME 环境变量。
+    ''' </summary>
+    Private ReadOnly Property PathJavaHome As String
+        Get
+            If _PathJavaHome Is Nothing Then _PathJavaHome = If(Environment.GetEnvironmentVariable("JAVA_HOME"), "")
+            Return _PathJavaHome
+        End Get
+    End Property
+    Private _PathJavaHome As String = Nothing
+
+    ''' <summary>
+    ''' 初始化 Java 列表,但除非没有 Java,否则不进行检查。
+    ''' </summary>
+    Public Sub JavaListInit()
+        JavaList = New List(Of JavaEntry)
+        Try
+            If Setup.Get("CacheJavaListVersion") < JavaListCacheVersion Then
+                '不使用缓存
+                Log("[Java] 要求 Java 列表缓存更新")
+                Setup.Set("CacheJavaListVersion", JavaListCacheVersion)
+            Else
+                '使用缓存
+                For Each JsonEntry In GetJson(Setup.Get("LaunchArgumentJavaAll"))
+                    JavaList.Add(JavaEntry.FromJson(JsonEntry))
+                Next
+            End If
+            If JavaList.Count = 0 Then
+                Log("[Java] 初始化未找到可用的 Java,将自动触发搜索", LogLevel.Developer)
+                JavaSearchLoader.Start(0)
+            Else
+                Log("[Java] 缓存中有 " & JavaList.Count & " 个可用的 Java")
+            End If
+        Catch ex As Exception
+            Log(ex, "初始化 Java 列表失败", LogLevel.Feedback)
+            Setup.Set("LaunchArgumentJavaAll", "[]")
+        End Try
+    End Sub
+    ''' <summary>
+    ''' 根据要求返回最适合的 Java,若找不到则返回 Nothing。
+    ''' 最小与最大版本在与输入相同时也会通过。
+    ''' 必须在工作线程调用。
+    ''' </summary>
+    Public Function JavaSelect(CancelException As String, Optional MinVersion As Version = Nothing, Optional MaxVersion As Version = Nothing,
+                               Optional RelatedVersion As McVersion = Nothing) As JavaEntry
+        Try
+            Dim AllowedJavaList As New List(Of JavaEntry)
+
+            '添加特定的 Java
+            Dim JavaPreList As New Dictionary(Of String, Boolean)
+            If PathMcFolder.Split("\").Count > 3 Then JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
+            JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹
+            If RelatedVersion IsNot Nothing Then JavaSearchFolder(RelatedVersion.Path, JavaPreList, False, True) '所选版本文件夹
+            Dim TargetJavaList As New List(Of JavaEntry)
+            For Each Entry In JavaPreList
+                TargetJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
+            Next
+
+#Region "添加用户指定的 Java,储存到 UserJava 中"
+
+            Dim UserJava As JavaEntry = Nothing
+
+            '获取版本独立设置中指定的 Java
+            If RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion).ToString.StartsWith("{") Then
+                Try
+                    UserJava = JavaEntry.FromJson(GetJson(Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)))
+                    '确保版本独立设置中指定的 Java 在 Java 列表中(#978)
+                    If Not JavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
+                        UserJava = Nothing
+                        Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
+                        Log("版本独立设置中指定的 Java 不在主 Java 列表中,此设置已重置", LogLevel.Debug)
+                        Exit Try
+                    End If
+                Catch ex As Exception
+                    UserJava = Nothing
+                    Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
+                    Log(ex, "版本独立设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
+                End Try
+            End If
+
+            '获取全局设置中指定的 Java
+            If UserJava Is Nothing AndAlso Setup.Get("LaunchArgumentJavaSelect") <> "" Then
+                Try
+                    UserJava = JavaEntry.FromJson(GetJson(Setup.Get("LaunchArgumentJavaSelect")))
+                Catch ex As Exception
+                    UserJava = Nothing
+                    Setup.Reset("LaunchArgumentJavaSelect")
+                    Log(ex, "设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
+                End Try
+            End If
+
+            '添加到特定 Java 列表
+            If UserJava IsNot Nothing Then
+                Log($"[Java] 用户指定的 Java:{UserJava}")
+                TargetJavaList.Add(UserJava)
+            End If
+
+#End Region
+
+            '检查特定的 Java
+            If TargetJavaList.Count > 0 Then
+                TargetJavaList = JavaCheckList(TargetJavaList)
+                Log("[Java] 检查后找到 " & TargetJavaList.Count & " 个特定的 Java")
+            End If
+
+RetryGet:
+            '等待进行中的搜索结束
+            If JavaSearchLoader.State <> LoadState.Finished AndAlso JavaSearchLoader.State <> LoadState.Waiting Then JavaSearchLoader.WaitForExit()
+            Select Case JavaSearchLoader.State
+                Case LoadState.Failed
+                    Throw JavaSearchLoader.Error
+                Case LoadState.Aborted
+                    Throw New ThreadInterruptedException("Java 搜索加载器已中断")
+            End Select
+
+            '生成完整的 Java 列表
+            Dim AllJavaList As New List(Of JavaEntry)
+            AllJavaList.AddRange(TargetJavaList)
+            AllJavaList.AddRange(JavaList)
+
+            '根据选定条件进行过滤
+            For Each Java In AllJavaList
+                If MinVersion IsNot Nothing AndAlso Java.Version < MinVersion Then Continue For
+                If MaxVersion IsNot Nothing AndAlso Java.Version > MaxVersion Then Continue For
+                If Java.Is64Bit AndAlso Is32BitSystem Then Continue For
+                AllowedJavaList.Add(Java)
+            Next
+
+            '若未找到适合的 Java,尝试触发搜索
+            If AllowedJavaList.Count = 0 AndAlso JavaSearchLoader.State = LoadState.Waiting Then
+                Log("[Java] 未找到满足条件的 Java,尝试进行搜索")
+                JavaSearchLoader.Start(IsForceRestart:=True)
+                GoTo RetryGet
+            End If
+
+#Region "检查用户指定的 Java 是否可用"
+
+            '确保指定的 Java 可用
+            If UserJava Is Nothing Then GoTo ExitUserJavaCheck
+            If AllowedJavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
+                Log("[Java] 使用用户指定的 Java:" & UserJava.PathFolder)
+                AllowedJavaList = New List(Of JavaEntry) From {UserJava}
+                GoTo UserPass
+            End If
+
+            '指定的 Java 不可用,弹窗要求选择
+            Log("[Java] 发现用户指定的不兼容 Java:" & UserJava.ToString)
+            For Each Java In AllowedJavaList
+                Log($"[Java] 实际可用的 Java:{Java.ToString}")
+            Next
+            Dim Requirement As String = ""
+            Dim ShowRevision As Boolean = False
+            If (MinVersion Is Nothing OrElse MinVersion.Minor = 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
+                ShowRevision = MaxVersion.MinorRevision < 999
+                Requirement = "最高兼容到 Java " & MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
+            ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion Is Nothing OrElse MaxVersion.Minor >= 999) Then
+                ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0
+                Requirement = "至少需要 Java " & MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
+            ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
+                ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0 OrElse MaxVersion.MinorRevision < 999
+                Dim Left As String = MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
+                Dim Right As String = MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
+                Requirement = "需要 Java " & If(Left = Right, Left, Left & " ~ " & Right)
+            End If
+            Dim JavaCurrent As String = UserJava.VersionCode & If(ShowRevision, "." & UserJava.Version.MajorRevision & "." & UserJava.Version.MinorRevision, "")
+            If Setup.Get("LaunchAdvanceJava") Then
+                '直接跳过弹窗
+                Hint("设置中指定了使用 Java " & JavaCurrent & ",但当前版本" & Requirement & ",这可能会导致游戏崩溃!", HintType.Critical)
+                AllowedJavaList = New List(Of JavaEntry) From {UserJava}
+            Else
+                Select Case MyMsgBox("你在设置中手动指定了使用 Java " & JavaCurrent & ",但当前" & Requirement & "。" & vbCrLf &
+                            "如果强制使用该 Java,可能导致游戏崩溃。" & vbCrLf &
+                            "你也可以将 游戏 Java 设置修改为 自动选择合适的 Java。" & vbCrLf &
+                            vbCrLf &
+                            " - 指定的 Java:" & UserJava.ToString,
+                            "Java 兼容性警告", "让 PCL 自动选择", "强制使用该 Java", "取消")
+                    Case 1 '让 PCL 自动选择
+                    Case 2 '强制使用指定的 Java
+                        Log("[Java] 已强制使用用户指定的不兼容 Java")
+                        AllowedJavaList = New List(Of JavaEntry) From {UserJava}
+                    Case 3 '取消启动
+                        Throw New Exception(CancelException)
+                End Select
+            End If
+
+ExitUserJavaCheck:
+#End Region
+
+            '若依然未找到适合的 Java,直接返回
+            If AllowedJavaList.Count = 0 Then Return Nothing
+
+            '优先使用特定目录下的 Java
+            For Each Java In AllowedJavaList
+                If TargetJavaList.Contains(Java) Then
+                    '直接使用指定的 Java
+                    AllowedJavaList = New List(Of JavaEntry) From {Java}
+                    Log("[Java] 优先使用特定的 Java:" & Java.ToString)
+                    GoTo UserPass
+                End If
+            Next
+UserPass:
+
+            '对适合的 Java 进行排序
+            AllowedJavaList = Sort(AllowedJavaList, AddressOf JavaSorter)
+
+            '检查选定的 Java,若测试失败则尝试进行搜索
+            Dim SelectedJava = AllowedJavaList.First
+            Try
+                SelectedJava.Check()
+            Catch ex As ThreadInterruptedException
+                Throw
+            Catch ex As Exception
+                Log(ex, "找到的 Java 已无法使用,尝试进行搜索")
+                AllowedJavaList = New List(Of JavaEntry)
+                JavaSearchLoader.Start(IsForceRestart:=True)
+                GoTo RetryGet
+            End Try
+
+            '返回
+            Log("[Java] 选定的 Java:" & SelectedJava.ToString)
+            Return SelectedJava
+
+        Catch ex As ThreadInterruptedException
+            Log(ex, "查找符合条件的 Java 时出现加载器中断")
+            Return Nothing
+        Catch ex As Exception
+            If ex.Message = "$$" Then Throw ex
+            Log(ex, "查找符合条件的 Java 失败", LogLevel.Feedback)
+            Return Nothing
+        End Try
+    End Function
+    ''' <summary>
+    ''' 是否强制指定了 64 位 Java。如果没有强制指定,返回是否安装了 64 位 Java。
+    ''' </summary>
+    Public Function JavaIs64Bit(Optional RelatedVersion As McVersion = Nothing) As Boolean
+        Try
+            '检查强制指定
+            Dim UserSetup As String = Setup.Get("LaunchArgumentJavaSelect")
+            If RelatedVersion IsNot Nothing Then
+                Dim UserSetupVersion As String = Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)
+                If UserSetupVersion <> "使用全局设置" Then UserSetup = UserSetupVersion
+            End If
+            If UserSetup <> "" Then
+                Dim UserJava As JavaEntry = Nothing
+                Try
+                    UserJava = JavaEntry.FromJson(GetJson(UserSetup))
+                Catch ex As Exception
+                    Log(ex, "版本指定的 Java 信息已损坏,已重置版本设置中指定的 Java")
+                    Setup.Set("VersionArgumentJavaSelect", "使用全局设置", Version:=RelatedVersion)
+                    GoTo NoUserJava
+                End Try
+                For Each Java In JavaList
+                    If Java.PathFolder = UserJava.PathFolder Then Return UserJava.Is64Bit
+                Next
+            End If
+NoUserJava:
+            '检查列表
+            For Each Java In JavaList
+                If Java.Is64Bit Then Return True
+            Next
+            Return False
+        Catch ex As Exception
+            Log(ex, "检查 Java 类别时出错", LogLevel.Feedback)
+            Setup.Set("LaunchArgumentJavaSelect", "")
+            Return True
+        End Try
+    End Function
+    ''' <summary>
+    ''' 将 Java 按照适用性排序。
+    ''' </summary>
+    Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
+        '1. 尽量在当前文件夹或当前 Minecraft 文件夹
+        Dim ProgramPathParent As String, MinecraftPathParent As String = ""
+        ProgramPathParent = If(New DirectoryInfo(Path).Parent, New DirectoryInfo(Path)).FullName
+        If PathMcFolder <> "" Then MinecraftPathParent = If(New DirectoryInfo(PathMcFolder).Parent, New DirectoryInfo(PathMcFolder)).FullName
+        If Left.PathFolder.StartsWith(ProgramPathParent) AndAlso Not Right.PathFolder.StartsWith(ProgramPathParent) Then Return True
+        If Not Left.PathFolder.StartsWith(ProgramPathParent) AndAlso Right.PathFolder.StartsWith(ProgramPathParent) Then Return False
+        If PathMcFolder <> "" Then
+            If Left.PathFolder.StartsWith(MinecraftPathParent) AndAlso Not Right.PathFolder.StartsWith(MinecraftPathParent) Then Return True
+            If Not Left.PathFolder.StartsWith(MinecraftPathParent) AndAlso Right.PathFolder.StartsWith(MinecraftPathParent) Then Return False
+        End If
+        '2. 尽量使用 64 位
+        If Left.Is64Bit AndAlso Not Right.Is64Bit Then Return True
+        If Not Left.Is64Bit AndAlso Right.Is64Bit Then Return False
+        '3. 尽量不使用 JDK
+        If Left.IsJre AndAlso Not Right.IsJre Then Return True
+        If Not Left.IsJre AndAlso Right.IsJre Then Return False
+        '4. Java 大版本
+        If Left.VersionCode <> Right.VersionCode Then
+            '                             Java  7   8   9  10  11  12 13 14 15  16  17  18  19  20...
+            Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 29, 10, 11, 12, 13, 9, 8, 7, 15, 31, 30, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}
+            Return Weight.ElementAtOrDefault(Left.VersionCode) >= Weight.ElementAtOrDefault(Right.VersionCode)
+        End If
+        '5. 最次级版本号更接近 51
+        Return Math.Abs(Left.Version.Revision - 51) <= Math.Abs(Right.Version.Revision - 51)
+    End Function
+
+#Region "搜索"
+
+    ''' <summary>
+    ''' 模糊搜索并获取所有可用的 Java,并在结束后更新设置页面显示。输出将直接写入 JavaList。
+    ''' </summary>
+    Public JavaSearchLoader As New LoaderTask(Of Integer, Integer)("查找 Java", AddressOf JavaSearchLoaderSub) With {.ProgressWeight = 2}
+    Private Sub JavaSearchLoaderSub(Loader As LoaderTask(Of Integer, Integer))
+        If FrmSetupLaunch IsNot Nothing Then
+            RunInUiWait(Sub()
+                            FrmSetupLaunch.ComboArgumentJava.Items.Clear()
+                            FrmSetupLaunch.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
+                        End Sub)
+        End If
+        If FrmVersionSetup IsNot Nothing Then
+            RunInUiWait(Sub()
+                            FrmVersionSetup.ComboArgumentJava.Items.Clear()
+                            FrmVersionSetup.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
+                        End Sub)
+        End If
+
+        Try
+
+            '可能包含 Java 的文件夹列表,以 “\” 结尾,且仅包含 “\”
+            'Key:文件夹地址
+            'Value: 是否为玩家手动导入
+            Dim JavaPreList As New Dictionary(Of String, Boolean)
+
+#Region "模糊查找可能可用的 Java"
+
+            '查找环境变量中的 Java
+            For Each PathInEnv As String In Split((PathEnv & ";" & PathJavaHome).Replace("\\", "\").Replace("/", "\"), ";")
+                PathInEnv = PathInEnv.Trim(" """.ToCharArray())
+                If PathInEnv = "" Then Continue For
+                If Not PathInEnv.EndsWith("\") Then PathInEnv += "\"
+                '粗略检查有效性
+                If File.Exists(PathInEnv & "javaw.exe") Then JavaPreList(PathInEnv) = False
+            Next
+            '查找磁盘中的 Java
+            For Each Disk As DriveInfo In DriveInfo.GetDrives()
+                JavaSearchFolder(Disk.Name, JavaPreList, False)
+            Next
+            '查找 APPDATA 文件夹中的 Java
+            JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
+            JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "\", JavaPreList, False)
+            '查找启动器目录中的 Java
+            JavaSearchFolder(Path, JavaPreList, False, IsFullSearch:=True)
+            '查找所选 Minecraft 文件夹中的 Java
+            If Not String.IsNullOrWhiteSpace(PathMcFolder) AndAlso Path <> PathMcFolder Then
+                JavaSearchFolder(PathMcFolder, JavaPreList, False, IsFullSearch:=True)
+            End If
+
+            '若不全为符号链接,则清除符号链接的地址
+            Dim JavaWithoutReparse As New Dictionary(Of String, Boolean)
+            For Each Pair In JavaPreList
+                Dim Folder As String = Pair.Key.Replace("\\", "\").Replace("/", "\")
+                Dim Info As FileSystemInfo = New FileInfo(Folder & "javaw.exe")
+                Do
+                    If Info.Attributes.HasFlag(FileAttributes.ReparsePoint) Then
+                        Log("[Java] 位于 " & Folder & " 的 Java 包含符号链接")
+                        Continue For
+                    End If
+                    Info = If(TypeOf Info Is FileInfo, CType(Info, FileInfo).Directory, CType(Info, DirectoryInfo).Parent)
+                Loop While Info IsNot Nothing
+                Log("[Java] 位于 " & Folder & " 的 Java 不含符号链接")
+                JavaWithoutReparse.Add(Pair.Key, Pair.Value)
+            Next
+            If JavaWithoutReparse.Count > 0 Then JavaPreList = JavaWithoutReparse
+
+            '若不全为特殊引用,则清除特殊引用的地址
+            Dim JavaWithoutInherit As New Dictionary(Of String, Boolean)
+            For Each Pair In JavaPreList
+                If Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") Then
+                    Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用")
+                    Continue For
+                End If
+                Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
+                JavaWithoutInherit.Add(Pair.Key, Pair.Value)
+            Next
+            If JavaWithoutInherit.Count > 0 Then JavaPreList = JavaWithoutInherit
+
+#End Region
+
+#Region "添加玩家手动导入的 Java"
+
+            Dim ImportedJava As String = Setup.Get("LaunchArgumentJavaAll")
+            Try
+                For Each JavaJsonObject In GetJson(ImportedJava)
+                    Dim Entry = JavaEntry.FromJson(JavaJsonObject)
+                    If Entry.IsUserImport Then JavaPreList(Entry.PathFolder) = True
+                Next
+            Catch ex As Exception
+                Log(ex, "Java 列表已损坏", LogLevel.Feedback)
+                Setup.Set("LaunchArgumentJavaAll", "[]")
+            End Try
+
+#End Region
+
+            '确保可用并获取详细信息,转入正式列表
+            Dim NewJavaList As New List(Of JavaEntry)
+            For Each Entry In Distinct(JavaPreList.ToList, Function(a, b) a.Key.ToLower = b.Key.ToLower) '#794
+                NewJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
+            Next
+            NewJavaList = Sort(JavaCheckList(NewJavaList), AddressOf JavaSorter)
+
+            '修改设置项
+            Dim AllList As New JArray
+            For Each Java In NewJavaList
+                AllList.Add(Java.ToJson)
+            Next
+            Setup.Set("LaunchArgumentJavaAll", AllList.ToString(Newtonsoft.Json.Formatting.None))
+            JavaList = NewJavaList
+
+        Catch ex As Exception
+            Log(ex, "搜索 Java 时出错", LogLevel.Feedback)
+            JavaList = New List(Of JavaEntry)
+        End Try
+
+        Log("[Java] Java 搜索完成,发现 " & JavaList.Count & " 个 Java")
+        If FrmSetupLaunch IsNot Nothing Then RunInUi(Sub() FrmSetupLaunch.RefreshJavaComboBox())
+        If FrmVersionSetup IsNot Nothing Then RunInUi(Sub() FrmVersionSetup.RefreshJavaComboBox())
+    End Sub
+
+    ''' <summary>
+    ''' 多线程检查列表中的所有 Java 项。
+    ''' </summary>
+    Private Function JavaCheckList(JavaEntries As List(Of JavaEntry)) As List(Of JavaEntry)
+        Log("[Java] 开始确认列表 Java 状态,共 " & JavaEntries.Count & " 项")
+        JavaCheckList = New List(Of JavaEntry)
+        Dim ListLock As New Object
+
+        '启动检查线程
+        Dim CheckThreads As New List(Of Thread)
+        For Each Entry In JavaEntries
+            Dim CheckThread As New Thread(Sub()
+                                              Try
+                                                  Entry.Check()
+                                                  Log("[Java] " & Entry.ToString)
+                                                  SyncLock ListLock
+                                                      JavaCheckList.Add(Entry)
+                                                  End SyncLock
+                                              Catch ex As Exception
+                                                  If Entry.IsUserImport Then
+                                                      Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常,将被自动移除", LogLevel.Hint)
+                                                  Else
+                                                      Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常")
+                                                  End If
+                                              End Try
+                                          End Sub)
+            CheckThreads.Add(CheckThread)
+            CheckThread.Start()
+        Next
+
+        '等待构造线程完成
+Wait:
+        Thread.Sleep(10)
+        For Each CheckThread In CheckThreads
+            If CheckThread.IsAlive Then GoTo Wait
+        Next
+    End Function
+    ''' <summary>
+    ''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
+    ''' </summary>
+    ''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
+    ''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
+    Private Sub JavaSearchFolder(OriginalPath As String, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
+        Try
+            Log("[Java] 开始" & If(IsFullSearch, "完全", "部分") & "遍历查找:" & OriginalPath)
+            JavaSearchFolder(New DirectoryInfo(OriginalPath), Results, Source, IsFullSearch)
+        Catch ex As UnauthorizedAccessException
+            Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath)
+        Catch ex As Exception
+            Log(ex, "遍历查找 Java 时出错(" & OriginalPath & ")")
+        End Try
+    End Sub
+    ''' <summary>
+    ''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
+    ''' </summary>
+    ''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
+    ''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
+    Private Sub JavaSearchFolder(OriginalPath As DirectoryInfo, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
+        Try
+            '确认目录存在
+            If Not OriginalPath.Exists Then Exit Sub
+            Dim Path As String = OriginalPath.FullName.Replace("\\", "\")
+            If Not Path.EndsWith("\") Then Path += "\"
+            '若该目录有 Java,则加入结果
+            If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
+            '查找其下的所有文件夹
+            For Each FolderInfo As DirectoryInfo In OriginalPath.EnumerateDirectories
+                If FolderInfo.Attributes.HasFlag(FileAttributes.ReparsePoint) Then Continue For '跳过符号链接
+                Dim SearchEntry = GetFolderNameFromPath(FolderInfo.Name).ToLower '用于搜索的字符串
+                If IsFullSearch OrElse
+                        FolderInfo.Parent.Name.ToLower = "users" OrElse
+                        SearchEntry.Contains("java") OrElse SearchEntry.Contains("jdk") OrElse SearchEntry.Contains("env") OrElse
+                        SearchEntry.Contains("环境") OrElse SearchEntry.Contains("run") OrElse SearchEntry.Contains("软件") OrElse
+                        SearchEntry.Contains("jre") OrElse SearchEntry = "bin" OrElse SearchEntry.Contains("mc") OrElse
+                        SearchEntry.Contains("software") OrElse SearchEntry.Contains("cache") OrElse SearchEntry.Contains("temp") OrElse
+                        SearchEntry.Contains("corretto") OrElse SearchEntry.Contains("roaming") OrElse SearchEntry.Contains("users") OrElse
+                        SearchEntry.Contains("craft") OrElse SearchEntry.Contains("program") OrElse SearchEntry.Contains("世界") OrElse
+                        SearchEntry.Contains("net") OrElse SearchEntry.Contains("游戏") OrElse SearchEntry.Contains("oracle") OrElse
+                        SearchEntry.Contains("game") OrElse SearchEntry.Contains("file") OrElse SearchEntry.Contains("data") OrElse
+                        SearchEntry.Contains("jvm") OrElse SearchEntry.Contains("服务") OrElse SearchEntry.Contains("server") OrElse
+                        SearchEntry.Contains("客户") OrElse SearchEntry.Contains("client") OrElse SearchEntry.Contains("整合") OrElse
+                        SearchEntry.Contains("应用") OrElse SearchEntry.Contains("运行") OrElse SearchEntry.Contains("前置") OrElse
+                        SearchEntry.Contains("mojang") OrElse SearchEntry.Contains("官启") OrElse SearchEntry.Contains("新建文件夹") OrElse
+                        SearchEntry.Contains("eclipse") OrElse SearchEntry.Contains("microsoft") OrElse SearchEntry.Contains("hotspot") OrElse
+                        SearchEntry.Contains("runtime") OrElse SearchEntry.Contains("x86") OrElse SearchEntry.Contains("x64") OrElse
+                        SearchEntry.Contains("forge") OrElse SearchEntry.Contains("原版") OrElse SearchEntry.Contains("optifine") OrElse
+                        SearchEntry.Contains("官方") OrElse SearchEntry.Contains("启动") OrElse SearchEntry.Contains("hmcl") OrElse
+                        SearchEntry.Contains("mod") OrElse SearchEntry.Contains("高清") OrElse SearchEntry.Contains("download") OrElse
+                        SearchEntry.Contains("launch") OrElse SearchEntry.Contains("程序") OrElse SearchEntry.Contains("path") OrElse
+                        SearchEntry.Contains("version") OrElse SearchEntry.Contains("baka") OrElse SearchEntry.Contains("pcl") OrElse
+                        SearchEntry.Contains("local") OrElse SearchEntry.Contains("packages") OrElse SearchEntry.Contains("4297127D64EC6") OrElse '官启文件夹
+                        SearchEntry.Contains("国服") OrElse SearchEntry.Contains("网易") OrElse SearchEntry.Contains("ext") OrElse '网易 Java 文件夹名
+                        SearchEntry.Contains("netease") OrElse SearchEntry.Contains("1.") OrElse SearchEntry.Contains("启动") Then
+                    JavaSearchFolder(FolderInfo, Results, Source)
+                End If
+            Next
+        Catch ex As UnauthorizedAccessException
+            Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath.FullName)
+        Catch ex As Exception
+            Log(ex, "遍历查找 Java 时出错(" & OriginalPath.FullName & ")")
+        End Try
+    End Sub
+
+#End Region
+
+#Region "下载"
+
+    ''' <summary>
+    ''' 提示 Java 缺失,并弹窗确认是否自动下载。返回玩家选择是否下载。
+    ''' 支持下载 Java 8、14、17,输入其他 VersionCode 会要求玩家自行下载。
+    ''' </summary>
+    Public Function JavaDownloadConfirm(VersionCode As String) As Boolean
+        If VersionCode = 8 OrElse VersionCode = 14 OrElse VersionCode = 17 Then
+            Return MyMsgBox($"PCL 未找到 Java {VersionCode},是否需要 PCL 自动下载?" & vbCrLf &
+                            $"如果你已经安装了 Java {VersionCode},请在 设置 → 启动选项 → 游戏 Java 中手动导入。",
+                            "未找到 Java", "自动下载", "取消") = 1
+        Else
+            MyMsgBox($"PCL 未找到 Java {VersionCode}。" & vbCrLf &
+                     $"请自行搜索并安装 Java {VersionCode},安装后在 设置 → 启动选项 → 游戏 Java 中重新搜索或导入。",
+                     "未找到 Java")
+            Return False
+        End If
+    End Function
+
+    ''' <summary>
+    ''' 获取下载 Java 8/14/17 的加载器。需要开启 IsForceRestart 以正常刷新 Java 列表。
+    ''' </summary>
+    Public Function JavaFixLoaders(Version As Integer) As LoaderCombo(Of Integer)
+        Return New LoaderCombo(Of Integer)($"下载 Java {Version}", {
+            New LoaderTask(Of Integer, List(Of NetFile))("获取 Java 下载信息", AddressOf JavaFileList) With {.ProgressWeight = 2},
+            New LoaderDownload("下载 Java 文件", New List(Of NetFile)) With {.ProgressWeight = 10},
+            JavaSearchLoader
+        })
+    End Function
+    Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
+        Log("[Java] 开始获取 Java 下载信息")
+        Dim IndexFileStr As String = NetGetCodeByDownload(
+            {"https://download.mcbbs.net/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
+             "https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
+             "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
+        IsJson:=True)
+        '获取下载地址
+        Dim MainEntry As JObject = CType(GetJson(IndexFileStr), JObject)($"windows-x{If(Is32BitSystem, "86", "64")}")
+        Dim Entries = MainEntry.Children.Reverse. '选择最靠后的一项(最新)
+            SelectMany(Function(e As JProperty) CType(e.Value, JArray).Select(Function(v) New KeyValuePair(Of String, JObject)(e.Name, v)))
+        Dim TargetEntry = Entries.First(Function(t) t.Value("version")("name").ToString.StartsWith(Loader.Input))
+        Dim Address As String = TargetEntry.Value("manifest")("url")
+        Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
+        '获取文件列表
+        Dim ListFileStr As String = NetGetCodeByDownload(
+            {Address.Replace("piston-meta.mojang.com", "download.mcbbs.net"),
+             Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"),
+             Address},
+        IsJson:=True)
+        Dim BaseDir As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
+        Dim Results As New List(Of NetFile)
+        For Each File As JProperty In CType(GetJson(ListFileStr), JObject)("files")
+            If CType(File.Value, JObject)("downloads")?("raw") Is Nothing Then Continue For
+            Dim Info As JObject = CType(File.Value, JObject)("downloads")("raw")
+            Dim Checker As New FileChecker(ActualSize:=Info("size"), Hash:=Info("sha1"))
+            If Checker.Check(BaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
+            Dim Url As String = Info("url")
+            Results.Add(New NetFile(
+                            {Url.Replace("piston-data.mojang.com", "download.mcbbs.net"),
+                             Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"),
+                             Url},
+                        BaseDir & File.Name, Checker))
+        Next
+        Loader.Output = Results
+        Log($"[Java] 需要下载 {Results.Count} 个文件,目标文件夹:{BaseDir}")
+    End Sub
+
+#End Region
+
+End Module

+ 48 - 29
Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb

@@ -102,9 +102,7 @@ Public Module ModLaunch
         Try
             '构造主加载器
             Dim LaunchLoader As New LoaderCombo(Of Object)("Minecraft 启动", {
-                New LoaderCombo(Of Integer)("Java 处理", {
-                    New LoaderTask(Of Integer, List(Of NetFile))("Java 验证", AddressOf McLaunchJavaValidate) With {.ProgressWeight = 2}
-                }) With {.ProgressWeight = 2, .Show = False, .Block = False},
+                New LoaderTask(Of Integer, Integer)("获取 Java", AddressOf McLaunchJava) With {.ProgressWeight = 4, .Block = False, .Show = False},
                 McLoginLoader,
                 New LoaderCombo(Of String)("补全文件", DlClientFix(McVersionCurrent, False, AssetsIndexExistsBehaviour.DownloadInBackground, True)) With {.ProgressWeight = 15, .Show = False, .Block = True},
                 New LoaderTask(Of String, List(Of McLibToken))("获取启动参数", AddressOf McLaunchArgumentMain) With {.ProgressWeight = 2},
@@ -1131,7 +1129,7 @@ SystemBrowser:
 #Region "Java 处理"
 
     Private McLaunchJavaSelected As JavaEntry = Nothing
-    Private Sub McLaunchJavaValidate(Task As LoaderTask(Of Integer, List(Of NetFile)))
+    Private Sub McLaunchJava(Task As LoaderTask(Of Integer, Integer))
         Dim MinVer As New Version(0, 0, 0, 0), MaxVer As New Version(999, 999, 999, 999)
 
         'MC 大版本检测
@@ -1188,11 +1186,11 @@ SystemBrowser:
         'Fabric 检测
         If McVersionCurrent.Version.HasFabric Then
             If McVersionCurrent.Version.McCodeMain >= 15 AndAlso McVersionCurrent.Version.McCodeMain <= 16 AndAlso McVersionCurrent.Version.McCodeMain <> -1 Then
-                '1.15 - 1.16:Java 8 - 19
-                MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 19, 999, 999)
+                '1.15 - 1.16:Java 8+
+                MinVer = New Version(1, 8, 0, 0)
             ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 99 Then
-                '1.18+:Java 17 - 19
-                MinVer = New Version(1, 17, 0, 0) : MaxVer = New Version(1, 19, 999, 999)
+                '1.18+:Java 17+
+                MinVer = New Version(1, 17, 0, 0)
             End If
         End If
 
@@ -1213,21 +1211,37 @@ SystemBrowser:
 
         '无合适的 Java
         If Task.IsAborted Then Exit Sub '中断加载会导致 JavaSelect 异常地返回空值,误判找不到 Java
-        McLaunchLog("无合适的 Java,取消启动")
-        If MinVer >= New Version(1, 17, 0, 0) Then
-            '缺少 Java 17
-            JavaMissing(17)
-        ElseIf MinVer >= New Version(1, 16, 0, 0) Then
-            '缺少 Java 16
-            JavaMissing(16)
+        McLaunchLog("无合适的 Java,需要确认是否自动下载")
+        Dim JavaCode As String
+        If MinVer >= New Version(1, 16, 0, 0) Then
+            JavaCode = 17
         ElseIf MaxVer < New Version(1, 8, 0, 0) Then
-            '缺少 Java 7
-            JavaMissing(7)
+            JavaCode = 7
+        ElseIf MinVer > New Version(1, 8, 0, 52) Then
+            '缺少 Java 8 较新版(统一通行证)
+            JavaCode = "8u101"
         Else
-            '缺少 Java 8 较新版
-            JavaMissing(8)
+            JavaCode = 8
+        End If
+        If Not JavaDownloadConfirm(JavaCode) Then Throw New Exception("$$")
+
+        '开始自动下载
+        Dim JavaLoader = JavaFixLoaders(JavaCode)
+        JavaLoader.Start(JavaCode, IsForceRestart:=True)
+        Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted
+            Task.Progress = JavaLoader.Progress
+            Thread.Sleep(10)
+        Loop
+
+        '检查下载结果
+        McLaunchJavaSelected = JavaSelect("$$", MinVer, MaxVer, McVersionCurrent)
+        If Task.IsAborted Then Exit Sub
+        If McLaunchJavaSelected IsNot Nothing Then
+            McLaunchLog("选择的 Java:" & McLaunchJavaSelected.ToString)
+        Else
+            Hint("没有可用的 Java,已取消启动!", HintType.Critical)
+            Throw New Exception("$$")
         End If
-        Throw New Exception("$$")
 
     End Sub
 
@@ -1304,16 +1318,21 @@ SystemBrowser:
         '进服
         Dim Server As String = If(String.IsNullOrEmpty(McLaunchLoader.Input.ServerIp), Setup.Get("VersionServerEnter", McVersionCurrent), McLaunchLoader.Input.ServerIp)
         If Server.Length > 0 Then
-            If Server.Contains(":") Then
-                '包含端口号
-                Arguments += " --server " & Server.Split(":")(0) & " --port " & Server.Split(":")(1)
+            If McVersionCurrent.ReleaseTime > New Date(2023, 4, 4) Then
+                'QuickPlay
+                Arguments += $" --quickPlayMultiplayer ""{Server}"""
             Else
-                '不包含端口号
-                Arguments += " --server " & Server & " --port 25565"
-            End If
-            'OptiFine 警告
-            If McVersionCurrent.Version.HasOptiFine Then
-                Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Critical)
+                '老版本
+                If Server.Contains(":") Then
+                    '包含端口号
+                    Arguments += " --server " & Server.Split(":")(0) & " --port " & Server.Split(":")(1)
+                Else
+                    '不包含端口号
+                    Arguments += " --server " & Server & " --port 25565"
+                End If
+                If McVersionCurrent.Version.HasOptiFine Then 'OptiFine 警告
+                    Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Critical)
+                End If
             End If
         End If
         '自定义

+ 19 - 713
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -2,718 +2,6 @@
 
 Public Module ModMinecraft
 
-#Region "Java"
-
-    '列表初始化
-
-    Public JavaListCacheVersion As Integer = 3
-    ''' <summary>
-    ''' 目前所有可用的 Java。
-    ''' </summary>
-    Public JavaList As New List(Of JavaEntry)
-    ''' <summary>
-    ''' 初始化 Java 列表,但除非没有 Java,否则不进行检查。
-    ''' </summary>
-    Public Sub JavaListInit()
-        JavaList = New List(Of JavaEntry)
-        Try
-            If Setup.Get("CacheJavaListVersion") < JavaListCacheVersion Then
-                '不使用缓存
-                Log("[Java] 要求 Java 列表缓存更新")
-                Setup.Set("CacheJavaListVersion", JavaListCacheVersion)
-            Else
-                '使用缓存
-                For Each JsonEntry In GetJson(Setup.Get("LaunchArgumentJavaAll"))
-                    JavaList.Add(JavaEntry.FromJson(JsonEntry))
-                Next
-            End If
-            If JavaList.Count = 0 Then
-                Log("[Java] 初始化未找到可用的 Java,将自动触发搜索", LogLevel.Developer)
-                JavaSearchLoader.Start(0)
-            Else
-                Log("[Java] 缓存中有 " & JavaList.Count & " 个可用的 Java")
-            End If
-        Catch ex As Exception
-            Log(ex, "初始化 Java 列表失败", LogLevel.Feedback)
-            Setup.Set("LaunchArgumentJavaAll", "[]")
-        End Try
-    End Sub
-
-    '基础环境
-
-    ''' <summary>
-    ''' Path 环境变量。
-    ''' </summary>
-    Private ReadOnly Property PathEnv As String
-        Get
-            If _PathEnv Is Nothing Then _PathEnv = If(Environment.GetEnvironmentVariable("Path"), "")
-            Return _PathEnv
-        End Get
-    End Property
-    Private _PathEnv As String = Nothing
-
-    ''' <summary>
-    ''' JAVA_HOME 环境变量。
-    ''' </summary>
-    Private ReadOnly Property PathJavaHome As String
-        Get
-            If _PathJavaHome Is Nothing Then _PathJavaHome = If(Environment.GetEnvironmentVariable("JAVA_HOME"), "")
-            Return _PathJavaHome
-        End Get
-    End Property
-    Private _PathJavaHome As String = Nothing
-
-    Public Class JavaEntry
-
-        '路径
-        ''' <summary>
-        ''' Java.exe 文件的完整路径。
-        ''' </summary>
-        Public ReadOnly Property PathJava As String
-            Get
-                Return PathFolder & "java.exe"
-            End Get
-        End Property
-        ''' <summary>
-        ''' Javaw.exe 文件的完整路径。
-        ''' </summary>
-        Public ReadOnly Property PathJavaw As String
-            Get
-                Return PathFolder & "javaw.exe"
-            End Get
-        End Property
-        ''' <summary>
-        ''' Javaw.exe 文件所在文件夹的路径,以 \ 结尾。
-        ''' </summary>
-        Public PathFolder As String
-        ''' <summary>
-        ''' 是否为用户手动导入的 Java。
-        ''' </summary>
-        Public IsUserImport As Boolean
-
-        '版本信息
-        ''' <summary>
-        ''' Java 的详细版本。若不足 4 位会在前方补 1,例如 1.16.0.1。
-        ''' 其大版本号为 Minor。
-        ''' </summary>
-        Public Version As Version
-        ''' <summary>
-        ''' Java 的大版本号。
-        ''' </summary>
-        Public ReadOnly Property VersionCode As Integer
-            Get
-                Return Version.Minor
-            End Get
-        End Property
-        ''' <summary>
-        ''' 是否为 Java Runtime Environment。
-        ''' </summary>
-        Public IsJre As Boolean
-        ''' <summary>
-        ''' 是否为 64 位 Java。
-        ''' </summary>
-        Public Is64Bit As Boolean
-        ''' <summary>
-        ''' 是否已设置环境变量。
-        ''' </summary>
-        Public ReadOnly Property HasEnvironment As Boolean
-            Get
-                If PathFolder Is Nothing OrElse PathEnv Is Nothing Then Return False
-                Return PathEnv.Replace("\", "").Replace("/", "").ToLower.Contains(PathFolder.Replace("\", "").ToLower)
-            End Get
-        End Property
-
-        '序列化
-        Public Function ToJson() As JObject
-            Return New JObject({New JProperty("Path", PathFolder), New JProperty("VersionString", Version.ToString), New JProperty("IsJre", IsJre), New JProperty("Is64Bit", Is64Bit), New JProperty("IsUserImport", IsUserImport)})
-        End Function
-        Public Shared Function FromJson(Data As JObject) As JavaEntry
-            Return New JavaEntry(Data("Path"), Data("IsUserImport")) With {.Version = New Version(Data("VersionString")), .IsJre = Data("IsJre"), .Is64Bit = Data("Is64Bit")}
-        End Function
-        ''' <summary>
-        ''' 转化为用户友好的字符串输出。
-        ''' </summary>
-        Public Overrides Function ToString() As String
-            Dim VersionString = Version.ToString
-            If VersionString.StartsWith("1.") Then VersionString = Mid(VersionString, 3)
-            Return If(IsJre, "JRE ", "JDK ") & VersionCode & " (" & VersionString & ")," & If(Is64Bit, "64", "32") & " 位" & If(IsUserImport, ",手动导入", "") & ":" & PathFolder
-        End Function
-
-        '构造
-        ''' <summary>
-        ''' 输入 javaw.exe 文件所在文件夹的路径,不限制结尾。
-        ''' </summary>
-        Public Sub New(Folder As String, IsUserImport As Boolean)
-            If Not Folder.EndsWith("\") Then Folder += "\"
-            PathFolder = Folder.Replace("/", "\")
-            Me.IsUserImport = IsUserImport
-        End Sub
-
-        '方法
-        Private IsChecked As Boolean = False
-        ''' <summary>
-        ''' 检查并获取 Java 详细信息。在 Java 存在异常时抛出错误。
-        ''' </summary>
-        Public Sub Check()
-            If IsChecked Then Exit Sub
-            Dim Output As String = Nothing
-            Try
-                '确定文件存在
-                If Not File.Exists(PathJavaw) Then
-                    Throw New FileNotFoundException("未找到 javaw.exe 文件", PathJavaw)
-                End If
-                If Not File.Exists(PathFolder & "java.exe") Then
-                    Throw New FileNotFoundException("未找到 java.exe 文件", PathFolder & "java.exe")
-                End If
-                IsJre = Not File.Exists(PathFolder & "javac.exe")
-                '运行 -version
-                Output = ShellAndGetOutput(PathFolder & "java.exe", "-version", 15000).ToLower
-                If Output = "" Then Throw New Exception("尝试运行该 Java 失败")
-                If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
-                '获取详细信息
-                Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
-                Do While VersionString.Split(".").Count < 4
-                    If VersionString.StartsWith("1.") Then
-                        VersionString = VersionString & ".0"
-                    Else
-                        VersionString = "1." & VersionString
-                    End If
-                Loop
-                Version = New Version(VersionString)
-                If Version.Minor = 0 Then
-                    Log("[Java] 疑似 X.0.X.X 格式版本号:" & Version.ToString)
-                    Version = New Version(1, Version.Major, Version.Build, Version.Revision)
-                End If
-                Is64Bit = Output.Contains("64-bit")
-                If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New Exception("分析详细信息失败,获取的版本为 " & Version.ToString)
-                '无论如何不允许使用 JRE 16,它完全是炸的,安 OptiFine、Forge 和启动 MC 都会炸
-                If IsJre AndAlso VersionCode = 16 Then Throw New Exception("由于 JRE 16 对 Minecraft 的兼容性很差,因此不再允许使用")
-            Catch ex As Exception
-                Log("[Java] 检查失败的 Java 输出:" & PathFolder & "java.exe" & vbCrLf & If(Output, "无程序输出"))
-                Throw New Exception("检查 Java 失败(" & If(PathJavaw, "Nothing") & ")", ex)
-            End Try
-            IsChecked = True
-        End Sub
-
-    End Class
-
-    '搜索
-
-    ''' <summary>
-    ''' 模糊搜索并获取所有可用的 Java,并在结束后更新设置页面显示。输出将直接写入 JavaList。
-    ''' </summary>
-    Public JavaSearchLoader As New LoaderTask(Of Integer, Integer)("Java Search Loader", AddressOf JavaSearchLoaderSub)
-    Private Sub JavaSearchLoaderSub(Loader As LoaderTask(Of Integer, Integer))
-        If FrmSetupLaunch IsNot Nothing Then
-            RunInUiWait(Sub()
-                            FrmSetupLaunch.ComboArgumentJava.Items.Clear()
-                            FrmSetupLaunch.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
-                        End Sub)
-        End If
-        If FrmVersionSetup IsNot Nothing Then
-            RunInUiWait(Sub()
-                            FrmVersionSetup.ComboArgumentJava.Items.Clear()
-                            FrmVersionSetup.ComboArgumentJava.Items.Add(New ComboBoxItem With {.Content = "加载中……", .IsSelected = True})
-                        End Sub)
-        End If
-
-        Try
-
-            '可能包含 Java 的文件夹列表,以 “\” 结尾,且仅包含 “\”
-            'Key:文件夹地址
-            'Value: 是否为玩家手动导入
-            Dim JavaPreList As New Dictionary(Of String, Boolean)
-
-#Region "模糊查找可能可用的 Java"
-
-            '查找环境变量中的 Java
-            For Each PathInEnv As String In Split((PathEnv & ";" & PathJavaHome).Replace("\\", "\").Replace("/", "\"), ";")
-                PathInEnv = PathInEnv.Trim(" """.ToCharArray())
-                If PathInEnv = "" Then Continue For
-                If Not PathInEnv.EndsWith("\") Then PathInEnv += "\"
-                '粗略检查有效性
-                If File.Exists(PathInEnv & "javaw.exe") Then JavaPreList(PathInEnv) = False
-            Next
-            '查找磁盘中的 Java
-            For Each Disk As DriveInfo In DriveInfo.GetDrives()
-                JavaSearchFolder(Disk.Name, JavaPreList, False)
-            Next
-            '查找 APPDATA 文件夹中的 Java
-            JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
-            '查找启动器目录中的 Java
-            JavaSearchFolder(Path, JavaPreList, False, IsFullSearch:=True)
-            '查找所选 Minecraft 文件夹中的 Java
-            If Not String.IsNullOrWhiteSpace(PathMcFolder) AndAlso Path <> PathMcFolder Then
-                JavaSearchFolder(PathMcFolder, JavaPreList, False, IsFullSearch:=True)
-            End If
-
-            '若不全为符号链接,则清除符号链接的地址
-            Dim JavaWithoutReparse As New Dictionary(Of String, Boolean)
-            For Each Pair In JavaPreList
-                Dim Folder As String = Pair.Key.Replace("\\", "\").Replace("/", "\")
-                Dim Info As FileSystemInfo = New FileInfo(Folder & "javaw.exe")
-                Do
-                    If Info.Attributes.HasFlag(FileAttributes.ReparsePoint) Then
-                        Log("[Java] 位于 " & Folder & " 的 Java 包含符号链接")
-                        Continue For
-                    End If
-                    Info = If(TypeOf Info Is FileInfo, CType(Info, FileInfo).Directory, CType(Info, DirectoryInfo).Parent)
-                Loop While Info IsNot Nothing
-                Log("[Java] 位于 " & Folder & " 的 Java 不含符号链接")
-                JavaWithoutReparse.Add(Pair.Key, Pair.Value)
-            Next
-            If JavaWithoutReparse.Count > 0 Then JavaPreList = JavaWithoutReparse
-
-            '若不全为特殊引用,则清除特殊引用的地址
-            Dim JavaWithoutInherit As New Dictionary(Of String, Boolean)
-            For Each Pair In JavaPreList
-                If Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") Then
-                    Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用")
-                    Continue For
-                End If
-                Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
-                JavaWithoutInherit.Add(Pair.Key, Pair.Value)
-            Next
-            If JavaWithoutInherit.Count > 0 Then JavaPreList = JavaWithoutInherit
-
-#End Region
-
-#Region "添加玩家手动导入的 Java"
-
-            Dim ImportedJava As String = Setup.Get("LaunchArgumentJavaAll")
-            Try
-                For Each JavaJsonObject In GetJson(ImportedJava)
-                    Dim Entry = JavaEntry.FromJson(JavaJsonObject)
-                    If Entry.IsUserImport Then JavaPreList(Entry.PathFolder) = True
-                Next
-            Catch ex As Exception
-                Log(ex, "Java 列表已损坏", LogLevel.Feedback)
-                Setup.Set("LaunchArgumentJavaAll", "[]")
-            End Try
-
-#End Region
-
-            '确保可用并获取详细信息,转入正式列表
-            Dim NewJavaList As New List(Of JavaEntry)
-            For Each Entry In Distinct(JavaPreList.ToList, Function(a, b) a.Key.ToLower = b.Key.ToLower) '#794
-                NewJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
-            Next
-            NewJavaList = Sort(JavaCheckList(NewJavaList), AddressOf JavaSorter)
-
-            '修改设置项
-            Dim AllList As New JArray
-            For Each Java In NewJavaList
-                AllList.Add(Java.ToJson)
-            Next
-            Setup.Set("LaunchArgumentJavaAll", AllList.ToString(Newtonsoft.Json.Formatting.None))
-            JavaList = NewJavaList
-
-        Catch ex As Exception
-            Log(ex, "搜索 Java 时出错", LogLevel.Feedback)
-            JavaList = New List(Of JavaEntry)
-        End Try
-
-        Log("[Java] Java 搜索完成,发现 " & JavaList.Count & " 个 Java")
-        If FrmSetupLaunch IsNot Nothing Then RunInUi(Sub() FrmSetupLaunch.RefreshJavaComboBox())
-        If FrmVersionSetup IsNot Nothing Then RunInUi(Sub() FrmVersionSetup.RefreshJavaComboBox())
-    End Sub
-
-    ''' <summary>
-    ''' 多线程检查列表中的所有 Java 项。
-    ''' </summary>
-    Private Function JavaCheckList(JavaEntries As List(Of JavaEntry)) As List(Of JavaEntry)
-        Log("[Java] 开始确认列表 Java 状态,共 " & JavaEntries.Count & " 项")
-        JavaCheckList = New List(Of JavaEntry)
-        Dim ListLock As New Object
-
-        '启动检查线程
-        Dim CheckThreads As New List(Of Thread)
-        For Each Entry In JavaEntries
-            Dim CheckThread As New Thread(Sub()
-                                              Try
-                                                  Entry.Check()
-                                                  Log("[Java] " & Entry.ToString)
-                                                  SyncLock ListLock
-                                                      JavaCheckList.Add(Entry)
-                                                  End SyncLock
-                                              Catch ex As Exception
-                                                  If Entry.IsUserImport Then
-                                                      Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常,将被自动移除", LogLevel.Hint)
-                                                  Else
-                                                      Log(ex, "位于 " & Entry.PathFolder & " 的 Java 存在异常")
-                                                  End If
-                                              End Try
-                                          End Sub)
-            CheckThreads.Add(CheckThread)
-            CheckThread.Start()
-        Next
-
-        '等待构造线程完成
-Wait:
-        Thread.Sleep(10)
-        For Each CheckThread In CheckThreads
-            If CheckThread.IsAlive Then GoTo Wait
-        Next
-    End Function
-    ''' <summary>
-    ''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
-    ''' </summary>
-    ''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
-    ''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
-    Private Sub JavaSearchFolder(OriginalPath As String, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
-        Try
-            Log("[Java] 开始" & If(IsFullSearch, "完全", "部分") & "遍历查找:" & OriginalPath)
-            JavaSearchFolder(New DirectoryInfo(OriginalPath), Results, Source, IsFullSearch)
-        Catch ex As UnauthorizedAccessException
-            Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath)
-        Catch ex As Exception
-            Log(ex, "遍历查找 Java 时出错(" & OriginalPath & ")")
-        End Try
-    End Sub
-    ''' <summary>
-    ''' 模糊搜索指定文件夹下的 Java,并只进行粗略的检查。这不会搜索全部路径。
-    ''' </summary>
-    ''' <param name="OriginalPath">开始搜索的起始路径,不限制结尾。</param>
-    ''' <param name="IsFullSearch">搜索当前文件夹下的全部文件夹(此参数不会传递到子文件夹)。</param>
-    Private Sub JavaSearchFolder(OriginalPath As DirectoryInfo, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
-        Try
-            '确认目录存在
-            If Not OriginalPath.Exists Then Exit Sub
-            Dim Path As String = OriginalPath.FullName.Replace("\\", "\")
-            If Not Path.EndsWith("\") Then Path += "\"
-            '若该目录有 Java,则加入结果
-            If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
-            '查找其下的所有文件夹
-            For Each FolderInfo As DirectoryInfo In OriginalPath.EnumerateDirectories
-                If FolderInfo.Attributes.HasFlag(FileAttributes.ReparsePoint) Then Continue For '跳过符号链接
-                Dim SearchEntry = GetFolderNameFromPath(FolderInfo.Name).ToLower '用于搜索的字符串
-                If IsFullSearch OrElse
-                        FolderInfo.Parent.Name.ToLower = "users" OrElse
-                        SearchEntry.Contains("java") OrElse SearchEntry.Contains("jdk") OrElse SearchEntry.Contains("env") OrElse
-                        SearchEntry.Contains("环境") OrElse SearchEntry.Contains("run") OrElse SearchEntry.Contains("软件") OrElse
-                        SearchEntry.Contains("jre") OrElse SearchEntry = "bin" OrElse SearchEntry.Contains("mc") OrElse
-                        SearchEntry.Contains("software") OrElse SearchEntry.Contains("cache") OrElse SearchEntry.Contains("temp") OrElse
-                        SearchEntry.Contains("corretto") OrElse SearchEntry.Contains("roaming") OrElse SearchEntry.Contains("users") OrElse
-                        SearchEntry.Contains("craft") OrElse SearchEntry.Contains("program") OrElse SearchEntry.Contains("世界") OrElse
-                        SearchEntry.Contains("net") OrElse SearchEntry.Contains("游戏") OrElse SearchEntry.Contains("oracle") OrElse
-                        SearchEntry.Contains("game") OrElse SearchEntry.Contains("file") OrElse SearchEntry.Contains("data") OrElse
-                        SearchEntry.Contains("jvm") OrElse SearchEntry.Contains("服务") OrElse SearchEntry.Contains("server") OrElse
-                        SearchEntry.Contains("客户") OrElse SearchEntry.Contains("client") OrElse SearchEntry.Contains("整合") OrElse
-                        SearchEntry.Contains("应用") OrElse SearchEntry.Contains("运行") OrElse SearchEntry.Contains("前置") OrElse
-                        SearchEntry.Contains("mojang") OrElse SearchEntry.Contains("官启") OrElse SearchEntry.Contains("新建文件夹") OrElse
-                        SearchEntry.Contains("eclipse") OrElse SearchEntry.Contains("microsoft") OrElse SearchEntry.Contains("hotspot") OrElse
-                        SearchEntry.Contains("runtime") OrElse SearchEntry.Contains("x86") OrElse SearchEntry.Contains("x64") OrElse
-                        SearchEntry.Contains("forge") OrElse SearchEntry.Contains("原版") OrElse SearchEntry.Contains("optifine") OrElse
-                        SearchEntry.Contains("官方") OrElse SearchEntry.Contains("启动") OrElse SearchEntry.Contains("hmcl") OrElse
-                        SearchEntry.Contains("mod") OrElse SearchEntry.Contains("高清") OrElse SearchEntry.Contains("download") OrElse
-                        SearchEntry.Contains("launch") OrElse SearchEntry.Contains("程序") OrElse SearchEntry.Contains("path") OrElse
-                        SearchEntry.Contains("国服") OrElse SearchEntry.Contains("网易") OrElse SearchEntry.Contains("ext") OrElse '网易 Java 文件夹名
-                        SearchEntry.Contains("netease") OrElse SearchEntry.Contains("1.") OrElse SearchEntry.Contains("启动") Then
-                    JavaSearchFolder(FolderInfo, Results, Source)
-                End If
-            Next
-        Catch ex As UnauthorizedAccessException
-            Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath.FullName)
-        Catch ex As Exception
-            Log(ex, "遍历查找 Java 时出错(" & OriginalPath.FullName & ")")
-        End Try
-    End Sub
-
-    '获取
-
-    ''' <summary>
-    ''' 根据要求返回最适合的 Java,若找不到则返回 Nothing。
-    ''' 最小与最大版本在与输入相同时也会通过。
-    ''' 必须在工作线程调用。
-    ''' </summary>
-    Public Function JavaSelect(CancelException As String, Optional MinVersion As Version = Nothing, Optional MaxVersion As Version = Nothing,
-                               Optional RelatedVersion As McVersion = Nothing) As JavaEntry
-        Try
-            Dim AllowedJavaList As New List(Of JavaEntry)
-
-            '添加特定的 Java
-            Dim JavaPreList As New Dictionary(Of String, Boolean)
-            If PathMcFolder.Split("\").Count > 3 Then JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
-            JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹
-            If RelatedVersion IsNot Nothing Then JavaSearchFolder(RelatedVersion.Path, JavaPreList, False, True) '所选版本文件夹
-            Dim TargetJavaList As New List(Of JavaEntry)
-            For Each Entry In JavaPreList
-                TargetJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
-            Next
-
-#Region "添加用户指定的 Java,储存到 UserJava 中"
-
-            Dim UserJava As JavaEntry = Nothing
-
-            '获取版本独立设置中指定的 Java
-            If RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion).ToString.StartsWith("{") Then
-                Try
-                    UserJava = JavaEntry.FromJson(GetJson(Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)))
-                    '确保版本独立设置中指定的 Java 在 Java 列表中(#978)
-                    If Not JavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
-                        UserJava = Nothing
-                        Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
-                        Log("版本独立设置中指定的 Java 不在主 Java 列表中,此设置已重置", LogLevel.Debug)
-                        Exit Try
-                    End If
-                Catch ex As Exception
-                    UserJava = Nothing
-                    Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
-                    Log(ex, "版本独立设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
-                End Try
-            End If
-
-            '获取全局设置中指定的 Java
-            If UserJava Is Nothing AndAlso Setup.Get("LaunchArgumentJavaSelect") <> "" Then
-                Try
-                    UserJava = JavaEntry.FromJson(GetJson(Setup.Get("LaunchArgumentJavaSelect")))
-                Catch ex As Exception
-                    UserJava = Nothing
-                    Setup.Reset("LaunchArgumentJavaSelect")
-                    Log(ex, "设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
-                End Try
-            End If
-
-            '添加到特定 Java 列表
-            If UserJava IsNot Nothing Then
-                Log($"[Java] 用户指定的 Java:{UserJava}")
-                TargetJavaList.Add(UserJava)
-            End If
-
-#End Region
-
-            '检查特定的 Java
-            If TargetJavaList.Count > 0 Then
-                TargetJavaList = JavaCheckList(TargetJavaList)
-                Log("[Java] 检查后找到 " & TargetJavaList.Count & " 个特定的 Java")
-            End If
-
-RetryGet:
-            '等待进行中的搜索结束
-            If JavaSearchLoader.State <> LoadState.Finished AndAlso JavaSearchLoader.State <> LoadState.Waiting Then JavaSearchLoader.WaitForExit()
-            Select Case JavaSearchLoader.State
-                Case LoadState.Failed
-                    Throw JavaSearchLoader.Error
-                Case LoadState.Aborted
-                    Throw New ThreadInterruptedException("Java 搜索加载器已中断")
-            End Select
-
-            '生成完整的 Java 列表
-            Dim AllJavaList As New List(Of JavaEntry)
-            AllJavaList.AddRange(TargetJavaList)
-            AllJavaList.AddRange(JavaList)
-
-            '根据选定条件进行过滤
-            For Each Java In AllJavaList
-                If MinVersion IsNot Nothing AndAlso Java.Version < MinVersion Then Continue For
-                If MaxVersion IsNot Nothing AndAlso Java.Version > MaxVersion Then Continue For
-                If Java.Is64Bit AndAlso Is32BitSystem Then Continue For
-                AllowedJavaList.Add(Java)
-            Next
-
-            '若未找到适合的 Java,尝试触发搜索
-            If AllowedJavaList.Count = 0 AndAlso JavaSearchLoader.State = LoadState.Waiting Then
-                Log("[Java] 未找到满足条件的 Java,尝试进行搜索")
-                JavaSearchLoader.Start()
-                GoTo RetryGet
-            End If
-
-#Region "检查用户指定的 Java 是否可用"
-
-            '确保指定的 Java 可用
-            If UserJava Is Nothing Then GoTo ExitUserJavaCheck
-            If AllowedJavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
-                Log("[Java] 使用用户指定的 Java:" & UserJava.PathFolder)
-                AllowedJavaList = New List(Of JavaEntry) From {UserJava}
-                GoTo UserPass
-            End If
-
-            '指定的 Java 不可用,弹窗要求选择
-            Log("[Java] 发现用户指定的不兼容 Java:" & UserJava.ToString)
-            For Each Java In AllowedJavaList
-                Log($"[Java] 实际可用的 Java:{Java.ToString}")
-            Next
-            Dim Requirement As String = ""
-            Dim ShowRevision As Boolean = False
-            If (MinVersion Is Nothing OrElse MinVersion.Minor = 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
-                ShowRevision = MaxVersion.MinorRevision < 999
-                Requirement = "最高兼容到 Java " & MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
-            ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion Is Nothing OrElse MaxVersion.Minor >= 999) Then
-                ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0
-                Requirement = "至少需要 Java " & MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
-            ElseIf (MinVersion IsNot Nothing AndAlso MinVersion.Minor > 0) AndAlso (MaxVersion IsNot Nothing AndAlso MaxVersion.Minor < 999) Then
-                ShowRevision = MinVersion.MinorRevision > 0 OrElse MinVersion.MajorRevision > 0 OrElse MaxVersion.MinorRevision < 999
-                Dim Left As String = MinVersion.Minor & If(ShowRevision, "." & MinVersion.MajorRevision & "." & MinVersion.MinorRevision, "")
-                Dim Right As String = MaxVersion.Minor & If(ShowRevision, "." & MaxVersion.MajorRevision & "." & MaxVersion.MinorRevision, "")
-                Requirement = "需要 Java " & If(Left = Right, Left, Left & " ~ " & Right)
-            End If
-            Dim JavaCurrent As String = UserJava.VersionCode & If(ShowRevision, "." & UserJava.Version.MajorRevision & "." & UserJava.Version.MinorRevision, "")
-            If Setup.Get("LaunchAdvanceJava") Then
-                '直接跳过弹窗
-                Hint("设置中指定了使用 Java " & JavaCurrent & ",但当前版本" & Requirement & ",这可能会导致游戏崩溃!", HintType.Critical)
-                AllowedJavaList = New List(Of JavaEntry) From {UserJava}
-            Else
-                Select Case MyMsgBox("你在设置中手动指定了使用 Java " & JavaCurrent & ",但当前" & Requirement & "。" & vbCrLf &
-                            "如果强制使用该 Java,可能导致游戏崩溃。" & vbCrLf &
-                            "你也可以将 游戏 Java 设置修改为 自动选择合适的 Java。" & vbCrLf &
-                            vbCrLf &
-                            " - 指定的 Java:" & UserJava.ToString,
-                            "Java 兼容性警告", "让 PCL 自动选择", "强制使用该 Java", "取消")
-                    Case 1 '让 PCL 自动选择
-                    Case 2 '强制使用指定的 Java
-                        Log("[Java] 已强制使用用户指定的不兼容 Java")
-                        AllowedJavaList = New List(Of JavaEntry) From {UserJava}
-                    Case 3 '取消启动
-                        Throw New Exception(CancelException)
-                End Select
-            End If
-
-ExitUserJavaCheck:
-#End Region
-
-            '若依然未找到适合的 Java,直接返回
-            If AllowedJavaList.Count = 0 Then Return Nothing
-
-            '优先使用特定目录下的 Java
-            For Each Java In AllowedJavaList
-                If TargetJavaList.Contains(Java) Then
-                    '直接使用指定的 Java
-                    AllowedJavaList = New List(Of JavaEntry) From {Java}
-                    Log("[Java] 优先使用特定的 Java:" & Java.ToString)
-                    GoTo UserPass
-                End If
-            Next
-UserPass:
-
-            '对适合的 Java 进行排序
-            AllowedJavaList = Sort(AllowedJavaList, AddressOf JavaSorter)
-
-            '检查选定的 Java,若测试失败则尝试进行搜索
-            Dim SelectedJava = AllowedJavaList.First
-            Try
-                SelectedJava.Check()
-            Catch ex As ThreadInterruptedException
-                Throw
-            Catch ex As Exception
-                Log(ex, "找到的 Java 已无法使用,尝试进行搜索")
-                AllowedJavaList = New List(Of JavaEntry)
-                JavaSearchLoader.Start(IsForceRestart:=True)
-                GoTo RetryGet
-            End Try
-
-            '返回
-            Log("[Java] 选定的 Java:" & SelectedJava.ToString)
-            Return SelectedJava
-
-        Catch ex As ThreadInterruptedException
-            Log(ex, "查找符合条件的 Java 时出现加载器中断")
-            Return Nothing
-        Catch ex As Exception
-            If ex.Message = "$$" Then Throw ex
-            Log(ex, "查找符合条件的 Java 失败", LogLevel.Feedback)
-            Return Nothing
-        End Try
-    End Function
-    ''' <summary>
-    ''' 返回是否安装了 64 位 Java,或在强制指定的时候是否指定 64 位 Java。
-    ''' </summary>
-    Public Function JavaUse64Bit(Optional RelatedVersion As McVersion = Nothing) As Boolean
-        Try
-            '检查强制指定
-            Dim UserSetup As String = Setup.Get("LaunchArgumentJavaSelect")
-            If RelatedVersion IsNot Nothing Then
-                Dim UserSetupVersion As String = Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)
-                If UserSetupVersion <> "使用全局设置" Then UserSetup = UserSetupVersion
-            End If
-            If UserSetup <> "" Then
-                Dim UserJava As JavaEntry = Nothing
-                Try
-                    UserJava = JavaEntry.FromJson(GetJson(UserSetup))
-                Catch ex As Exception
-                    Log(ex, "版本指定的 Java 信息已损坏,已重置版本设置中指定的 Java")
-                    Setup.Set("VersionArgumentJavaSelect", "使用全局设置", Version:=RelatedVersion)
-                    GoTo NoUserJava
-                End Try
-                For Each Java In JavaList
-                    If Java.PathFolder = UserJava.PathFolder Then Return UserJava.Is64Bit
-                Next
-            End If
-NoUserJava:
-            '检查列表
-            For Each Java In JavaList
-                If Java.Is64Bit Then Return True
-            Next
-            Return False
-        Catch ex As Exception
-            Log(ex, "检查 Java 类别时出错", LogLevel.Feedback)
-            Setup.Set("LaunchArgumentJavaSelect", "")
-            Return True
-        End Try
-    End Function
-    ''' <summary>
-    ''' 用于 Java 排序的函数。
-    ''' </summary>
-    Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
-        '1. 尽量在当前文件夹或当前 Minecraft 文件夹
-        Dim ProgramPathParent As String, MinecraftPathParent As String = ""
-        ProgramPathParent = If(New DirectoryInfo(Path).Parent, New DirectoryInfo(Path)).FullName
-        If PathMcFolder <> "" Then MinecraftPathParent = If(New DirectoryInfo(PathMcFolder).Parent, New DirectoryInfo(PathMcFolder)).FullName
-        If Left.PathFolder.StartsWith(ProgramPathParent) AndAlso Not Right.PathFolder.StartsWith(ProgramPathParent) Then Return True
-        If Not Left.PathFolder.StartsWith(ProgramPathParent) AndAlso Right.PathFolder.StartsWith(ProgramPathParent) Then Return False
-        If PathMcFolder <> "" Then
-            If Left.PathFolder.StartsWith(MinecraftPathParent) AndAlso Not Right.PathFolder.StartsWith(MinecraftPathParent) Then Return True
-            If Not Left.PathFolder.StartsWith(MinecraftPathParent) AndAlso Right.PathFolder.StartsWith(MinecraftPathParent) Then Return False
-        End If
-        '2. 尽量使用 64 位
-        If Left.Is64Bit AndAlso Not Right.Is64Bit Then Return True
-        If Not Left.Is64Bit AndAlso Right.Is64Bit Then Return False
-        '3. 尽量不使用 JDK
-        If Left.IsJre AndAlso Not Right.IsJre Then Return True
-        If Not Left.IsJre AndAlso Right.IsJre Then Return False
-        '4. Java 大版本
-        If Left.VersionCode <> Right.VersionCode Then
-            '                             Java  7   8   9  10  11  12 13 14 15  16  17  18  19  20...
-            Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 29, 10, 11, 12, 13, 9, 8, 7, 15, 31, 30, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}
-            Return Weight.ElementAtOrDefault(Left.VersionCode) >= Weight.ElementAtOrDefault(Right.VersionCode)
-        End If
-        '5. 最次级版本号更接近 51
-        Return Math.Abs(Left.Version.Revision - 51) <= Math.Abs(Right.Version.Revision - 51)
-    End Function
-
-    '缺失
-    ''' <summary>
-    ''' 提示 Java 缺失并跳转下载。支持 Java 7、8、16、17。
-    ''' </summary>
-    Public Sub JavaMissing(VersionCode As Integer)
-        Select Case VersionCode
-            Case 7
-                MyMsgBox("PCL 未找到 Java 7。" & vbCrLf &
-                         "请自行百度安装 Java 7,安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。",
-                         "未找到 Java")
-            Case 8 '291
-                If Is32BitSystem Then
-                    OpenWebsite("https://wwa.lanzoui.com/i7RyXq0jbub")
-                Else
-                    OpenWebsite("https://wwa.lanzoui.com/i2UyMq0jaqb")
-                End If
-                MyMsgBox("PCL 未找到版本适宜的 Java 8。" & vbCrLf &
-                         "请在打开的网页中下载安装包并安装,安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。",
-                         "未找到 Java")
-            Case 16, 17
-                If Is32BitSystem Then
-                    MyMsgBox("该版本的 MC 已不支持 32 位操作系统。你必须增加内存并重装为 64 位系统才能继续。", "系统兼容性提示")
-                Else
-                    OpenWebsite("https://www.oracle.com/java/technologies/downloads/#java17")
-                    MyMsgBox("PCL 未找到 Java 17。" & vbCrLf &
-                             "请在打开的网页中选择 x64 Installer,下载并安装。" & vbCrLf &
-                             "安装后在 PCL 的 设置 → 启动设置 → 游戏 Java 中通过搜索或导入,确保安装的 Java 已列入 Java 列表。",
-                             "未找到 Java")
-                End If
-        End Select
-    End Sub
-
-#End Region
-
 #Region "文件夹"
 
     ''' <summary>
@@ -1701,6 +989,8 @@ ExitDataLoad:
             Return "我们从地下室的废墟里找到了这个开发于 1994 年的杰作!"
         ElseIf Name = "22w13oneblockatatime" Then
             Return "一次一个方块更新!迎接全新的挖掘、合成与骑乘玩法吧!"
+        ElseIf Name = "23w13a_or_b" Then
+            Return "研究表明玩家喜欢作出选择——越多越好!"
         Else
             Return ""
         End If
@@ -2265,6 +1555,19 @@ NextVersion:
         Dim c = Integer.Parse(Uuid(23), Globalization.NumberStyles.AllowHexSpecifier)
         Dim d = Integer.Parse(Uuid(31), Globalization.NumberStyles.AllowHexSpecifier)
         Return If((a Xor b Xor c Xor d) Mod 2, "Alex", "Steve")
+        'Math.floorMod(uuid.hashCode(), 18)
+
+        'Public Function hashCode(ByVal str As String) As Integer
+        'Dim hash As Integer = 0
+        'Dim n As Integer = str.Length
+        'If n = 0 Then
+        '    Return hash
+        'End If
+        'For i As Integer = 0 To n - 1
+        '    hash = hash + Asc(str(i)) * (1 << (n - i - 1))
+        'Next
+        'Return hash
+        'End Function
     End Function
 
 #End Region
@@ -2366,7 +1669,10 @@ NextVersion:
                 End If
             End If
             If Not IsNothing(Rule("features")) Then '标签
-                IsRightRule = IsRightRule AndAlso IsNothing(Rule("features")("is_demo_user")) '只反选是否为 Demo 用户
+                IsRightRule = IsRightRule AndAlso IsNothing(Rule("features")("is_demo_user")) '反选是否为 Demo 用户
+                If CType(Rule("features"), JObject).Children.Any(Function(j As JProperty) j.Name.StartsWith("is_quick_play")) Then
+                    IsRightRule = False '不开 Quick Play,让玩家自己加去
+                End If
             End If
 
             '反选确认

+ 12 - 2
Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb

@@ -95,7 +95,7 @@
                                        TimerLog()
                                        '设置窗口标题
                                        For i = 1 To 3
-                                           If IsWindowFinished AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
+                                           If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
                                                Dim RealTitle As String = WindowTitle.Replace("{time}", Date.Now.ToShortTimeString)
                                                SetWindowText(WindowHandle, RealTitle.ToCharArray)
                                            End If
@@ -248,6 +248,9 @@
 
         '窗口检查
         Private IsWindowAppeared As Boolean = False
+        ''' <summary>
+        ''' 窗口检查是否已经完成。这不一定代表着找到了窗口(如果没有找到,IsWindowAppeared 仍为 False)。
+        ''' </summary>
         Private IsWindowFinished As Boolean = False
         Private WindowHandle As IntPtr
         Private Sub TimerWindow()
@@ -255,7 +258,14 @@
                 If GameProcess.HasExited Then Exit Sub
                 If IsWindowFinished Then Exit Sub
                 '获取全部窗口,检查是否有新增的
-                Dim MinecraftWindow = TryGetMinecraftWindow()
+                Dim MinecraftWindow As KeyValuePair(Of IntPtr, String)? = Nothing
+                Try
+                    MinecraftWindow = TryGetMinecraftWindow()
+                Catch ex As ComponentModel.Win32Exception
+                    '拒绝访问(#1062)
+                    Log(ex, "由于反作弊或安全软件拦截,PCL 无法操作游戏窗口", LogLevel.Hint)
+                    IsWindowFinished = True
+                End Try
                 If MinecraftWindow Is Nothing Then Exit Sub
                 Dim MinecraftWindowName = MinecraftWindow.Value.Value, MinecraftWindowHandle = MinecraftWindow.Value.Key
                 '已找到窗口

+ 6 - 6
Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml

@@ -49,17 +49,17 @@
     <Grid Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Bottom"
           IsHitTestVisible="False" SnapsToDevicePixels="False" UseLayoutRounding="False">
         <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="Auto" />
-            <ColumnDefinition Width="Auto" SharedSizeGroup="CompItemVersion" />
-            <ColumnDefinition Width="0.7*" x:Name="ColumnVersionMargin" />
+            <ColumnDefinition x:Name="ColumnVersion1" Width="Auto" />
+            <ColumnDefinition x:Name="ColumnVersion2" Width="Auto" SharedSizeGroup="CompItemVersion" />
+            <ColumnDefinition x:Name="ColumnVersion3" Width="0.7*" />
             
             <ColumnDefinition Width="Auto" />
             <ColumnDefinition Width="55" />
             <ColumnDefinition Width="1*" />
 
-            <ColumnDefinition Width="Auto" />
-            <ColumnDefinition Width="Auto" MinWidth="57" SharedSizeGroup="CompItemTime" />
-            <ColumnDefinition Width="1*" />
+            <ColumnDefinition x:Name="ColumnTime1"  Width="Auto" />
+            <ColumnDefinition x:Name="ColumnTime2" Width="Auto" MinWidth="57" SharedSizeGroup="CompItemTime" />
+            <ColumnDefinition x:Name="ColumnTime3" Width="1*" />
 
             <ColumnDefinition Width="Auto" />
             <ColumnDefinition Width="Auto" SharedSizeGroup="CompItemSource" />

+ 4 - 1
Plain Craft Launcher 2/Modules/ModMusic.vb

@@ -259,7 +259,10 @@
                     PreviousVolume = Setup.Get("UiMusicVolume")
                     CurrentWave.Volume = PreviousVolume / 1000
                 End If
-                Thread.Sleep(50)
+                '更新进度条
+                Dim Percent = Reader.CurrentTime.TotalMilliseconds / Reader.TotalTime.TotalMilliseconds
+                RunInUi(Sub() FrmMain.BtnExtraMusic.Progress = Percent)
+                Thread.Sleep(100)
             End While
             '当前音乐已播放结束,继续下一曲
             If CurrentWave.PlaybackState = NAudio.Wave.PlaybackState.Stopped AndAlso MusicAllList.Count > 0 Then MusicStartPlay(DequeueNextMusicAddress)

+ 2 - 2
Plain Craft Launcher 2/My Project/AssemblyInfo.vb

@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
 ' 方法是按如下所示使用“*”
 
-<Assembly: AssemblyVersion("2.5.2.0")>
-<Assembly: AssemblyFileVersion("2.5.2.0")>
+<Assembly: AssemblyVersion("2.5.3.0")>
+<Assembly: AssemblyFileVersion("2.5.3.0")>
 <Assembly: NeutralResourcesLanguage("")>

+ 22 - 4
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -382,8 +382,17 @@ Public Module ModDownloadLib
         '选择 Java
         Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
         If Java Is Nothing Then
-            JavaMissing(17)
-            Throw New Exception("未找到用于安装 OptiFine 的 Java")
+            If Not JavaDownloadConfirm(17) Then Throw New Exception("由于未找到 Java,已取消安装。")
+            '开始自动下载
+            Dim JavaLoader = JavaFixLoaders(17)
+            JavaLoader.Start(17, IsForceRestart:=True)
+            Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted
+                Thread.Sleep(10)
+            Loop
+            '检查下载结果
+            Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+            If Task.IsAborted Then Exit Sub
+            If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
         End If
         '添加 Java Wrapper 作为主 Jar
         Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -Duser.home=""" & BaseMcFolderHome & """ -cp """ & Target & """ -jar """ & ExtractJavaWrapper() & """ optifine.Installer"
@@ -1099,8 +1108,17 @@ Public Module ModDownloadLib
         '选择 Java
         Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
         If Java Is Nothing Then
-            JavaMissing(17)
-            Throw New Exception("未找到用于安装 Forge 的 Java")
+            If Not JavaDownloadConfirm(17) Then Throw New Exception("由于未找到 Java,已取消安装。")
+            '开始自动下载
+            Dim JavaLoader = JavaFixLoaders(17)
+            JavaLoader.Start(17, IsForceRestart:=True)
+            Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted
+                Thread.Sleep(10)
+            Loop
+            '检查下载结果
+            Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+            If Task.IsAborted Then Exit Sub
+            If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
         End If
         '添加 Java Wrapper 作为主 Jar
         Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -cp """ & PathTemp & "Cache\forge_installer.jar;" & Target & """ -jar """ & ExtractJavaWrapper() & """  com.bangbang93.ForgeInstaller """ & McFolder

+ 1 - 1
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml.vb

@@ -39,7 +39,7 @@
                                 Version("id") = "20w14∞"
                                 Version("type") = "special"
                                 Version.Add("lore", GetMcFoolName(Version("id")))
-                            Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime"
+                            Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b"
                                 Type = "愚人节版"
                                 Version("type") = "special"
                                 Version.Add("lore", GetMcFoolName(Version("id")))

+ 1 - 3
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml

@@ -100,9 +100,7 @@
                         </Grid>
                     </local:MyCard>
                     <local:MyCard Title="Fabric" Height="40" Margin="0,15,0,0" x:Name="CardFabric" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
-                        <StackPanel Margin="20,40,18,15" VerticalAlignment="Top">
-                            <StackPanel Name="PanFabric" />
-                        </StackPanel>
+                        <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" />
                         <Grid x:Name="PanFabricInfo" Height="18" Margin="175,11,15,0" VerticalAlignment="Top" Tag="True">
                             <Grid.RenderTransform>
                                 <TranslateTransform />

+ 5 - 5
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb

@@ -522,7 +522,7 @@
                                 Version("id") = "20w14∞"
                                 Version("type") = "special"
                                 Version.Add("lore", GetMcFoolName(Version("id")))
-                            Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime"
+                            Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b"
                                 Type = "愚人节版"
                                 Version("type") = "special"
                                 Version.Add("lore", GetMcFoolName(Version("id")))
@@ -859,16 +859,16 @@
             If Versions.Count = 0 Then Exit Sub
             '可视化
             PanFabric.Children.Clear()
-            For Each Version In Versions
-                PanFabric.Children.Add(FabricDownloadListItem(Version, AddressOf Fabric_Selected))
-            Next
+            PanFabric.Tag = Versions
+            CardFabric.SwapControl = PanFabric
+            CardFabric.SwapType = 12
         Catch ex As Exception
             Log(ex, "可视化 Fabric 安装版本列表出错", LogLevel.Feedback)
         End Try
     End Sub
 
     '选择与清除
-    Private Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
+    Public Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
         SelectedFabric = sender.Tag("version").ToString
         FabricApi_Loaded()
         OptiFabric_Loaded()

+ 40 - 34
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml

@@ -10,22 +10,55 @@
                 <StackPanel Margin="25,40,25,15">
                     <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
                         <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" />
-                            <ColumnDefinition Width="1*" />
-                            <ColumnDefinition Width="70" />
                             <ColumnDefinition Width="Auto" />
                             <ColumnDefinition Width="1.3*" />
                             <ColumnDefinition Width="1.2*" />
+                            <ColumnDefinition Width="50" />
+                            <ColumnDefinition Width="Auto" />
+                            <ColumnDefinition Width="1*" />
                         </Grid.ColumnDefinitions>
                         <Grid.RowDefinitions>
                             <RowDefinition Height="28" />
                             <RowDefinition Height="9" />
                             <RowDefinition Height="28" />
                         </Grid.RowDefinitions>
-                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,25,0" />
-                        <local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="10" />
-                        <TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,25,0" />
-                        <local:MyComboBox x:Name="ComboSearchTag" Grid.Column="1" Grid.Row="2" MaxDropDownHeight="320">
+                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,18,0" />
+                        <local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="2" />
+                        <TextBlock VerticalAlignment="Center" Grid.Column="4" HorizontalAlignment="Left" Text="来源" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="ComboSearchSource" Grid.Column="5">
+                            <local:MyComboBoxItem Content="全部" Tag="3" IsSelected="True" />
+                            <local:MyComboBoxItem Content="CurseForge" Tag="1" />
+                            <local:MyComboBoxItem Content="Modrinth" Tag="2" />
+                        </local:MyComboBox>
+                        <TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" IsEditable="True" MaxDropDownHeight="320">
+                            <local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
+                            <local:MyComboBoxItem Content="1.19.4" />
+                            <local:MyComboBoxItem Content="1.19.3" />
+                            <local:MyComboBoxItem Content="1.19.2" />
+                            <local:MyComboBoxItem Content="1.19.1" />
+                            <local:MyComboBoxItem Content="1.19" />
+                            <local:MyComboBoxItem Content="1.18.2" />
+                            <local:MyComboBoxItem Content="1.17.1" />
+                            <local:MyComboBoxItem Content="1.16.5" />
+                            <local:MyComboBoxItem Content="1.15.2" />
+                            <local:MyComboBoxItem Content="1.14.4" />
+                            <local:MyComboBoxItem Content="1.13.2" />
+                            <local:MyComboBoxItem Content="1.12.2" />
+                            <local:MyComboBoxItem Content="1.11.2" />
+                            <local:MyComboBoxItem Content="1.10.2" />
+                            <local:MyComboBoxItem Content="1.9.4" />
+                            <local:MyComboBoxItem Content="1.8.9" />
+                            <local:MyComboBoxItem Content="1.7.10" />
+                        </local:MyComboBox>
+                        <local:MyComboBox x:Name="ComboSearchLoader" Visibility="Collapsed" Grid.Row="2" Grid.Column="2" MaxDropDownHeight="320" Margin="10,0,0,0">
+                            <local:MyComboBoxItem Content="任意 Mod 加载器" IsSelected="True" Tag="0" />
+                            <local:MyComboBoxItem Content="Forge" Tag="1" />
+                            <local:MyComboBoxItem Content="Fabric" Tag="4" />
+                            <local:MyComboBoxItem Content="Quilt" Tag="5" />
+                        </local:MyComboBox>
+                        <TextBlock VerticalAlignment="Center" Grid.Column="4" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="ComboSearchTag" Grid.Column="5" Grid.Row="2" MaxDropDownHeight="320">
                             <local:MyComboBoxItem Content="全部" Tag="" IsSelected="True" />
                             <local:MyComboBoxItem Content="世界元素" Tag="406/worldgen" />
                             <local:MyComboBoxItem Content="生物群系" Tag="407/" Margin="15,0,0,0" />
@@ -53,33 +86,6 @@
                             <local:MyComboBoxItem Content="改良" Tag="5191/utility" />
                             <local:MyComboBoxItem Content="支持库" Tag="421/library" />
                         </local:MyComboBox>
-                        <TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="版本" Margin="0,0,25,0" />
-                        <local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="2" IsEditable="True" MaxDropDownHeight="320">
-                            <local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.3" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <local:MyComboBoxItem Content="1.19.1" />
-                            <local:MyComboBoxItem Content="1.19" />
-                            <local:MyComboBoxItem Content="1.18.2" />
-                            <local:MyComboBoxItem Content="1.17.1" />
-                            <local:MyComboBoxItem Content="1.16.5" />
-                            <local:MyComboBoxItem Content="1.15.2" />
-                            <local:MyComboBoxItem Content="1.14.4" />
-                            <local:MyComboBoxItem Content="1.13.2" />
-                            <local:MyComboBoxItem Content="1.12.2" />
-                            <local:MyComboBoxItem Content="1.11.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <local:MyComboBoxItem Content="1.9.4" />
-                            <local:MyComboBoxItem Content="1.8.9" />
-                            <local:MyComboBoxItem Content="1.7.10" />
-                        </local:MyComboBox>
-                        <local:MyComboBox x:Name="ComboSearchLoader" Visibility="Collapsed" Grid.Row="2" Grid.Column="5" MaxDropDownHeight="320" Margin="10,0,0,0">
-                            <local:MyComboBoxItem Content="任意 Mod 加载器" IsSelected="True" Tag="0" />
-                            <local:MyComboBoxItem Content="Forge" Tag="1" />
-                            <local:MyComboBoxItem Content="Fabric" Tag="4" />
-                            <local:MyComboBoxItem Content="Quilt" Tag="5" />
-                        </local:MyComboBox>
                     </Grid>
                     <Grid Margin="0,4,0,6" Height="35">
                         <Grid.ColumnDefinitions>

+ 2 - 1
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb

@@ -12,13 +12,14 @@
     End Sub
     Private Shared Function LoaderInput() As CompProjectRequest
         Dim Request As New CompProjectRequest(CompType.Mod, Storage, (Page + 1) * PageSize)
-        If FrmDownloadMod IsNot Nothing AndAlso FrmDownloadMod.IsLoaded Then
+        If FrmDownloadMod IsNot Nothing Then
             With Request
                 .SearchText = FrmDownloadMod.TextSearchName.Text
                 .GameVersion = If(FrmDownloadMod.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
                     If(FrmDownloadMod.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadMod.TextSearchVersion.Text.Contains("w"), FrmDownloadMod.TextSearchVersion.Text, Nothing))
                 .Tag = FrmDownloadMod.ComboSearchTag.SelectedItem.Tag
                 .ModLoader = Val(FrmDownloadMod.ComboSearchLoader.SelectedItem.Tag)
+                .Source = CType(Val(FrmDownloadMod.ComboSearchSource.SelectedItem.Tag), CompSourceType)
             End With
         End If
         Return Request

+ 34 - 28
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml

@@ -10,43 +10,27 @@
                 <StackPanel Margin="25,40,25,15">
                     <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
                         <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" />
-                            <ColumnDefinition Width="1*" />
-                            <ColumnDefinition Width="70" />
                             <ColumnDefinition Width="Auto" />
                             <ColumnDefinition Width="2.5*" />
+                            <ColumnDefinition Width="50" />
+                            <ColumnDefinition Width="Auto" />
+                            <ColumnDefinition Width="1*" />
                         </Grid.ColumnDefinitions>
                         <Grid.RowDefinitions>
                             <RowDefinition Height="28" />
                             <RowDefinition Height="9" />
                             <RowDefinition Height="28" />
                         </Grid.RowDefinitions>
-                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,25,0" />
-                        <local:MyTextBox x:Name="TextSearchName" Grid.Column="1" Grid.ColumnSpan="10" HintText="仅支持使用英文搜索" />
-                        <TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="类型" Margin="0,0,25,0" />
-                        <local:MyComboBox x:Name="ComboSearchTag" Grid.Column="1" Grid.Row="2" MaxDropDownHeight="320">
-                            <local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
-                            <local:MyComboBoxItem Tag="4484/" Content="多人" />
-                            <local:MyComboBoxItem Tag="/optimization" Content="性能优化" />
-                            <local:MyComboBoxItem Tag="4479/challenging" Content="硬核" />
-                            <local:MyComboBoxItem Tag="4483/combat" Content="战斗" />
-                            <local:MyComboBoxItem Tag="4478/quests" Content="任务" />
-                            <local:MyComboBoxItem Tag="4472/technology" Content="科技" />
-                            <local:MyComboBoxItem Tag="4473/magic" Content="魔法" />
-                            <local:MyComboBoxItem Tag="4475/adventure" Content="冒险" />
-                            <local:MyComboBoxItem Tag="/kitchen-sink" Content="烹饪" />
-                            <local:MyComboBoxItem Tag="4476/" Content="探索" />
-                            <local:MyComboBoxItem Tag="4477/" Content="小游戏" />
-                            <local:MyComboBoxItem Tag="4471/" Content="科幻" />
-                            <local:MyComboBoxItem Tag="4736/" Content="空岛" />
-                            <local:MyComboBoxItem Tag="5128/" Content="原版改良" />
-                            <local:MyComboBoxItem Tag="4487/" Content="FTB" ToolTip="该整合包由 FTB 官方发布" />
-                            <local:MyComboBoxItem Tag="4480/" Content="基于地图" />
-                            <local:MyComboBoxItem Tag="4481/lightweight" Content="轻量整合" />
-                            <local:MyComboBoxItem Tag="4482/" Content="大型整合" />
+                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="名称" Margin="0,0,18,0" />
+                        <local:MyTextBox x:Name="TextSearchName" Grid.Column="1" />
+                        <TextBlock VerticalAlignment="Center" Grid.Column="3" HorizontalAlignment="Left" Text="来源" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="ComboSearchSource" Grid.Column="4">
+                            <local:MyComboBoxItem Content="全部" Tag="3" IsSelected="True" />
+                            <local:MyComboBoxItem Content="CurseForge" Tag="1" />
+                            <local:MyComboBoxItem Content="Modrinth" Tag="2" />
                         </local:MyComboBox>
-                        <TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="版本" Margin="0,0,25,0" />
-                        <local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="4" IsEditable="True" MaxDropDownHeight="320">
+                        <TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" IsEditable="True" MaxDropDownHeight="320">
                             <local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
                             <local:MyComboBoxItem Content="1.19.4" />
                             <local:MyComboBoxItem Content="1.19.3" />
@@ -66,6 +50,28 @@
                             <local:MyComboBoxItem Content="1.8.9" />
                             <local:MyComboBoxItem Content="1.7.10" />
                         </local:MyComboBox>
+                        <TextBlock VerticalAlignment="Center" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Left" Text="类型" Margin="0,0,18,0" />
+                        <local:MyComboBox x:Name="ComboSearchTag" Grid.Column="4" Grid.Row="2" MaxDropDownHeight="320">
+                            <local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
+                            <local:MyComboBoxItem Tag="4484/" Content="多人" />
+                            <local:MyComboBoxItem Tag="/optimization" Content="性能优化" />
+                            <local:MyComboBoxItem Tag="4479/challenging" Content="硬核" />
+                            <local:MyComboBoxItem Tag="4483/combat" Content="战斗" />
+                            <local:MyComboBoxItem Tag="4478/quests" Content="任务" />
+                            <local:MyComboBoxItem Tag="4472/technology" Content="科技" />
+                            <local:MyComboBoxItem Tag="4473/magic" Content="魔法" />
+                            <local:MyComboBoxItem Tag="4475/adventure" Content="冒险" />
+                            <local:MyComboBoxItem Tag="/kitchen-sink" Content="烹饪" />
+                            <local:MyComboBoxItem Tag="4476/" Content="探索" />
+                            <local:MyComboBoxItem Tag="4477/" Content="小游戏" />
+                            <local:MyComboBoxItem Tag="4471/" Content="科幻" />
+                            <local:MyComboBoxItem Tag="4736/" Content="空岛" />
+                            <local:MyComboBoxItem Tag="5128/" Content="原版改良" />
+                            <local:MyComboBoxItem Tag="4487/" Content="FTB" ToolTip="该整合包由 FTB 官方发布" />
+                            <local:MyComboBoxItem Tag="4480/" Content="基于地图" />
+                            <local:MyComboBoxItem Tag="4481/lightweight" Content="轻量整合" />
+                            <local:MyComboBoxItem Tag="4482/" Content="大型整合" />
+                        </local:MyComboBox>
                     </Grid>
                     <Grid Margin="0,4,0,6" Height="35">
                         <Grid.ColumnDefinitions>

+ 3 - 2
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb

@@ -6,18 +6,19 @@
     Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject ModPack", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
     Public Shared Storage As New CompProjectStorage
     Public Shared Page As Integer = 0
-    Private Sub PageDownloadMod_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
+    Private Sub PageDownloadPack_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
         PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
         McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1)))
     End Sub
     Private Shared Function LoaderInput() As CompProjectRequest
         Dim Request As New CompProjectRequest(CompType.ModPack, Storage, (Page + 1) * PageSize)
-        If FrmDownloadPack IsNot Nothing AndAlso FrmDownloadPack.IsLoaded Then
+        If FrmDownloadPack IsNot Nothing Then
             With Request
                 .SearchText = FrmDownloadPack.TextSearchName.Text
                 .GameVersion = If(FrmDownloadPack.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
                     If(FrmDownloadPack.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadPack.TextSearchVersion.Text.Contains("w"), FrmDownloadPack.TextSearchVersion.Text, Nothing))
                 .Tag = FrmDownloadPack.ComboSearchTag.SelectedItem.Tag
+                .Source = CType(Val(FrmDownloadPack.ComboSearchSource.SelectedItem.Tag), CompSourceType)
             End With
         End If
         Return Request

+ 6 - 10
Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb

@@ -214,7 +214,7 @@
                           If(RamGame <> RamGameActual, " (可用 " & If(RamGameActual = Math.Floor(RamGameActual), RamGameActual & ".0", RamGameActual) & " GB)", "")
         LabRamUsed.Text = If(RamUsed = Math.Floor(RamUsed), RamUsed & ".0", RamUsed) & " GB"
         LabRamTotal.Text = " / " & If(RamTotal = Math.Floor(RamTotal), RamTotal & ".0", RamTotal) & " GB"
-        LabRamWarn.Visibility = If(RamGame = 1 AndAlso Not JavaUse64Bit() AndAlso Not Is32BitSystem, Visibility.Visible, Visibility.Collapsed)
+        LabRamWarn.Visibility = If(RamGame = 1 AndAlso Not JavaIs64Bit() AndAlso Not Is32BitSystem, Visibility.Visible, Visibility.Collapsed)
         If ShowAnim Then
             '宽度动画
             AniStart({
@@ -413,7 +413,7 @@ PreFin:
             End If
         End If
         '若使用 32 位 Java,则限制为 1G
-        If Not JavaUse64Bit(If(UseVersionJavaSetup, Version, Nothing)) Then RamGive = Math.Min(1, RamGive)
+        If Not JavaIs64Bit(If(UseVersionJavaSetup, Version, Nothing)) Then RamGive = Math.Min(1, RamGive)
         Return RamGive
     End Function
 
@@ -487,18 +487,14 @@ PreFin:
             Exit Sub
         End If
         '选择 Java
-        Dim JavaSelected As String = SelectFile("javaw.exe|javaw.exe", "选择 javaw.exe 文件")
+        Dim JavaSelected As String = SelectFile("javaw.exe|javaw.exe", "选择 bin 文件夹中的 javaw.exe 文件")
         If JavaSelected = "" Then Exit Sub
-        If Not JavaSelected.ToLower.EndsWith("javaw.exe") Then
-            Hint("请选择 bin 文件夹中的 javaw.exe 文件!", HintType.Critical)
-            Exit Sub
-        End If
         JavaSelected = GetPathFromFullPath(JavaSelected)
         Try
             '验证 Java 可用
             Dim NewEntry As New JavaEntry(JavaSelected, True)
             NewEntry.Check()
-            '加入列表并改变选择
+            '加入列表
             Dim JavaNewList As New JArray From {NewEntry.ToJson}
             For Each JsonEntry In GetJson(Setup.Get("LaunchArgumentJavaAll"))
                 Dim Entry = JavaEntry.FromJson(JsonEntry)
@@ -506,9 +502,9 @@ PreFin:
                 JavaNewList.Add(JsonEntry)
             Next
             Setup.Set("LaunchArgumentJavaAll", JavaNewList.ToString(Newtonsoft.Json.Formatting.None))
-            Setup.Set("LaunchArgumentJavaSelect", NewEntry.ToJson.ToString(Newtonsoft.Json.Formatting.None)) '加载器会优先选择设置中的 Java
             '重新加载列表
             JavaSearchLoader.Start(IsForceRestart:=True)
+            Hint("已将该 Java 加入 Java 列表!", HintType.Finish)
         Catch ex As Exception
             Log(ex, "该 Java 存在异常,无法使用", LogLevel.Msgbox, "异常的 Java")
             Exit Sub
@@ -522,7 +518,7 @@ PreFin:
         End If
         RunInThread(Sub()
                         Hint("正在搜索 Java!")
-                        JavaSearchLoader.WaitForExit(GetUuid)
+                        JavaSearchLoader.WaitForExit(IsForceRestart:=True)
                         If JavaList.Count = 0 Then
                             Hint("未找到可用的 Java!", HintType.Critical)
                         Else

+ 2 - 2
Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb

@@ -164,7 +164,7 @@
                           If(RamGame <> RamGameActual, " (可用 " & If(RamGameActual = Math.Floor(RamGameActual), RamGameActual & ".0", RamGameActual) & " GB)", "")
         LabRamUsed.Text = If(RamUsed = Math.Floor(RamUsed), RamUsed & ".0", RamUsed) & " GB"
         LabRamTotal.Text = " / " & If(RamTotal = Math.Floor(RamTotal), RamTotal & ".0", RamTotal) & " GB"
-        LabRamWarn.Visibility = If(RamGame = 1 AndAlso Not JavaUse64Bit(PageVersionLeft.Version) AndAlso Not Is32BitSystem, Visibility.Visible, Visibility.Collapsed)
+        LabRamWarn.Visibility = If(RamGame = 1 AndAlso Not JavaIs64Bit(PageVersionLeft.Version) AndAlso Not Is32BitSystem, Visibility.Visible, Visibility.Collapsed)
         If ShowAnim Then
             '宽度动画
             AniStart({
@@ -349,7 +349,7 @@ PreFin:
             End If
         End If
         '若使用 32 位 Java,则限制为 1G
-        If Not JavaUse64Bit(PageVersionLeft.Version) Then RamGive = Math.Min(1, RamGive)
+        If Not JavaIs64Bit(PageVersionLeft.Version) Then RamGive = Math.Min(1, RamGive)
         Return RamGive
     End Function
 

+ 1 - 0
Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj

@@ -183,6 +183,7 @@
     <Compile Include="Controls\MyPageRight.vb" />
     <Compile Include="Controls\MyPageLeft.vb" />
     <Compile Include="Modules\Minecraft\ModComp.vb" />
+    <Compile Include="Modules\Minecraft\ModJava.vb" />
     <Compile Include="Modules\Minecraft\ModModpack.vb" />
     <Compile Include="Modules\ModEvent.vb" />
     <Compile Include="Controls\MyExtraButton.xaml.vb">

+ 0 - 1
Plain Craft Launcher 2/Resources/Custom.xaml

@@ -280,7 +280,6 @@ local:MyCard(卡片):
 local:MyHint(提示条):
  - Text:设置显示的文本(&#xA; 代表换行)
  - IsWarn:设置为 True 代表警告样式,False 代表提示样式
- - Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
  - HorizontalAlignment:设置对齐方式(Left、Center、Right)
 
 local:MyButton(按钮):

Разница между файлами не показана из-за своего большого размера
+ 129 - 129
Plain Craft Launcher 2/Resources/ModData.txt


Некоторые файлы не были показаны из-за большого количества измененных файлов