Bläddra i källkod

Merge pull request #43 from Hex-Dragon/main

u
薄奚梦灵 1 år sedan
förälder
incheckning
124f5c2f8b
63 ändrade filer med 1277 tillägg och 1991 borttagningar
  1. 2 1
      Plain Craft Launcher 2/Application.xaml
  2. 4 0
      Plain Craft Launcher 2/Application.xaml.vb
  3. 1 1
      Plain Craft Launcher 2/Controls/MyListItem.xaml.vb
  4. 2 2
      Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.vb
  5. 0 1
      Plain Craft Launcher 2/Controls/MyResizer.vb
  6. 1 2
      Plain Craft Launcher 2/Controls/MyScrollViewer.vb
  7. 24 29
      Plain Craft Launcher 2/FormMain.xaml.vb
  8. 15 12
      Plain Craft Launcher 2/Modules/Base/ModBase.vb
  9. 2 2
      Plain Craft Launcher 2/Modules/Base/ModLoader.vb
  10. 14 13
      Plain Craft Launcher 2/Modules/Base/ModValidate.vb
  11. 165 185
      Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
  12. 0 4
      Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
  13. 73 33
      Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
  14. 10 12
      Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
  15. 38 33
      Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
  16. 119 151
      Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
  17. 6 2
      Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
  18. 94 148
      Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
  19. 2 2
      Plain Craft Launcher 2/My Project/AssemblyInfo.vb
  20. 0 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/MyCompItem.xaml
  21. 28 20
      Plain Craft Launcher 2/Pages/PageDownload/Comp/MyCompItem.xaml.vb
  22. 95 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml
  23. 110 31
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb
  24. 0 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml
  25. 47 27
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb
  26. 26 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadDataPack.xaml
  27. 2 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadDataPack.xaml.vb
  28. 35 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadMod.xaml
  29. 2 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadMod.xaml.vb
  30. 28 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadPack.xaml
  31. 2 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadPack.xaml.vb
  32. 45 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadResourcePack.xaml
  33. 2 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadResourcePack.xaml.vb
  34. 31 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml
  35. 2 0
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml.vb
  36. 42 36
      Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
  37. 0 111
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadDataPack.xaml
  38. 0 122
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadDataPack.xaml.vb
  39. 4 4
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
  40. 32 32
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml
  41. 39 26
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb
  42. 0 122
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml
  43. 0 111
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml
  44. 0 128
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb
  45. 0 126
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml
  46. 0 124
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml.vb
  47. 0 115
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml
  48. 0 122
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml.vb
  49. 3 0
      Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb
  50. 30 33
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
  51. 1 1
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb
  52. 6 1
      Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
  53. 9 6
      Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb
  54. 2 1
      Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb
  55. 1 1
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb
  56. 29 21
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
  57. 3 1
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb
  58. 1 1
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb
  59. 0 0
      Plain Craft Launcher 2/Pages/PageVersion/MyLocalModItem.xaml
  60. 0 0
      Plain Craft Launcher 2/Pages/PageVersion/MyLocalModItem.xaml.vb
  61. 5 4
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
  62. 41 29
      Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
  63. 2 2
      Plain Craft Launcher 2/Resources/Custom.xaml

+ 2 - 1
Plain Craft Launcher 2/Application.xaml

@@ -209,7 +209,7 @@
                     </Setter.Value>
                 </Setter>
             </Style>
-            <Style TargetType="local:MyScrollViewer">
+            <Style TargetType="local:MyScrollViewer" x:Name="StyleMyScrollViewer">
                 <Setter Property="OverridesDefaultStyle" Value="True" />
                 <Setter Property="CanContentScroll" Value="False" />
                 <Setter Property="PanningMode" Value="VerticalOnly" />
@@ -225,6 +225,7 @@
                     </Setter.Value>
                 </Setter>
             </Style>
+            <Style TargetType="local:PageComp" BasedOn="{StaticResource {x:Type local:MyScrollViewer}}" />
             <Style TargetType="ScrollViewer" x:Key="ScrollViewerFullMargin">
                 <Setter Property="OverridesDefaultStyle" Value="True" />
                 <Setter Property="CanContentScroll" Value="False" />

+ 4 - 0
Plain Craft Launcher 2/Application.xaml.vb

@@ -115,7 +115,11 @@ WaitRetry:
             LogStart()
             '添加日志
             Log($"[Start] 程序版本:{VersionDisplayName} ({VersionCode}{If(CommitHash = "", "", $",#{CommitHash}")})")
+#If RELEASE Then
+            Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",正式版", "")}")
+#Else
             Log($"[Start] 识别码:{UniqueAddress}{If(ThemeCheckOne(9), ",已解锁反馈主题", "")}")
+#End If
             Log($"[Start] 程序路径:{PathWithName}")
             Log($"[Start] 系统编码:{Encoding.Default.HeaderName} ({Encoding.Default.CodePage}, GBK={IsGBKEncoding})")
             Log($"[Start] 管理员权限:{IsAdmin()}")

+ 1 - 1
Plain Craft Launcher 2/Controls/MyListItem.xaml.vb

@@ -345,7 +345,7 @@ Public Class MyListItem
             Return _Checked
         End Get
         Set(value As Boolean)
-            SetChecked(value, False, True)
+            SetChecked(value, False, value <> _Checked) '仅在值发生变化时触发动画 (#4596)
         End Set
     End Property
     ''' <summary>

+ 2 - 2
Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.vb

@@ -1,4 +1,4 @@
-Public Class MyMsgInput
+Public Class MyMsgInput
 
     Private ReadOnly MyConverter As MyMsgBoxConverter
     Private ReadOnly Uuid As Integer = GetUuid()
@@ -72,7 +72,7 @@ Public Class MyMsgInput
     End Sub
 
     Public Sub Btn1_Click() Handles Btn1.Click
-        TextArea.Validate()
+        TextArea.Validate() '#5773
         If MyConverter.IsExited OrElse Not TextArea.IsValidated Then Exit Sub
         MyConverter.IsExited = True
         MyConverter.Result = TextArea.Text

+ 0 - 1
Plain Craft Launcher 2/Controls/MyResizer.vb

@@ -311,6 +311,5 @@ Public Class MyResizer
     End Sub
 
     Private Declare Function GetCursorPos Lib "user32.dll" (<Out()> ByRef lpPoint As PointAPI) As Boolean
-
     Private Declare Function GetAsyncKeyState Lib "user32.dll" (vKey As Integer) As Short
 End Class

+ 1 - 2
Plain Craft Launcher 2/Controls/MyScrollViewer.vb

@@ -12,8 +12,7 @@
                 (GetType(ComboBox).IsAssignableFrom(SourceType) AndAlso CType(e.Source, ComboBox).IsDropDownOpen) OrElse
                 (GetType(TextBox).IsAssignableFrom(SourceType) AndAlso CType(e.Source, TextBox).AcceptsReturn) OrElse
                 GetType(ComboBoxItem).IsAssignableFrom(SourceType) OrElse
-                TypeOf e.Source Is CheckBox
-           ) Then
+                TypeOf e.Source Is CheckBox) Then
             '如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作
             Exit Sub
         End If

+ 24 - 29
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -10,6 +10,12 @@ Public Class FormMain
         Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
         '统计更新日志条目
 #If BETA Then
+        If LastVersion < 357 Then 'Release 2.10.0
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:下载资源包、光影包、数据包"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:允许设置文件下载源"))
+            FeatureCount += 9
+            BugCount += 26
+        End If
         If LastVersion < 355 Then 'Release 2.9.3
             FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:Minecraft 会优先使用独立显卡运行"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:简化下载新版本第二步的 UI"))
@@ -66,6 +72,12 @@ Public Class FormMain
         '3:BUG+ IMP* FEAT-
         '2:BUG* IMP-
         '1:BUG-
+        If LastVersion < 356 Then 'Snapshot 2.10.0
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:下载资源包、光影包、数据包"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:允许设置文件下载源"))
+            FeatureCount += 9
+            BugCount += 26
+        End If
         If LastVersion < 354 Then 'Snapshot 2.9.3
             If LastVersion = 352 Then
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:低版本 MC 没有声音"))
@@ -74,7 +86,7 @@ Public Class FormMain
             FeatureCount += 8
             BugCount += 8
         End If
-        If LastVersion < 353 Then 'Snapshot 2.9.2
+        If LastVersion < 352 Then 'Snapshot 2.9.2
             FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:Minecraft 会优先使用独立显卡运行"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:简化下载新版本第二步的 UI"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:使用新的版本隔离策略"))
@@ -145,7 +157,7 @@ Public Class FormMain
         '整理更新日志文本
         Dim ContentList As New List(Of String)
         Dim SortedFeatures = FeatureList.OrderByDescending(Function(f) f.Key).ToList
-        If Not SortedFeatures.Any() AndAlso FeatureCount = 0 AndAlso BugCount = 0 Then ContentList.Add("龙猫忘记写更新日志啦!可以去提醒他一下……")
+        If Not SortedFeatures.Any() AndAlso FeatureCount = 0 AndAlso BugCount = 0 Then ContentList.Add("没有更新日志……")
         For i = 0 To Math.Min(9, SortedFeatures.Count - 1) '最多取 10 项
             ContentList.Add(SortedFeatures(i).Value)
         Next
@@ -311,8 +323,8 @@ Public Class FormMain
             '启动加载器池
             Try
                 JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作
-                Thread.Sleep(200)
-                DlClientListMojangLoader.Start(1)
+                Thread.Sleep(100)
+                DlClientListMojangLoader.Start(1) 'PCL 会同时根据这里的加载结果决定是否使用官方源进行下载
                 RunCountSub()
                 ServerLoader.Start(1)
                 RunInNewThread(AddressOf TryClearTaskTemp, "TryClearTaskTemp", ThreadPriority.BelowNormal)
@@ -743,12 +755,12 @@ Public Class FormMain
                     End If
                 Next
             End If
-            '自定义主页
+            '主页
             Dim Extension As String = FilePath.AfterLast(".").ToLower
             If Extension = "xaml" Then
-                Log("[System] 文件后缀为 XAML,作为自定义主页加载")
+                Log("[System] 文件后缀为 XAML,作为主页加载")
                 If File.Exists(Path & "PCL\Custom.xaml") Then
-                    If MyMsgBox("已存在一个自定义主页文件,是否要将它覆盖?", "覆盖确认", "覆盖", "取消") = 2 Then
+                    If MyMsgBox("已存在一个主页文件,是否要将它覆盖?", "覆盖确认", "覆盖", "取消") = 2 Then
                         Exit Sub
                     End If
                 End If
@@ -927,9 +939,9 @@ Public Class FormMain
         DownloadLiteLoader = 9
         DownloadMod = 11
         DownloadPack = 12
-        DownloadResourcePack = 13
-        DownloadShader = 14
-        DownloadDataPack = 15
+        DownloadDataPack = 13
+        DownloadResourcePack = 14
+        DownloadShader = 15
         SetupLaunch = 0
         SetupUI = 1
         SetupSystem = 2
@@ -960,26 +972,9 @@ Public Class FormMain
             Case PageType.VersionSetup
                 Return "版本设置 - " & If(PageVersionLeft.Version Is Nothing, "未知版本", PageVersionLeft.Version.Name)
             Case PageType.CompDetail
-                Dim Project As CompProject = Stack.Additional(0)
-                Select Case Project.Type
-                    Case CompType.Mod
-                        Return "Mod 下载 - " & Project.TranslatedName
-                    Case CompType.ModPack
-                        Return "整合包下载 - " & Project.TranslatedName
-                    Case CompType.DataPack
-                        If Project.IsMix Then
-                            Return "Mod / 数据包下载 - " & Project.TranslatedName
-                        Else
-                            Return "数据包下载 - " & Project.TranslatedName
-                        End If
-                    Case CompType.ResourcePack
-                        Return "资源包下载 - " & Project.TranslatedName
-                    Case Else 'CompType.Shader
-                        Return "光影包下载 - " & Project.TranslatedName
-                End Select
+                Return "资源下载 - " & CType(Stack.Additional(0), CompProject).TranslatedName
             Case PageType.HelpDetail
-                Dim Entry As HelpEntry = Stack.Additional(0)
-                Return Entry.Title
+                Return CType(Stack.Additional(0), HelpEntry).Title
             Case Else
                 Return ""
         End Select

+ 15 - 12
Plain Craft Launcher 2/Modules/Base/ModBase.vb

@@ -13,13 +13,13 @@ Public Module ModBase
 #Region "声明"
 
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.9.3" '不含分支前缀的显示用版本名
-    Public Const VersionStandardCode As String = "2.9.3." & VersionBranchCode '标准格式的四段式版本号
+    Public Const VersionBaseName As String = "2.10.0" '不含分支前缀的显示用版本名
+    Public Const VersionStandardCode As String = "2.10.0." & VersionBranchCode '标准格式的四段式版本号
     Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
 #If BETA Then
-    Public Const VersionCode As Integer = 355 'Release
+    Public Const VersionCode As Integer = 357 'Release
 #Else
-    Public Const VersionCode As Integer = 354 'Snapshot
+    Public Const VersionCode As Integer = 356 'Snapshot
 #End If
     '自动生成的版本信息
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -745,7 +745,6 @@ Public Module ModBase
             Dim IsRight As Boolean = FilePath.EndsWithF("\")
             FilePath = Left(FilePath, Len(FilePath) - 1)
             GetPathFromFullPath = Left(FilePath, FilePath.LastIndexOfAny({"\", "/"})) & If(IsRight, "\", "/")
-            If GetPathFromFullPath = "" Then Throw New Exception("不包含路径:" & FilePath)
         Else
             '是文件路径
             GetPathFromFullPath = Left(FilePath, FilePath.LastIndexOfAny({"\", "/"}) + 1)
@@ -2333,13 +2332,17 @@ NextElement:
     ''' <param name="FileName">文件名。可以为“notepad”等缩写。</param>
     ''' <param name="Arguments">运行参数。</param>
     Public Sub ShellOnly(FileName As String, Optional Arguments As String = "")
-        FileName = ShortenPath(FileName)
-        Using Program As New Process
-            Program.StartInfo.Arguments = Arguments
-            Program.StartInfo.FileName = FileName
-            Log("[System] 执行外部命令:" & FileName & " " & Arguments)
-            Program.Start()
-        End Using
+        Try
+            FileName = ShortenPath(FileName)
+            Using Program As New Process
+                Program.StartInfo.Arguments = Arguments
+                Program.StartInfo.FileName = FileName
+                Log("[System] 执行外部命令:" & FileName & " " & Arguments)
+                Program.Start()
+            End Using
+        Catch ex As Exception
+            Log(ex, "打开文件或程序失败:" & FileName, LogLevel.Msgbox)
+        End Try
     End Sub
     ''' <summary>
     ''' 前台运行文件并返回返回值。

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

@@ -625,8 +625,8 @@ Restart:
     End Sub
     Public Function LoaderTaskbarProgressGet() As Double
         Try
-            If Not LoaderTaskbar.Any Then Return 1
-            Return MathClamp(LoaderTaskbar.Select(Function(e) e.Progress).Average(), 0, 1)
+            If Not LoaderTaskbar.Where(Function(l) l.Show).Any Then Return 1
+            Return MathClamp(LoaderTaskbar.Where(Function(l) l.Show).Select(Function(l) l.Progress).Average(), 0, 1)
         Catch ex As Exception
             Log(ex, "获取任务栏进度出错", LogLevel.Feedback)
             Return 0.5

+ 14 - 13
Plain Craft Launcher 2/Modules/Base/ModValidate.vb

@@ -9,6 +9,7 @@ Public Module ModValidate
         Dim Result As String = ""
         For Each ValidateRule As Validate In ValidateRules
             Result = ValidateRule.Validate(Text)
+            If Result Is Nothing Then Return ""
             If Result <> "" Then Return Result
         Next
         Return Result
@@ -21,7 +22,7 @@ End Module
 ''' </summary>
 Public MustInherit Class Validate
     ''' <summary>
-    ''' 验证某字符串是否符合验证要求。若符合,返回空字符串;若不符合,返回错误原因。
+    ''' 验证某字符串是否符合验证要求。若符合,返回空字符串;若不符合,返回错误原因;若需要中断检查并直接通过,返回 Nothing。
     ''' </summary>
     Public MustOverride Function Validate(Str As String) As String
 End Class
@@ -135,9 +136,9 @@ Public Class ValidateLength
         Me.Max = Max
     End Sub
     Public Overrides Function Validate(Str As String) As String
-        If Len(Str) <> Max AndAlso Max = Min Then Return "长度必须为 " & Max & " 个字符!"
-        If Len(Str) > Max Then Return "长度最长为 " & Max & " 个字符!"
-        If Len(Str) < Min Then Return "长度至少需 " & Min & " 个字符!"
+        If Len(Str) <> Max AndAlso Max = Min Then Return $"长度必须为 {Max} 个字符!"
+        If Len(Str) > Max Then Return $"长度最长为 {Max} 个字符!"
+        If Len(Str) < Min Then Return $"长度至少需 {Min} 个字符!"
         Return ""
     End Function
 End Class
@@ -234,21 +235,21 @@ Public Class ValidateFolderName
         Try
             '检查是否为空
             Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
-            If Not LengthCheck = "" Then Return LengthCheck
+            If LengthCheck <> "" Then Return LengthCheck
             '检查空格
             If Str.StartsWithF(" ") Then Return "文件夹名不能以空格开头!"
             If Str.EndsWithF(" ") Then Return "文件夹名不能以空格结尾!"
             '检查长度
             LengthCheck = New ValidateLength(1, 100).Validate(Str)
-            If Not LengthCheck = "" Then Return LengthCheck
+            If LengthCheck <> "" Then Return LengthCheck
             '检查尾部小数点
             If Str.EndsWithF(".") Then Return "文件夹名不能以小数点结尾!"
             '检查特殊字符
             Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件夹名不可包含 % 字符!").Validate(Str)
-            If Not CharactCheck = "" Then Return CharactCheck
+            If CharactCheck <> "" Then Return CharactCheck
             '检查特殊字符串
             Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件夹名不可为 %!", True).Validate(Str)
-            If Not InvalidStrCheck = "" Then Return InvalidStrCheck
+            If InvalidStrCheck <> "" Then Return InvalidStrCheck
             '检查 NTFS 8.3 文件名(#4505)
             If RegexCheck(Str, ".{2,}~\d") Then Return "文件夹名不能包含这一特殊格式!"
             '检查文件夹重名
@@ -295,15 +296,15 @@ Public Class ValidateFileName
             If Str.EndsWithF(" ") Then Return "文件名不能以空格结尾!"
             '检查长度
             LengthCheck = New ValidateLength(1, 253).Validate(Str & If(ParentFolder, ""))
-            If Not LengthCheck = "" Then Return LengthCheck
+            If LengthCheck <> "" Then Return LengthCheck
             '检查尾部小数点
             If Str.EndsWithF(".") Then Return "文件名不能以小数点结尾!"
             '检查特殊字符
             Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件名不可包含 % 字符!").Validate(Str)
-            If Not CharactCheck = "" Then Return CharactCheck
+            If CharactCheck <> "" Then Return CharactCheck
             '检查特殊字符串
             Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件名不可为 %!", True).Validate(Str)
-            If Not InvalidStrCheck = "" Then Return InvalidStrCheck
+            If InvalidStrCheck <> "" Then Return InvalidStrCheck
             '检查 NTFS 8.3 文件名(#4505)
             If RegexCheck(Str, ".{2,}~\d") Then Return "文件名不能包含这一特殊格式!"
             '检查文件重名
@@ -342,10 +343,10 @@ Public Class ValidateFolderPath
         If Not Str.TrimEnd("\").EndsWithF(":") Then Str = Str.TrimEnd("\")
         '检查是否为空
         Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
-        If Not LengthCheck = "" Then Return LengthCheck
+        If LengthCheck <> "" Then Return LengthCheck
         '检查长度
         LengthCheck = New ValidateLength(1, 254).Validate(Str)
-        If Not LengthCheck = "" Then Return LengthCheck
+        If LengthCheck <> "" Then Return LengthCheck
         '检查开头
         If Str.StartsWithF("\\Mac\") Then GoTo Fin
         For Each Drive As DriveInfo In My.Computer.FileSystem.Drives

+ 165 - 185
Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb

@@ -2,6 +2,10 @@
 
     Public Enum CompType
         ''' <summary>
+        ''' 允许任意种类,或种类未知。
+        ''' </summary>
+        Any = -1
+        ''' <summary> 
         ''' Mod。
         ''' </summary>
         [Mod] = 0
@@ -18,9 +22,14 @@
         ''' </summary>
         Shader = 3
         ''' <summary>
-        ''' 数据包。
+        ''' CurseForge:数据包。
+        ''' Modrinth:数据包,或数据包与 Mod 的混合。
         ''' </summary>
         DataPack = 4
+        ''' <summary>
+        ''' 服务端插件。
+        ''' </summary>
+        Plugin = 5
     End Enum
     Public Enum CompModLoaderType
         'https://docs.curseforge.com/?http#tocS_ModLoaderType
@@ -119,13 +128,10 @@
         Public ReadOnly FromCurseForge As Boolean
         ''' <summary>
         ''' 工程的种类。
+        ''' 由于 Modrinth 混合使用 Mod 和数据包,结果不一定准确。
         ''' </summary>
         Public ReadOnly Type As CompType
         ''' <summary>
-        ''' 是否为 Mod / 数据包融合工程。
-        ''' </summary>
-        Public ReadOnly IsMix As Boolean
-        ''' <summary>
         ''' 工程的短名。例如 technical-enchant。
         ''' </summary>
         Public ReadOnly Slug As String
@@ -190,7 +196,9 @@
             Get
                 If Not LoadedDatabase Then
                     LoadedDatabase = True
-                    If Type = CompType.Mod Then _DatabaseEntry = CompDatabase.FirstOrDefault(Function(c) If(FromCurseForge, c.CurseForgeSlug, c.ModrinthSlug) = Slug)
+                    If Type = CompType.Mod OrElse Type = CompType.DataPack Then
+                        _DatabaseEntry = CompDatabase.FirstOrDefault(Function(c) If(FromCurseForge, c.CurseForgeSlug, c.ModrinthSlug) = Slug)
+                    End If
                 End If
                 Return _DatabaseEntry
             End Get
@@ -266,6 +274,20 @@
                             LogoUrl = Data("logo")("thumbnailUrl")
                         End If
                     End If
+                    'Type
+                    If Website.Contains("/mc-mods/") OrElse Website.Contains("/mod/") Then
+                        Type = CompType.Mod
+                    ElseIf Website.Contains("/modpacks/") Then
+                        Type = CompType.ModPack
+                    ElseIf Website.Contains("/resourcepacks/") Then
+                        Type = CompType.ResourcePack
+                    ElseIf Website.Contains("/texture-packs/") Then
+                        Type = CompType.ResourcePack
+                    ElseIf Website.Contains("/shaders/") Then
+                        Type = CompType.Shader
+                    Else
+                        Type = CompType.DataPack
+                    End If
                     'FileIndexes / GameVersions / ModLoaders
                     ModLoaders = New List(Of CompModLoaderType)
                     Dim Files As New List(Of KeyValuePair(Of Integer, List(Of String))) 'FileId, GameVersions
@@ -286,26 +308,12 @@
                         Select(Function(v) CInt(Val(v.Split(".")(1).BeforeFirst("-")))).Where(Function(v) v > 0).
                         Distinct.OrderByDescending(Function(v) v).ToList
                     ModLoaders = ModLoaders.Distinct.OrderBy(Of Integer)(Function(t) t).ToList
-                    'Type
-                    If Website.Contains("/mc-mods/") OrElse Website.Contains("/mod/") Then
-                        Type = CompType.Mod
-                    ElseIf Website.Contains("/modpacks/") Then
-                        Type = CompType.ModPack
-                    ElseIf Website.Contains("/resourcepacks/") Then
-                        Type = CompType.ResourcePack
-                    ElseIf Website.Contains("/texture-packs/") Then
-                        Type = CompType.ResourcePack
-                    ElseIf Website.Contains("/shaders/")
-                        Type = CompType.Shader
-                    Else
-                        Type = CompType.DataPack
-                    End If
                     'Tags
                     Tags = New List(Of String)
-                    For Each Category In If(Data("categories"), New JArray). '镜像源 API 可能丢失此字段:https://github.com/Hex-Dragon/PCL2/issues/4267#issuecomment-2254590831
+                    For Each Category In If(Data("categories"), New JArray). '镜像源 API 可能丢失此字段 (4267#issuecomment-2254590831)
                         Select(Of Integer)(Function(t) t("id")).Distinct.OrderByDescending(Function(c) c)
                         Select Case Category
-                        'Mod
+                            'Mod
                             Case 406 : Tags.Add("世界元素")
                             Case 407 : Tags.Add("生物群系")
                             Case 410 : Tags.Add("维度")
@@ -329,7 +337,7 @@
                             Case 435 : Tags.Add("服务器")
                             Case 5191 : Tags.Add("改良")
                             Case 421 : Tags.Add("支持库")
-                        '整合包
+                            '整合包
                             Case 4484 : Tags.Add("多人")
                             Case 4479 : Tags.Add("硬核")
                             Case 4483 : Tags.Add("战斗")
@@ -346,39 +354,36 @@
                             Case 4480 : Tags.Add("基于地图")
                             Case 4481 : Tags.Add("轻量")
                             Case 4482 : Tags.Add("大型")
-                        '数据包
-                            Case 6946 : Tags.Add("Mod 支持")
-                            Case 6947 : Tags.Add("杂项")
+                            '资源包
+                            Case 403 : Tags.Add("原版风")
+                            Case 400 : Tags.Add("写实风")
+                            Case 401 : Tags.Add("现代风")
+                            Case 402 : Tags.Add("中世纪")
+                            Case 399 : Tags.Add("蒸汽朋克")
+                            Case 5244 : Tags.Add("含字体")
+                            Case 404 : Tags.Add("动态效果")
+                            Case 4465 : Tags.Add("兼容 Mod")
+                            Case 393 : Tags.Add("16x")
+                            Case 394 : Tags.Add("32x")
+                            Case 395 : Tags.Add("64x")
+                            Case 396 : Tags.Add("128x")
+                            Case 397 : Tags.Add("256x")
+                            Case 398 : Tags.Add("超高清")
+                            Case 5193 : Tags.Add("数据包") '有这个 Tag 的项会从资源包请求中被移除
+                            '光影包
+                            Case 6553 : Tags.Add("写实风")
+                            Case 6554 : Tags.Add("幻想风")
+                            Case 6555 : Tags.Add("原版风")
+                            '数据包
                             Case 6948 : Tags.Add("冒险")
                             Case 6949 : Tags.Add("幻想")
                             Case 6950 : Tags.Add("支持库")
-                            Case 6951 : Tags.Add("科技")
                             Case 6952 : Tags.Add("魔法")
-                            Case 6953 : Tags.Add("实用工具")
-                        '光影包
-                            Case 6553 : Tags.Add("写实")
-                            Case 6554 : Tags.Add("幻想")
-                            Case 6555 : Tags.Add("原版风")
-                        '资源包
-                            Case 5244 : Tags.Add("字体包")
-                            Case 5193 : Tags.Add("数据包")
-                            Case 399 : Tags.Add("蒸汽朋克")
-                            Case 396 : Tags.Add("128x")
-                            Case 398 : Tags.Add("512x 或更高")
-                            Case 397 : Tags.Add("256x")
-                            Case 405 : Tags.Add("其他")
-                            Case 395 : Tags.Add("64x")
-                            Case 400 : Tags.Add("仿真")
-                            Case 393 : Tags.Add("16x")
-                            Case 403 : Tags.Add("传统")
-                            Case 394 : Tags.Add("32x")
-                            Case 404 : Tags.Add("动态效果")
-                            Case 4465 : Tags.Add("模组支持")
-                            Case 402 : Tags.Add("中世纪")
-                            Case 401 : Tags.Add("现代")
+                            Case 6946 : Tags.Add("Mod 相关")
+                            Case 6951 : Tags.Add("科技")
+                            Case 6953 : Tags.Add("实用")
                         End Select
                     Next
-                    If Not Tags.Any() Then Tags.Add("杂项")
 #End Region
                 Else
 #Region "Modrinth"
@@ -400,16 +405,11 @@
                                        Distinct.OrderByDescending(Function(v) v).ToList
                     'Type
                     Select Case Data("project_type").ToString
-                        Case "mod" : Type = CompType.Mod
                         Case "modpack" : Type = CompType.ModPack
                         Case "resourcepack" : Type = CompType.ResourcePack
-                        Case "datapack" : Type = CompType.DataPack
                         Case "shader" : Type = CompType.Shader
+                        Case Else : Type = CompType.Mod 'Modrinth 将数据包标为 Mod
                     End Select
-                    If Data("categories").ToArray.Contains("datapack") Then 'Modrinth 上的数据包由于未知原因,返回的 project_type 为 mod,这里做兜底处理
-                        Type = CompType.DataPack
-                        IsMix = Data("categories").ToArray.Contains("forge") OrElse Data("categories").ToArray.Contains("fabric") OrElse Data("categories").ToArray.Contains("neoforge") OrElse Data("categories").ToArray.Contains("quilt")
-                    End If
                     'Tags & ModLoaders
                     Tags = New List(Of String)
                     ModLoaders = New List(Of CompModLoaderType)
@@ -430,117 +430,75 @@
                             Case "fabric" : ModLoaders.Add(CompModLoaderType.Fabric)
                             Case "quilt" : ModLoaders.Add(CompModLoaderType.Quilt)
                             Case "neoforge" : ModLoaders.Add(CompModLoaderType.NeoForge)
-                            'Mod
-                            Case "worldgen" : Tags.Add("世界元素")
+                            Case "datapack" : Type = CompType.DataPack '若包含数据包版本,则优先标为 DataPack
+                            '共用
                             Case "technology" : Tags.Add("科技")
+                            Case "magic" : Tags.Add("魔法")
+                            Case "adventure" : Tags.Add("冒险")
+                            Case "utility" : Tags.Add("实用")
+                            Case "optimization" : Tags.Add("性能优化")
+                            Case "vanilla-like" : Tags.Add("原版风")
+                            Case "realistic" : Tags.Add("写实风")
+                            'Mod/数据包
+                            Case "worldgen" : Tags.Add("世界元素")
                             Case "food" : Tags.Add("食物/烹饪")
                             Case "game-mechanics" : Tags.Add("游戏机制")
                             Case "transportation" : Tags.Add("运输")
                             Case "storage" : Tags.Add("仓储")
-                            Case "magic" : Tags.Add("魔法")
-                            Case "adventure" : Tags.Add("冒险")
-                            Case "decoration" : Tags.Add("装饰")
-                            Case "mobs" : Tags.Add("生物")
-                            Case "equipment" : Tags.Add("装备")
-                            Case "optimization" : Tags.Add("性能优化")
+                            Case "decoration" : If Type <> CompType.ResourcePack Then Tags.Add("装饰")
+                            Case "mobs" : If Type <> CompType.ResourcePack Then Tags.Add("生物")
+                            Case "equipment" : If Type <> CompType.ResourcePack Then Tags.Add("装备")
                             Case "social" : Tags.Add("服务器")
-                            Case "utility" : Tags.Add("改良")
                             Case "library" : Tags.Add("支持库")
                             '整合包
                             Case "multiplayer" : Tags.Add("多人")
-                            Case "optimization" : Tags.Add("性能优化")
                             Case "challenging" : Tags.Add("硬核")
                             Case "combat" : Tags.Add("战斗")
                             Case "quests" : Tags.Add("任务")
-                            Case "technology" : Tags.Add("科技")
-                            Case "magic" : Tags.Add("魔法")
-                            Case "adventure" : Tags.Add("冒险")
-                            Case "kitchen-sink" : Tags.Add("水槽包/大杂烩")
+                            Case "kitchen-sink" : Tags.Add("水槽包")
                             Case "lightweight" : Tags.Add("轻量")
-                            '数据包
-                            Case "adventure" : Tags.Add("冒险")
-                            Case "cursed" : Tags.Add("Cursed")
-                            Case "decoration" : Tags.Add("装饰")
-                            Case "economy" : Tags.Add("经济")
-                            Case "equipment" : Tags.Add("装备")
-                            Case "food" : Tags.Add("食物")
-                            Case "game-mechanics" : Tags.Add("游戏机制")
-                            Case "library" : Tags.Add("支持库")
-                            Case "magic" : Tags.Add("魔法")
-                            Case "management" : Tags.Add("管理")
-                            Case "minigame" : Tags.Add("小游戏")
-                            Case "mobs" : Tags.Add("生物")
-                            Case "optimization" : Tags.Add("优化")
-                            Case "social" : Tags.Add("社交")
-                            Case "storage" : Tags.Add("存储")
-                            Case "technology" : Tags.Add("科技")
-                            Case "transportation" : Tags.Add("交通")
-                            Case "utility" : Tags.Add("实用工具")
-                            Case "worldgen" : Tags.Add("世界生成")
-                            '光影包
-                            Case "cartoon" : Tags.Add("卡通")
-                            Case "cursed" : Tags.Add("Cursed")
-                            Case "fantasy" : Tags.Add("幻想")
-                            Case "realistic" : Tags.Add("写实")
-                            Case "semi-realistic" : Tags.Add("半写实")
-                            Case "vanilla-like" : Tags.Add("原版风")
-
-                            Case "atmosphere" : Tags.Add("大气环境")
-                            Case "bloom" : Tags.Add("植被")
-                            Case "colored-lighting" : Tags.Add("光源着色")
-                            Case "foliage" : Tags.Add("树叶")
-                            Case "path-tracing" : Tags.Add("路径追踪")
-                            Case "pbr" : Tags.Add("PBR")
-                            Case "reflections" : Tags.Add("反射")
-                            Case "shadows" : Tags.Add("阴影")
-
-                            Case "potato" : Tags.Add("土豆画质")
-                            Case "low" : Tags.Add("低性能影响")
-                            Case "medium" : Tags.Add("中性能影响")
-                            Case "high" : Tags.Add("高性能影响")
-                            Case "screenshot" : Tags.Add("极致画质")
-
-                            Case "canvas" : Tags.Add("Canvas")
-                            Case "iris" : Tags.Add("Iris")
-                            Case "optifine" : Tags.Add("OptiFine")
-                            Case "vanilla" : Tags.Add("原版光影")
                             '资源包
-                            Case "8x-" : Tags.Add("8x-")
+                            Case "simplistic" : Tags.Add("简洁")
+                            Case "combat" : Tags.Add("战斗")
+                            Case "tweaks" : Tags.Add("改良")
+
+                            Case "8x-" : Tags.Add("极简")
                             Case "16x" : Tags.Add("16x")
                             Case "32x" : Tags.Add("32x")
                             Case "48x" : Tags.Add("48x")
                             Case "64x" : Tags.Add("64x")
                             Case "128x" : Tags.Add("128x")
                             Case "256x" : Tags.Add("256x")
-                            Case "512x+" : Tags.Add("512x+")
-                            Case "audio" : Tags.Add("声音")
-                            Case "blocks" : Tags.Add("方块")
-                            Case "combat" : Tags.Add("战斗")
+                            Case "512x+" : Tags.Add("超高清")
+
+                            Case "audio" : Tags.Add("含声音")
+                            Case "fonts" : Tags.Add("含字体")
+                            Case "models" : Tags.Add("含模型")
+                            Case "gui" : Tags.Add("含 UI")
+                            Case "locale" : Tags.Add("含语言")
                             Case "core-shaders" : Tags.Add("核心着色器")
-                            Case "cursed" : Tags.Add("Cursed")
-                            Case "decoration" : Tags.Add("装饰")
-                            Case "entities" : Tags.Add("实体")
-                            Case "environment" : Tags.Add("环境")
-                            Case "equipment" : Tags.Add("装备")
-                            Case "fonts" : Tags.Add("字体")
-                            Case "gui" : Tags.Add("GUI")
-                            Case "items" : Tags.Add("物品")
-                            Case "locale" : Tags.Add("本地化")
-                            Case "modded" : Tags.Add("Modded")
-                            Case "models" : Tags.Add("模型")
-                            Case "realistic" : Tags.Add("写实")
-                            Case "simplistic" : Tags.Add("扁平")
-                            Case "themed" : Tags.Add("主题")
-                            Case "tweaks" : Tags.Add("优化")
-                            Case "utility" : Tags.Add("实用")
-                            Case "vanilla-like" : Tags.Add("类原生")
+                            Case "modded" : Tags.Add("兼容 Mod")
+                            '光影包
+                            Case "fantasy" : Tags.Add("幻想风")
+                            Case "semi-realistic" : Tags.Add("半写实风")
+                            Case "cartoon" : Tags.Add("卡通风")
+                            '暂时不添加性能负荷 Tag
+                            'Case "potato" : Tags.Add("极低")
+                            'Case "low" : Tags.Add("低")
+                            'Case "medium" : Tags.Add("中")
+                            'Case "high" : Tags.Add("高")
+                            Case "colored-lighting" : Tags.Add("彩色光照")
+                            Case "path-tracing" : Tags.Add("路径追踪")
+                            Case "pbr" : Tags.Add("PBR")
+                            Case "reflections" : Tags.Add("反射")
+                            Case "vanilla" : Tags.Add("原版可用")
                         End Select
                     Next
-                    If Not Tags.Any() Then Tags.Add("杂项")
-                    Tags.Sort()
-                    ModLoaders.Sort()
 #End Region
                 End If
+                If Not Tags.Any() Then Tags.Add("其他")
+                Tags.Sort()
+                ModLoaders.Sort()
             End If
             '保存缓存
             CompProjectCache(Id) = Me
@@ -663,7 +621,7 @@
                 NewItem.ColumnVersion1.Width = New GridLength(0)
                 NewItem.ColumnVersion2.MaxWidth = 0
                 NewItem.ColumnVersion3.Width = New GridLength(0)
-            ElseIf ShowMcVersionDesc AndAlso ShowMcVersionDesc Then
+            ElseIf ShowMcVersionDesc AndAlso ShowLoaderDesc Then
                 '全部显示
                 NewItem.LabVersion.Text = If(ModLoaderDescriptionPart = "", "", ModLoaderDescriptionPart & " ") & GameVersionDescription
             ElseIf ShowMcVersionDesc Then
@@ -793,8 +751,8 @@ NoSubtitle:
             If FromCurseForge = Project.FromCurseForge Then Return False
             'Mod 加载器一致
             If ModLoaders.Count <> Project.ModLoaders.Count OrElse ModLoaders.Except(Project.ModLoaders).Any() Then Return False
-            'MC 版本一致
-            If GameVersions.Count <> Project.GameVersions.Count OrElse GameVersions.Except(Project.GameVersions).Any() Then Return False
+            '若不为光影,则要求 MC 版本一致
+            If Type <> CompType.Shader AndAlso (GameVersions.Count <> Project.GameVersions.Count OrElse GameVersions.Except(Project.GameVersions).Any()) Then Return False
             '最近更新时间差距在一周以内
             If LastUpdate IsNot Nothing AndAlso Project.LastUpdate IsNot Nothing AndAlso
                Math.Abs((LastUpdate - Project.LastUpdate).Value.TotalDays) > 7 Then Return False
@@ -1020,7 +978,7 @@ NoSubtitle:
 
         '中文请求关键字处理
         Dim IsChineseSearch As Boolean = RegexCheck(RawFilter, "[\u4e00-\u9fbb]") AndAlso Not String.IsNullOrEmpty(RawFilter)
-        If IsChineseSearch AndAlso Task.Input.Type = CompType.Mod Then
+        If IsChineseSearch AndAlso (Task.Input.Type = CompType.Mod OrElse Task.Input.Type = CompType.DataPack) Then
             '构造搜索请求
             Dim SearchEntries As New List(Of SearchEntry(Of CompDatabaseEntry))
             For Each Entry In CompDatabase
@@ -1111,7 +1069,9 @@ Retry:
                         Task.Progress += 0.2
                         Dim ProjectList As New List(Of CompProject)
                         For Each JsonEntry As JObject In RequestResult("data")
-                            ProjectList.Add(New CompProject(JsonEntry))
+                            Dim Project As New CompProject(JsonEntry)
+                            If Task.Input.Type = CompType.ResourcePack AndAlso Project.Tags.Contains("数据包") Then Continue For 'CurseForge 将一些数据包分类成了资源包
+                            ProjectList.Add(Project)
                         Next
                         '更新结果
                         SyncLock ResultsLock
@@ -1146,7 +1106,6 @@ Retry:
                         '更新结果
                         SyncLock ResultsLock
                             For Each Project In ProjectList
-                                If Task.Input.Type = CompType.Mod AndAlso Not Project.ModLoaders.Any() Then Continue For '过滤插件(#2458)
                                 RawResults.Add(Project)
                             Next
                         End SyncLock
@@ -1174,8 +1133,8 @@ Retry:
                 If [Error] IsNot Nothing Then
                     Throw [Error]
                 Else
-                    If IsChineseSearch AndAlso Task.Input.Type <> CompType.Mod Then
-                        Throw New Exception($"{If(Task.Input.Type = CompType.ModPack, "整合包", "资源包")}搜索仅支持英文")
+                    If IsChineseSearch AndAlso Not (Task.Input.Type = CompType.Mod OrElse Task.Input.Type = CompType.DataPack) Then
+                        Throw New Exception("没有搜索结果,请尝试使用英文搜索")
                     ElseIf Task.Input.Source = CompSourceType.CurseForge AndAlso Task.Input.Tag.StartsWithF("/") Then
                         Throw New Exception("CurseForge 不兼容所选的类型")
                     ElseIf Task.Input.Source = CompSourceType.Modrinth AndAlso Task.Input.Tag.EndsWithF("/") Then
@@ -1233,10 +1192,23 @@ Retry:
 #Region "将结果排序并添加"
 
         Dim Scores As New Dictionary(Of CompProject, Double) '排序分
+        Dim GetDownloadCountMult =
+        Function(Project As CompProject) As Double
+            Select Case Task.Input.Type
+                Case CompType.Mod, CompType.ModPack
+                    Return If(Project.FromCurseForge, 1, 7)
+                Case CompType.DataPack
+                    Return If(Project.FromCurseForge, 10, 1)
+                Case CompType.ResourcePack, CompType.Shader
+                    Return If(Project.FromCurseForge, 1, 5)
+                Case Else
+                    Return 1
+            End Select
+        End Function
         If String.IsNullOrEmpty(Task.Input.SearchText) Then
             '如果没有搜索文本,按下载量将结果排序
             For Each Result As CompProject In RealResults
-                Scores.Add(Result, Result.DownloadCount * If(Result.FromCurseForge, 1, 5))
+                Scores.Add(Result, Result.DownloadCount * GetDownloadCountMult(Result))
             Next
         Else
             '如果有搜索文本,按关联度将结果排序
@@ -1244,7 +1216,7 @@ Retry:
             Dim Entry As New List(Of SearchEntry(Of CompProject))
             For Each Result As CompProject In RealResults
                 Scores.Add(Result, If(Result.WikiId > 0, 0.2, 0) +
-                           Math.Log10(Math.Max(Result.DownloadCount, 1) * If(Result.FromCurseForge, 1, 5)) / 9)
+                           Math.Log10(Math.Max(Result.DownloadCount, 1) * GetDownloadCountMult(Result)) / 9)
                 Entry.Add(New SearchEntry(Of CompProject) With {.Item = Result, .SearchSource = New List(Of KeyValuePair(Of String, Double)) From {
                           New KeyValuePair(Of String, Double)(If(IsChineseSearch, Result.TranslatedName, Result.RawName), 1),
                           New KeyValuePair(Of String, Double)(Result.Description, 0.05)}})
@@ -1327,7 +1299,7 @@ Retry:
                     Case CompFileStatus.Beta
                         Return If(ModeDebug, "Beta 版", "测试版")
                     Case Else
-                        Return If(ModeDebug, "Alpha 版", "测试版")
+                        Return If(ModeDebug, "Alpha 版", "早期测试版")
                 End Select
             End Get
         End Property
@@ -1375,8 +1347,8 @@ Retry:
         ''' <summary>
         ''' 从文件 Json 中初始化实例。若出错会抛出异常。
         ''' </summary>
-        Public Sub New(Data As JObject, Type As CompType)
-            Me.Type = Type
+        Public Sub New(Data As JObject, DefaultType As CompType)
+            Type = DefaultType
             If Data.ContainsKey("FromCurseForge") Then
 #Region "CompJson"
                 FromCurseForge = Data("FromCurseForge").ToObject(Of Boolean)
@@ -1414,7 +1386,7 @@ Retry:
                     DownloadUrls.AddRange(DownloadUrls.Select(Function(u) DlSourceModGet(u)).ToList) '添加镜像源,这个写法是为了让镜像源排在后面
                     DownloadUrls = DownloadUrls.Distinct.ToList '最终去重
                     'Dependencies
-                    If Type = CompType.Mod Then
+                    If Data.ContainsKey("dependencies") Then
                         RawDependencies = Data("dependencies").
                             Where(Function(d) d("relationType").ToObject(Of Integer) = 3 AndAlso '种类为依赖
                                               d("modId").ToObject(Of Integer) <> 306612 AndAlso d("modId").ToObject(Of Integer) <> 634179). '排除 Fabric API 和 Quilt API
@@ -1425,7 +1397,7 @@ Retry:
                     GameVersions = RawVersions.Where(Function(v) v.StartsWithF("1.")).Select(Function(v) v.Replace("-snapshot", " 预览版")).ToList
                     If GameVersions.Count > 1 Then
                         GameVersions = GameVersions.Sort(AddressOf VersionSortBoolean).ToList
-                        If Type = CompType.ModPack Then GameVersions = New List(Of String) From {GameVersions(0)}
+                        If Type = CompType.ModPack Then GameVersions = New List(Of String) From {GameVersions(0)} '整合包理应只 “支持” 一个版本
                     ElseIf GameVersions.Count = 1 Then
                         GameVersions = GameVersions.ToList
                     Else
@@ -1452,8 +1424,27 @@ Retry:
                         DownloadUrls = New List(Of String) From {File("url"), DlSourceModGet(File("url"))}.Distinct.ToList '同时添加了镜像源
                         Hash = File("hashes")("sha1")
                     End If
+                    'ModLoaders
+                    '结果可能混杂着 Mod、数据包和服务端插件
+                    Dim RawLoaders As List(Of String) = Data("loaders").Select(Function(v) v.ToString).ToList
+                    ModLoaders = New List(Of CompModLoaderType)
+                    If Type = CompType.Mod Then '以尽量宽容的方式检测加载器,以免同时兼容两种的项被删除
+                        If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then Type = CompType.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
+                        If RawLoaders.Contains("datapack") Then Type = CompType.DataPack
+                        If RawLoaders.Contains("forge") Then ModLoaders.Add(CompModLoaderType.Forge) : Type = CompType.Mod
+                        If RawLoaders.Contains("neoforge") Then ModLoaders.Add(CompModLoaderType.NeoForge) : Type = CompType.Mod
+                        If RawLoaders.Contains("fabric") Then ModLoaders.Add(CompModLoaderType.Fabric) : Type = CompType.Mod
+                        If RawLoaders.Contains("quilt") Then ModLoaders.Add(CompModLoaderType.Quilt) : Type = CompType.Mod
+                    ElseIf Type = CompType.DataPack Then
+                        If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then Type = CompType.Plugin
+                        If RawLoaders.Contains("forge") Then ModLoaders.Add(CompModLoaderType.Forge) : Type = CompType.Mod
+                        If RawLoaders.Contains("neoforge") Then ModLoaders.Add(CompModLoaderType.NeoForge) : Type = CompType.Mod
+                        If RawLoaders.Contains("fabric") Then ModLoaders.Add(CompModLoaderType.Fabric) : Type = CompType.Mod
+                        If RawLoaders.Contains("quilt") Then ModLoaders.Add(CompModLoaderType.Quilt) : Type = CompType.Mod
+                        If RawLoaders.Contains("datapack") Then Type = CompType.DataPack
+                    End If
                     'Dependencies
-                    If Type = CompType.Mod Then
+                    If Data.ContainsKey("dependencies") Then
                         RawDependencies = Data("dependencies").
                             Where(Function(d) d("dependency_type") = "required" AndAlso '种类为依赖
                                               d("project_id") <> "P7dR8mSH" AndAlso d("project_id") <> "qvIfYCYJ" AndAlso '排除 Fabric API 和 Quilt API
@@ -1466,7 +1457,7 @@ Retry:
                                                Select(Function(v) If(v.Contains("-"), v.BeforeFirst("-") & " 预览版", If(v.StartsWithF("b1."), "远古版本", v))).ToList
                     If GameVersions.Count > 1 Then
                         GameVersions = GameVersions.Sort(AddressOf VersionSortBoolean).ToList
-                        If Type = CompType.ModPack Then GameVersions = New List(Of String) From {GameVersions(0)}
+                        If Type = CompType.ModPack Then GameVersions = New List(Of String) From {GameVersions(0)} '整合包理应只 “支持” 一个版本
                     ElseIf GameVersions.Count = 1 Then
                         '无需处理
                     ElseIf RawVersions.Any(Function(v) RegexCheck(v, "[0-9]{2}w[0-9]{2}[a-z]{1}")) Then
@@ -1474,13 +1465,6 @@ Retry:
                     Else
                         GameVersions = New List(Of String) From {"未知版本"}
                     End If
-                    'ModLoaders
-                    Dim RawLoaders As List(Of String) = Data("loaders").Select(Function(v) v.ToString).ToList
-                    ModLoaders = New List(Of CompModLoaderType)
-                    If RawLoaders.Contains("forge") Then ModLoaders.Add(CompModLoaderType.Forge)
-                    If RawLoaders.Contains("neoforge") Then ModLoaders.Add(CompModLoaderType.NeoForge)
-                    If RawLoaders.Contains("fabric") Then ModLoaders.Add(CompModLoaderType.Fabric)
-                    If RawLoaders.Contains("quilt") Then ModLoaders.Add(CompModLoaderType.Quilt)
 #End Region
                 End If
             End If
@@ -1528,12 +1512,8 @@ Retry:
             Dim Title As String = If(BadDisplayName, FileName, DisplayName)
             Dim Info As New List(Of String)
             If Title <> FileName.BeforeLast(".") Then Info.Add(FileName.BeforeLast("."))
-            Select Case Type
-                Case CompType.Mod
-                    If Dependencies.Any Then Info.Add(Dependencies.Count & " 个前置 Mod")
-                Case CompType.ModPack
-                    If GameVersions.All(Function(v) v.Contains("w")) Then Info.Add($"游戏版本 {Join(GameVersions, "、")}")
-            End Select
+            If Dependencies.Any Then Info.Add(Dependencies.Count & " 项前置")
+            If GameVersions.All(Function(v) Not IsVersionNameLikeRelease(v)) Then Info.Add($"游戏版本 {Join(GameVersions, "、")}")
             If DownloadCount > 0 Then 'CurseForge 的下载次数经常错误地返回 0
                 Info.Add("下载 " & If(DownloadCount > 100000, Math.Round(DownloadCount / 10000) & " 万次", DownloadCount & " 次"))
             End If
@@ -1599,7 +1579,7 @@ Retry:
             TargetProject = New CompProject(DlModRequest("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True))
         End If
         '获取工程对象的文件列表
-        If Not CompFilesCache.ContainsKey(ProjectId) Then '有缓存也不能直接返回,这时候前置 Mod 可能没获取(#5173)
+        If Not CompFilesCache.ContainsKey(ProjectId) Then '有缓存也不能直接返回,这时候前置可能没获取(#5173)
             Log("[Comp] 开始获取文件列表:" & ProjectId)
             Dim ResultJsonArray As JArray
             If FromCurseForge Then
@@ -1617,15 +1597,15 @@ Retry:
                 ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version", IsJson:=True)
             End If
             CompFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)).
-                Where(Function(a) a.Available).ToList.Distinct(Function(a, b) a.Id = b.Id) 'CurseForge 可能会重复返回相同项(#1330)
+                Where(Function(a) a.Available).ToList.
+                Distinct(Function(a, b) a.Id = b.Id) 'CurseForge 可能会重复返回相同项(#1330)
         End If
-        '获取前置 Mod 列表
-        If TargetProject.Type <> CompType.Mod Then Return CompFilesCache(ProjectId)
+        '获取前置列表
         Dim Deps As List(Of String) = CompFilesCache(ProjectId).SelectMany(Function(f) f.RawDependencies).Distinct().ToList
         Dim UndoneDeps = Deps.Where(Function(f) Not CompProjectCache.ContainsKey(f)).ToList
-        '获取前置 Mod 工程信息
+        '获取前置工程信息
         If UndoneDeps.Any Then
-            Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置 Mod:{Join(UndoneDeps, ",")}")
+            Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置:{Join(UndoneDeps, ",")}")
             Dim Projects As JArray
             If TargetProject.FromCurseForge Then
                 Projects = GetJson(DlModRequest("https://api.curseforge.com/v1/mods",
@@ -1634,10 +1614,10 @@ Retry:
                 Projects = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]", IsJson:=True)
             End If
             For Each Project In Projects
-                Dim NewProject As New CompProject(Project) '在 New 的时候就添加了缓存
+                Dim Unused As New CompProject(Project) '在 New 的时候会添加缓存以便之后读取
             Next
         End If
-        '更新前置 Mod 信息
+        '更新前置信息
         If Deps.Any Then
             For Each DepProject In Deps.Where(Function(id) CompProjectCache.ContainsKey(id)).Select(Function(id) CompProjectCache(id))
                 For Each File In CompFilesCache(ProjectId)
@@ -1651,7 +1631,7 @@ Retry:
     End Function
 
     ''' <summary>
-    ''' 预载包含大量 CompFile 的卡片,添加必要的元素和前置 Mod 列表。
+    ''' 预载包含大量 CompFile 的卡片,添加必要的元素和前置列表。
     ''' </summary>
     Public Sub CompFilesCardPreload(Stack As StackPanel, Files As List(Of CompFile))
         '获取卡片对应的前置 ID
@@ -1661,18 +1641,18 @@ Retry:
         If Not Deps.Any() Then Exit Sub
         Deps = Deps.Where(
         Function(dep)
-            If Not CompProjectCache.ContainsKey(dep) Then Log($"[Comp] 未找到 ID {dep} 的前置 Mod 信息", LogLevel.Debug)
+            If Not CompProjectCache.ContainsKey(dep) Then Log($"[Comp] 未找到 ID {dep} 的前置信息", LogLevel.Debug)
             Return CompProjectCache.ContainsKey(dep)
         End Function).ToList
         '添加开头间隔
-        Stack.Children.Add(New TextBlock With {.Text = "前置 Mod", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
-        '添加前置 Mod 列表
+        Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
+        '添加前置列表
         For Each Dep In Deps
             Dim Item = CompProjectCache(Dep).ToCompItem(False, False)
             Stack.Children.Add(Item)
         Next
         '添加结尾间隔
-        Stack.Children.Add(New TextBlock With {.Text = "可选版本", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
+        Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
     End Sub
 
 #End Region

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

@@ -858,7 +858,6 @@ NextStack:
             "确定", If(IsHandAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsHandAnalyze, "", "导出错误报告"),
             Button2Action:=If(IsHandAnalyze OrElse DirectFile Is Nothing, Nothing,
             Sub()
-            Try
                 '弹窗选择:查看日志
                 If File.Exists(DirectFile.Value.Key) Then
                     ShellOnly(DirectFile.Value.Key)
@@ -867,9 +866,6 @@ NextStack:
                     WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf))
                     ShellOnly(FilePath)
                 End If
-            Catch ex As Exception
-                Log(ex,"尝试打开文件失败",LogLevel.Hint)
-            End Try
             End Sub))
             Case 3
                 '弹窗选择:导出错误报告

+ 73 - 33
Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb

@@ -197,10 +197,17 @@
     Public DlClientListMojangLoader As New LoaderTask(Of String, DlClientListResult)("DlClientList Mojang", AddressOf DlClientListMojangMain)
     Private IsNewClientVersionHinted As Boolean = False
     Private Sub DlClientListMojangMain(Loader As LoaderTask(Of String, DlClientListResult))
+        Dim StartTime As Long = GetTimeTick()
         Dim Json As JObject = NetGetCodeByRequestRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", IsJson:=True)
         Try
             Dim Versions As JArray = Json("versions")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
+            '确定官方源是否可用
+            If Not DlPreferMojang Then
+                Dim DeltaTime = GetTimeTick() - StartTime
+                DlPreferMojang = DeltaTime < 4000
+                Log($"[Download] Mojang 官方源加载耗时:{DeltaTime}ms,{If(DlPreferMojang, "可优先使用官方源", "不优先使用官方源")}")
+            End If
             '添加 PCL 特供项
             If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json")))
             '返回
@@ -1171,18 +1178,54 @@
 
 #Region "DlSource | 镜像下载源"
 
-    Public Function DlSourceResourceGet(Original As String) As String()
+    Private DlPreferMojang As Boolean = False
+    ''' <summary>
+    ''' 下载文件(而非获取版本列表)的时候,是否优先使用官方源。
+    ''' </summary>
+    Public ReadOnly Property DlSourcePreferMojang As Boolean
+        Get
+            Return Setup.Get("ToolDownloadSource") = 2 OrElse (Setup.Get("ToolDownloadSource") = 1 AndAlso DlPreferMojang)
+        End Get
+    End Property
+    ''' <summary>
+    ''' 下载文件(而非获取版本列表)的时候,根据是否优先使用官方源决定使用 Url 的顺序。
+    ''' </summary>
+    Public Function DlSourceOrder(OfficialUrls As IEnumerable(Of String), MirrorUrls As IEnumerable(Of String)) As IEnumerable(Of String)
+        Return If(DlSourcePreferMojang, OfficialUrls.Union(MirrorUrls), MirrorUrls.Union(OfficialUrls))
+    End Function
+    ''' <summary>
+    ''' 获取版本列表(而非下载文件)的时候,是否优先使用官方源。
+    ''' </summary>
+    Public ReadOnly Property DlVersionListPreferMojang As Boolean
+        Get
+            Return Setup.Get("ToolDownloadVersion") = 2 OrElse (Setup.Get("ToolDownloadVersion") = 1 AndAlso DlPreferMojang)
+        End Get
+    End Property
+    ''' <summary>
+    ''' 获取版本列表(而非下载文件)的时候,根据是否优先使用官方源决定使用 Url 的顺序。
+    ''' </summary>
+    Public Function DlVersionListOrder(OfficialUrls As IEnumerable(Of String), MirrorUrls As IEnumerable(Of String)) As IEnumerable(Of String)
+        Return If(DlVersionListPreferMojang, OfficialUrls.Union(MirrorUrls), MirrorUrls.Union(OfficialUrls))
+    End Function
+
+
+    ''' <summary>
+    ''' 下载 Assets 文件。
+    ''' </summary>
+    Public Function DlSourceAssetsGet(Original As String) As IEnumerable(Of String)
         Original = Original.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
-        Return {
-            Original.
+        Return DlSourceOrder(
+            {Original},
+            {Original.
                 Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").
                 Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").
-                Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets"),
-            Original
-        }
+                Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
+            })
     End Function
-
-    Public Function DlSourceLibraryGet(Original As String) As String()
+    ''' <summary>
+    ''' 下载 Libraries 文件。
+    ''' </summary>
+    Public Function DlSourceLibraryGet(Original As String) As IEnumerable(Of String)
         If {"minecraftforge", "fabricmc", "neoforged"}.Any(Function(k) Original.Contains(k)) Then '不添加原版源
             Return {
                 Original.
@@ -1195,44 +1238,41 @@
                     Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
             }
         Else
-            Return {
-                Original.
+            Return DlSourceOrder(
+                {Original},
+                {Original.
                     Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").
                     Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").
                     Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
-                Original.
+                 Original.
                     Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").
                     Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").
-                    Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries"),
-                Original
-            }
+                    Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
+                })
         End If
     End Function
-
-    Public Function DlSourceModGet(Original As String) As String
-        Return Original
-        'Return Original.
-        '    Replace("api.modrinth.com", "mod.mcimirror.top/modrinth").
-        '    Replace("staging-api.modrinth.com", "mod.mcimirror.top/modrinth").
-        '    Replace("cdn.modrinth.com", "mod.mcimirror.top").
-        '    Replace("api.curseforge.com", "mod.mcimirror.top/curseforge").
-        '    Replace("edge.forgecdn.net", "mod.mcimirror.top").
-        '    Replace("mediafilez.forgecdn.net", "mod.mcimirror.top").
-        '    Replace("media.forgecdn.net", "mod.mcimirror.top")
-    End Function
-
-    Public Function DlSourceLauncherOrMetaGet(Original As String) As String()
+    ''' <summary>
+    ''' 下载 Launcher 或 Meta 文件。
+    ''' 不应使用它来获取版本列表(因为它只使用文件下载源设置来决定源顺序)。
+    ''' </summary>
+    Public Function DlSourceLauncherOrMetaGet(Original As String) As IEnumerable(Of String)
         If Original Is Nothing Then Throw New Exception("无对应的 json 下载地址")
-        Return {
-            Original.
+        Return DlSourceOrder(
+            {Original},
+            {Original.
                 Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").
                 Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").
                 Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").
-                Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com"),
-            Original
-        }
+                Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
+            })
+    End Function
+
+    'Mod 下载源
+    Public Function DlSourceModGet(Original As String) As String
+        Return Original
     End Function
 
+    'Loader 自动切换
     Private Sub DlSourceLoader(Of InputType, OutputType)(MainLoader As LoaderTask(Of InputType, OutputType),
                                                          LoaderList As List(Of KeyValuePair(Of LoaderTask(Of InputType, OutputType), Integer)),
                                                          Optional IsForceRestart As Boolean = False)

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

@@ -372,8 +372,7 @@ ExitUserJavaCheck:
             For Each Java In AllowedJavaList
                 '如果在官启文件夹启动,会将官启自带 Java 错误视作 MC 文件夹指定 Java,导致了 #2054 的第二例
                 If Java.PathFolder.Contains(".minecraft\cache\java") Then Continue For
-                '#5780
-                If Java.PathFolder.Contains("PCL\MyDownload") Then Continue For
+                If Java.PathFolder.Contains("PCL\MyDownload\") Then Continue For '#5780
                 If TargetJavaList.Contains(Java) Then
                     '直接使用指定的 Java
                     AllowedJavaList = New List(Of JavaEntry) From {Java}
@@ -558,9 +557,8 @@ NoUserJava:
             Dim JavaWithoutInherit As New Dictionary(Of String, Boolean)
             For Each Pair In JavaPreList
                 If Pair.Key.Contains("java8path_target_") OrElse Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") OrElse Pair.Key.ContainsF("system32") Then
-                    Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用")
+                    Log("[Java] 位于 " & Pair.Key & " 的 Java 位于特殊路径,不应优先使用")
                 Else
-                    Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
                     JavaWithoutInherit.Add(Pair.Key, Pair.Value)
                 End If
             Next
@@ -714,8 +712,8 @@ Wait:
             Return False
         Else
             Return MyMsgBox($"PCL 未找到 {VersionDescription},是否需要 PCL 自动下载?" & vbCrLf &
-                            $"如果你已经安装了 {VersionDescription},请在 设置 → 启动选项 → 游戏 Java 中手动导入。",
-                            "未找到 Java", "自动下载", "取消") = 1
+                            $"如果你已经安装了 {VersionDescription},可以在 设置 → 启动选项 → 游戏 Java 中手动导入。",
+                            "自动下载 Java?", "自动下载", "取消") = 1
         End If
     End Function
 
@@ -744,10 +742,10 @@ Wait:
     Private LastJavaBaseDir As String = Nothing '用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
     Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
         Log("[Java] 开始获取 Java 下载信息")
-        Dim IndexFileStr As String = NetGetCodeByLoader(
-            {"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 IndexFileStr As String = NetGetCodeByLoader(DlVersionListOrder(
+            {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
+            {"https://bmclapi2.bangbang93.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. '选择最靠后的一项(最新)
@@ -756,7 +754,7 @@ Wait:
         Dim Address As String = TargetEntry.Value("manifest")("url")
         Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
         '获取文件列表
-        Dim ListFileStr As String = NetGetCodeByLoader({Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"), Address}, IsJson:=True)
+        Dim ListFileStr As String = NetGetCodeByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
         LastJavaBaseDir = 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")
@@ -769,7 +767,7 @@ Wait:
             End If
             If Checker.Check(LastJavaBaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
             Dim Url As String = Info("url")
-            Results.Add(New NetFile({Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"), Url}, LastJavaBaseDir & File.Name, Checker))
+            Results.Add(New NetFile(DlSourceOrder({Url}, {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com")}), LastJavaBaseDir & File.Name, Checker))
         Next
         Loader.Output = Results
         Log($"[Java] 需要下载 {Results.Count} 个文件,目标文件夹:{LastJavaBaseDir}")

+ 38 - 33
Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb

@@ -1134,23 +1134,22 @@ Retry:
         Dim MinVer As New Version(0, 0, 0, 0), MaxVer As New Version(999, 999, 999, 999)
 
         'MC 大版本检测
-        If (McVersionCurrent.ReleaseTime >= New Date(2024, 4, 2) AndAlso McVersionCurrent.Version.McCodeMain = 99) OrElse
-            (McVersionCurrent.Version.McCodeMain > 20 AndAlso McVersionCurrent.Version.McCodeMain <> 99) OrElse
-            (McVersionCurrent.Version.McCodeMain = 20 AndAlso McVersionCurrent.Version.McCodeSub >= 5) Then
+        If (Not McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.ReleaseTime >= New Date(2024, 4, 2)) OrElse
+           (McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 20, 5)) Then
             '1.20.5+(24w14a+):至少 Java 21
             MinVer = New Version(1, 21, 0, 0)
-        ElseIf (McVersionCurrent.ReleaseTime >= New Date(2021, 11, 16) AndAlso McVersionCurrent.Version.McCodeMain = 99) OrElse
-            (McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain <> 99) Then
+        ElseIf (Not McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.ReleaseTime >= New Date(2021, 11, 16)) OrElse
+            (McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 18)) Then
             '1.18 pre2+:至少 Java 17
             MinVer = New Version(1, 17, 0, 0)
-        ElseIf (McVersionCurrent.ReleaseTime >= New Date(2021, 5, 11) AndAlso McVersionCurrent.Version.McCodeMain = 99) OrElse
-           (McVersionCurrent.Version.McCodeMain >= 17 AndAlso McVersionCurrent.Version.McCodeMain <> 99) Then
+        ElseIf (Not McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.ReleaseTime >= New Date(2021, 5, 11)) OrElse
+           (McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 17)) Then
             '1.17+ (21w19a+):至少 Java 16
             MinVer = New Version(1, 16, 0, 0)
         ElseIf McVersionCurrent.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了
             '1.12+:至少 Java 8
             MinVer = New Version(1, 8, 0, 0)
-        ElseIf McVersionCurrent.ReleaseTime <= New Date(2013, 5, 1) AndAlso McVersionCurrent.ReleaseTime.Year >= 2001 Then '避免某些版本的 1960 癌
+        ElseIf McVersionCurrent.ReleaseTime <= New Date(2013, 5, 1) AndAlso McVersionCurrent.ReleaseTime.Year >= 2001 Then '避免某些版本写个 1960 年
             '1.5.2-:最高 Java 12
             MaxVer = New Version(1, 12, 999, 999)
         End If
@@ -1161,11 +1160,11 @@ Retry:
         End If
 
         'OptiFine 检测
-        If McVersionCurrent.Version.HasOptiFine Then
-            If McVersionCurrent.Version.McCodeMain <= 7 AndAlso McVersionCurrent.Version.McCodeMain > 0 Then
+        If McVersionCurrent.Version.HasOptiFine AndAlso McVersionCurrent.Version.IsStandardVersion Then '不管非标准版本
+            If McVersionCurrent.Version.McVersion < New Version(1, 7) Then
                 '<1.7:至多 Java 8
                 MaxVer = New Version(1, 8, 999, 999)
-            ElseIf McVersionCurrent.Version.McCodeMain >= 8 AndAlso McVersionCurrent.Version.McCodeMain <= 11 Then
+            ElseIf McVersionCurrent.Version.McVersion >= New Version(1, 8) AndAlso McVersionCurrent.Version.McVersion < New Version(1, 12) Then
                 '1.8 - 1.11:必须恰好 Java 8
                 MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 8, 999, 999)
             ElseIf McVersionCurrent.Version.McCodeMain = 12 Then
@@ -1176,14 +1175,14 @@ Retry:
 
         'Forge 检测
         If McVersionCurrent.Version.HasForge Then
-            If McVersionCurrent.Version.McName = "1.7.2" Then
-                '1.7.2:必须 Java 7
+            If McVersionCurrent.Version.McVersion >= New Version(1, 6, 1) AndAlso McVersionCurrent.Version.McVersion <= New Version(1, 7, 2) Then
+                '1.6.1 - 1.7.2:必须 Java 7
                 MinVer = If(New Version(1, 7, 0, 0) > MinVer, New Version(1, 7, 0, 0), MinVer)
                 MaxVer = If(New Version(1, 7, 999, 999) < MaxVer, New Version(1, 7, 999, 999), MaxVer)
-            ElseIf McVersionCurrent.Version.McCodeMain <= 12 AndAlso McVersionCurrent.Version.McCodeMain > 0 Then
+            ElseIf McVersionCurrent.Version.McCodeMain <= 12 OrElse Not McVersionCurrent.Version.IsStandardVersion Then '非标准版本
                 '<=1.12:Java 8
                 MaxVer = New Version(1, 8, 999, 999)
-            ElseIf McVersionCurrent.Version.McCodeMain <= 14 AndAlso McVersionCurrent.Version.McCodeMain >= 13 Then
+            ElseIf McVersionCurrent.Version.McCodeMain <= 14 Then
                 '1.13 - 1.14:Java 8 - 10
                 MinVer = If(New Version(1, 8, 0, 0) > MinVer, New Version(1, 8, 0, 0), MinVer)
                 MaxVer = If(New Version(1, 10, 999, 999) < MaxVer, New Version(1, 10, 999, 999), MaxVer)
@@ -1201,11 +1200,11 @@ Retry:
         End If
 
         'Fabric 检测
-        If McVersionCurrent.Version.HasFabric Then
-            If McVersionCurrent.Version.McCodeMain >= 15 AndAlso McVersionCurrent.Version.McCodeMain <= 16 AndAlso McVersionCurrent.Version.McCodeMain <> -1 Then
+        If McVersionCurrent.Version.HasFabric AndAlso McVersionCurrent.Version.IsStandardVersion Then '不管非标准版本
+            If McVersionCurrent.Version.McCodeMain >= 15 AndAlso McVersionCurrent.Version.McCodeMain <= 16 Then
                 '1.15 - 1.16:Java 8+
                 MinVer = If(New Version(1, 8, 0, 0) > MinVer, New Version(1, 8, 0, 0), MinVer)
-            ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 99 Then
+            ElseIf McVersionCurrent.Version.McCodeMain >= 18 Then
                 '1.18+:Java 17+
                 MinVer = If(New Version(1, 17, 0, 0) > MinVer, New Version(1, 17, 0, 0), MinVer)
             End If
@@ -1232,18 +1231,22 @@ Retry:
             If Task.IsAborted Then Exit Sub '中断加载会导致 JavaSelect 异常地返回空值,误判找不到 Java
             McLaunchLog("无合适的 Java,需要确认是否自动下载")
             Dim JavaCode As String
-            If MinVer >= New Version(1, 22, 0, 0) Then '潜在的向后兼容
+            If MinVer >= New Version(1, 22) Then '潜在的向后兼容
                 JavaCode = MinVer.Minor
                 If Not JavaDownloadConfirm("Java " & JavaCode) Then Throw New Exception("$$")
-            ElseIf MinVer >= New Version(1, 21, 0, 0) Then
+            ElseIf MinVer >= New Version(1, 21) Then
                 JavaCode = 21
                 If Not JavaDownloadConfirm("Java 21") Then Throw New Exception("$$")
-            ElseIf MinVer >= New Version(1, 9, 0, 0) Then
+            ElseIf MinVer >= New Version(1, 9) Then
                 JavaCode = 17
                 If Not JavaDownloadConfirm("Java 17") Then Throw New Exception("$$")
-            ElseIf MaxVer < New Version(1, 8, 0, 0) Then
+            ElseIf MaxVer < New Version(1, 8) Then
                 JavaCode = 7
-                If Not JavaDownloadConfirm("Java 7", True) Then Throw New Exception("$$")
+                If McVersionCurrent.Version.HasForge Then
+                    MyMsgBox("你需要先安装 LegacyJavaFixer Mod,或自行安装 Java 7,然后才能启动该版本。", "未找到 Java")
+                Else
+                    If Not JavaDownloadConfirm("Java 7", True) Then Throw New Exception("$$")
+                End If
             ElseIf MinVer > New Version(1, 8, 0, 140) AndAlso MaxVer < New Version(1, 8, 0, 321) Then
                 JavaCode = "8u141"
                 If Not JavaDownloadConfirm("Java 8.0.141 ~ 8.0.320", True) Then Throw New Exception("$$")
@@ -1350,12 +1353,14 @@ Retry:
             Arguments += " " & McLaunchArgumentsGameNew(McVersionCurrent)
             McLaunchLog("新版 Game 参数获取成功")
         End If
-        '编码参数(#5818、#5892)
+        '编码参数(#4700、#5892、#5909)
         If McLaunchJavaSelected.VersionCode > 8 Then
-            If Not Arguments.Contains("-Dfile.encoding=") Then Arguments += " -Dfile.encoding=UTF-8"
             If Not Arguments.Contains("-Dstdout.encoding=") Then Arguments += " -Dstdout.encoding=UTF-8"
             If Not Arguments.Contains("-Dstderr.encoding=") Then Arguments += " -Dstderr.encoding=UTF-8"
         End If
+        If McLaunchJavaSelected.VersionCode >= 18 Then
+            If Not Arguments.Contains("-Dfile.encoding=") Then Arguments += " -Dfile.encoding=COMPAT"
+        End If
         '替换参数
         Dim ReplaceArguments = McLaunchArgumentsReplace(McVersionCurrent, Loader)
         If String.IsNullOrWhiteSpace(ReplaceArguments("${version_type}")) Then
@@ -1675,16 +1680,16 @@ NextVersion:
         '窗口尺寸参数
         Dim GameSize As Size
         Select Case Setup.Get("LaunchArgumentWindowType")
-            Case 2
+            Case 2 '与启动器尺寸一致
                 Dim Result As Size
                 RunInUiWait(Sub() Result = New Size(GetPixelSize(FrmMain.PanForm.ActualWidth), GetPixelSize(FrmMain.PanForm.ActualHeight)))
                 GameSize = Result
-            Case 3
-                GameSize = New Size(Math.Max(100, Setup.Get("LaunchArgumentWindowWidth") - 2), Math.Max(100, Setup.Get("LaunchArgumentWindowHeight") - 2))
+                GameSize.Height -= 29.5 * DPI / 96 '标题栏高度
+            Case 3 '自定义
+                GameSize = New Size(Math.Max(100, Setup.Get("LaunchArgumentWindowWidth")), Math.Max(100, Setup.Get("LaunchArgumentWindowHeight")))
             Case Else
-                GameSize = New Size(875 - 2, 540 - 2)
+                GameSize = New Size(875, 540)
         End Select
-        GameSize.Height -= 29.5 * DPI / 96 '标题栏高度
         If McVersionCurrent.Version.McCodeMain <= 12 AndAlso
             McLaunchJavaSelected.VersionCode <= 8 AndAlso McLaunchJavaSelected.Version.Revision >= 200 AndAlso McLaunchJavaSelected.Version.Revision <= 321 AndAlso
             Not McVersionCurrent.Version.HasOptiFine AndAlso Not McVersionCurrent.Version.HasForge Then
@@ -1713,7 +1718,7 @@ NextVersion:
                 CpStrings.Add(Library.LocalPath)
             End If
         Next
-        If OptiFineCp IsNot Nothing Then CpStrings.Insert(CpStrings.Count - 2, OptiFineCp)
+        If OptiFineCp IsNot Nothing Then CpStrings.Insert(CpStrings.Count - 2, OptiFineCp) 'OptiFine 的总是需要放到倒数第二位
         GameArguments.Add("${classpath}", Join(CpStrings.Select(Function(c) ShortenPath(c)), ";"))
 
         Return GameArguments
@@ -2095,7 +2100,7 @@ IgnoreCustomSkin:
         '输出 bat
         Try
             Dim CmdString As String =
-                $"{If(McLaunchJavaSelected.VersionCode > 8, "chcp 65001>nul" & vbCrLf, "")}" &
+                $"{If(McLaunchJavaSelected.VersionCode > 8 AndAlso McLaunchJavaSelected.VersionCode < 18, "chcp 65001>nul" & vbCrLf, "")}" &
                 "@echo off" & vbCrLf &
                 $"title 启动 - {McVersionCurrent.Name}" & vbCrLf &
                 "echo 游戏正在启动,请稍候。" & vbCrLf &
@@ -2107,7 +2112,7 @@ IgnoreCustomSkin:
                 "echo 游戏已退出。" & vbCrLf &
                 "pause"
             WriteFile(If(CurrentLaunchOptions.SaveBatch, Path & "PCL\LatestLaunch.bat"), SecretFilter(CmdString, "F"),
-                      Encoding:=If(Encoding.Default.Equals(Encoding.UTF8), Encoding.UTF8, Encoding.GetEncoding("GB18030")))
+                      Encoding:=If(McLaunchJavaSelected.VersionCode > 8, Encoding.UTF8, Encoding.Default))
             If CurrentLaunchOptions.SaveBatch IsNot Nothing Then
                 McLaunchLog("导出启动脚本完成,强制结束启动过程")
                 AbortHint = "导出启动脚本成功!"

+ 119 - 151
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -207,46 +207,43 @@ Public Module ModMinecraft
         ''' </summary>
         Public ReadOnly Property PathIndie As String
             Get
-                InitPathIndie(Modable)
+                If Setup.IsUnset("VersionArgumentIndieV2", Version:=Me) Then
+                    '决定该版本是否应该被隔离
+                    Dim ShouldBeIndie =
+                    Function() As Boolean
+                        '从老的版本独立设置中迁移:-1 未决定,0 使用全局设置,1 手动开启,2 手动关闭
+                        If Not Setup.IsUnset("VersionArgumentIndie", Version:=Me) AndAlso Setup.Get("VersionArgumentIndie", Version:=Me) > 0 Then
+                            Log($"[Minecraft] 版本隔离初始化({Name}):从老的版本独立设置中迁移")
+                            Return Setup.Get("VersionArgumentIndie", Version:=Me) = 1
+                        End If
+                        '若版本文件夹下包含 mods 或 saves 文件夹,则自动开启版本隔离
+                        Dim ModFolder As New DirectoryInfo(Path & "mods\")
+                        Dim SaveFolder As New DirectoryInfo(Path & "saves\")
+                        If (ModFolder.Exists AndAlso ModFolder.EnumerateFiles.Any) OrElse (SaveFolder.Exists AndAlso SaveFolder.EnumerateDirectories.Any) Then
+                            Log($"[Minecraft] 版本隔离初始化({Name}):版本文件夹下存在 mods 或 saves 文件夹,自动开启")
+                            Return True
+                        End If
+                        '根据全局的默认设置决定是否隔离
+                        Log($"[Minecraft] 版本隔离初始化({Name}):从全局默认设置中({Setup.Get("LaunchArgumentIndieV2")})判断")
+                        Dim IsRelease As Boolean = State = McVersionState.Fool OrElse State = McVersionState.Old OrElse State = McVersionState.Snapshot
+                        Select Case Setup.Get("LaunchArgumentIndieV2")
+                            Case 0 '关闭
+                                Return False
+                            Case 1 '仅隔离可安装 Mod 的版本
+                                Return Modable
+                            Case 2 '仅隔离非正式版
+                                Return IsRelease
+                            Case 3 '隔离非正式版与可安装 Mod 的版本
+                                Return Modable OrElse IsRelease
+                            Case Else '隔离所有版本
+                                Return True
+                        End Select
+                    End Function
+                    Setup.Set("VersionArgumentIndieV2", ShouldBeIndie(), Version:=Me)
+                End If
                 Return If(Setup.Get("VersionArgumentIndieV2", Version:=Me), Path, PathMcFolder)
             End Get
         End Property
-        ''' <summary>
-        ''' 决定该版本是否应该被隔离。
-        ''' </summary>
-        Public Sub InitPathIndie(Modable As Boolean)
-            If Not Setup.IsUnset("VersionArgumentIndieV2", Version:=Me) Then Return
-            Dim ShouldBeIndie =
-            Function() As Boolean
-                '从老的版本独立设置中迁移:-1 未决定,0 使用全局设置,1 手动开启,2 手动关闭
-                If Not Setup.IsUnset("VersionArgumentIndie", Version:=Me) AndAlso Setup.Get("VersionArgumentIndie", Version:=Me) > 0 Then
-                    Log($"[Minecraft] 版本隔离初始化({Name}):从老的版本独立设置中迁移")
-                    Return Setup.Get("VersionArgumentIndie", Version:=Me) = 1
-                End If
-                '若版本文件夹下包含 mods 或 saves 文件夹,则自动开启版本隔离
-                Dim ModFolder As New DirectoryInfo(Path & "mods\")
-                Dim SaveFolder As New DirectoryInfo(Path & "saves\")
-                If (ModFolder.Exists AndAlso ModFolder.EnumerateFiles.Any) OrElse (SaveFolder.Exists AndAlso SaveFolder.EnumerateFiles.Any) Then
-                    Log($"[Minecraft] 版本隔离初始化({Name}):版本文件夹下存在 mods 或 saves 文件夹,自动开启")
-                    Return True
-                End If
-                '根据全局的默认设置决定是否隔离
-                Log($"[Minecraft] 版本隔离初始化({Name}):从全局默认设置中({Setup.Get("LaunchArgumentIndieV2")})判断")
-                Select Case Setup.Get("LaunchArgumentIndieV2")
-                    Case 0 '关闭
-                        Return False
-                    Case 1 '仅隔离可安装 Mod 的版本
-                        Return Modable
-                    Case 2 '仅隔离非正式版
-                        Return State = McVersionState.Fool OrElse State = McVersionState.Old OrElse State = McVersionState.Snapshot
-                    Case 3 '隔离非正式版与可安装 Mod 的版本
-                        Return Modable OrElse State = McVersionState.Fool OrElse State = McVersionState.Old OrElse State = McVersionState.Snapshot
-                    Case Else '隔离所有版本
-                        Return True
-                End Select
-            End Function
-            Setup.Set("VersionArgumentIndieV2", ShouldBeIndie(), Version:=Me)
-        End Sub
 
         ''' <summary>
         ''' 该版本的版本文件夹名称。
@@ -318,14 +315,6 @@ Public Module ModMinecraft
                             _Version.McName = "pending"
                             GoTo VersionSearchFinish
                         End If
-                        '从 JumpLoader 信息中获取版本号
-                        If HasJumpLoader Then
-                            Try
-                                _Version.McName = JsonObject("jumploader")("jars")("minecraft")(0)("gameVersion")
-                                GoTo VersionSearchFinish
-                            Catch
-                            End Try
-                        End If
                         '从 PCL 下载的版本信息中获取版本号
                         If JsonObject("clientVersion") IsNot Nothing Then
                             _Version.McName = JsonObject("clientVersion")
@@ -382,7 +371,6 @@ Public Module ModMinecraft
                             _Version.McName = Regex
                             GoTo VersionSearchFinish
                         End If
-                        'FUTURE: [Quilt 支持] 从 Quilt 版本中获取版本号
                         '从 jar 项中获取版本号
                         If JsonObject("jar") IsNot Nothing Then
                             _Version.McName = JsonObject("jar").ToString
@@ -432,7 +420,7 @@ VersionSearchFinish:
                         _Version.McCodeMain = If(SplitResult.Length <= 2, Val(SplitResult), "0")
                         SplitResult = If(SplitVersion.Count >= 3, SplitVersion(2), "0")
                         _Version.McCodeSub = If(SplitResult.Length <= 2, Val(SplitResult), "0")
-                    ElseIf _Version.McName.Contains("w") OrElse _Version.McName = "pending" Then
+                    ElseIf (Not IsVersionNameLikeRelease(_Version.McName)) OrElse _Version.McName = "pending" Then
                         _Version.McCodeMain = 99
                         _Version.McCodeSub = 99
                     End If
@@ -562,25 +550,6 @@ Recheck:
                     Catch ex As Exception
                         Throw New Exception("初始化版本 JSON 时失败(" & If(Name, "null") & ")", ex)
                     End Try
-                    Try
-                        '处理 JumpLoader
-                        If Text.Contains("minecraftforge") AndAlso File.Exists(PathIndie & "config\jumploader.json") Then
-                            For Each ModFile In Directory.EnumerateFiles(PathIndie & "mods")
-                                Dim FileName As String = GetFileNameFromPath(ModFile)
-                                If FileName.EndsWithF(".jar", True) AndAlso FileName.ContainsF("jumploader", True) Then
-                                    Log("[Minecraft] 发现 JumpLoader 分支项:" & FileName)
-                                    HasJumpLoader = True
-                                    Exit For
-                                End If
-                            Next
-                        End If
-                        If HasJumpLoader Then
-                            _JsonObject.Remove("jumploader")
-                            _JsonObject.Add("jumploader", GetJson(ReadFile(PathIndie & "config\jumploader.json")))
-                        End If
-                    Catch ex As Exception
-                        Log(ex, "处理 JumpLoader 失败")
-                    End Try
                 End If
                 Return _JsonObject
             End Get
@@ -601,10 +570,6 @@ Recheck:
         ''' Json 是否为 HMCL 格式。
         ''' </summary>
         Public Property IsHmclFormatJson As Boolean = False
-        ''' <summary>
-        ''' 是否包含 JumpLoader。
-        ''' </summary>
-        Public Property HasJumpLoader As Boolean = False
 
         ''' <summary>
         ''' 版本 jar 中的 version.json 文件对象。
@@ -748,7 +713,6 @@ Recheck:
                             Version.HasLiteLoader = True
                         End If
                         'Fabric、Forge
-                        'FUTURE: [Quilt 支持] 确认这里的玩意儿对不对
                         If RealJson.Contains("net.fabricmc:fabric-loader") OrElse RealJson.Contains("org.quiltmc:quilt-loader") Then
                             State = McVersionState.Fabric
                             Version.HasFabric = True
@@ -859,7 +823,6 @@ ExitDataLoad:
                     Info = "发生了未知错误,请向作者反馈此问题"
             End Select
             If Not State = McVersionState.Error Then
-                If HasJumpLoader Then Info += ", JumpLoader"
                 If Setup.Get("VersionServerLogin", Version:=Me) = 3 Then Info += ", 统一通行证验证"
                 If Setup.Get("VersionServerLogin", Version:=Me) = 4 Then Info += ", Authlib 验证"
             End If
@@ -919,6 +882,24 @@ ExitDataLoad:
         ''' 原版次版本号,如 2(For 1.12.2),快照则固定为 99。不可用则为 -1。
         ''' </summary>
         Public McCodeSub As Integer = -1
+        ''' <summary>
+        ''' 是否为非快照版,且读取到了一个有效的原版版本号。
+        ''' </summary>
+        Public ReadOnly Property IsStandardVersion As Boolean
+            Get
+                Return McCodeMain > -1 AndAlso McCodeMain < 99 AndAlso McCodeSub > -1 AndAlso McCodeSub < 99
+            End Get
+        End Property
+        ''' <summary>
+        ''' 标准的原版版本号。
+        ''' 若为快照版或没有有效版本号,则返回 0。
+        ''' </summary>
+        Public ReadOnly Property McVersion As Version
+            Get
+                If Not IsStandardVersion Then Return New Version(0, 0, 0)
+                Return New Version(1, McCodeMain, McCodeSub)
+            End Get
+        End Property
 
         'OptiFine
 
@@ -1728,10 +1709,6 @@ OnLoaded:
         ''' 原 Json 中的 Name 项。
         ''' </summary>
         Public OriginalName As String
-        ''' <summary>
-        ''' 是否为 JumpLoader 项。
-        ''' </summary>
-        Public IsJumpLoader As Boolean = False
 
         Public Overrides Function ToString() As String
             Return If(IsNatives, "[Native] ", "") & GetString(Size) & " | " & LocalPath
@@ -1794,65 +1771,58 @@ OnLoaded:
 
         '获取当前支持库列表
         Log("[Minecraft] 获取支持库列表:" & Version.Name)
-        McLibListGet = McLibListGetWithJson(Version.JsonObject, JumpLoaderFolder:=Version.PathIndie & ".jumploader\")
+        McLibListGet = McLibListGetWithJson(Version.JsonObject)
+        If Not IncludeVersionJar Then Return McLibListGet
 
         '需要添加原版 Jar
-        If IncludeVersionJar Then
-            Dim RealVersion As McVersion
-            Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
-            If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
-                'HMCL 项直接使用自身的 Jar
-                '根据 Inherit 获取最深层版本
-                Dim OriginalVersion As McVersion = Version
-                '1.17+ 的 Forge 不寻找 Inherit
-                If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
-                    Do Until OriginalVersion.InheritVersion = ""
-                        If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
-                        OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
-                    Loop
-                End If
-                '需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
-                '复现:启动一个 Snapshot 版本
-                RealVersion = New McVersion(OriginalVersion.Path)
-            Else
-                'Json 已提供 Jar 字段,使用该字段的信息
-                RealVersion = New McVersion(RequiredJar)
-            End If
-            Dim ClientUrl As String, ClientSHA1 As String
-            '判断需求的版本是否存在
-            '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
-            If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
-                RealVersion = Version
-                Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
-            End If
-            '获取详细下载信息
-            If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
-                ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
-                ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
-            Else
-                ClientUrl = Nothing
-                ClientSHA1 = Nothing
+        Dim RealVersion As McVersion
+        Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
+        If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
+            'HMCL 项直接使用自身的 Jar
+            '根据 Inherit 获取最深层版本
+            Dim OriginalVersion As McVersion = Version
+            '1.17+ 的 Forge 不寻找 Inherit
+            If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
+                Do Until OriginalVersion.InheritVersion = ""
+                    If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
+                    OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
+                Loop
             End If
-            '把所需的原版 Jar 添加进去
-            McLibListGet.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
+            '需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
+            '复现:启动一个 Snapshot 版本
+            RealVersion = New McVersion(OriginalVersion.Path)
+        Else
+            'Json 已提供 Jar 字段,使用该字段的信息
+            RealVersion = New McVersion(RequiredJar)
+        End If
+        Dim ClientUrl As String, ClientSHA1 As String
+        '判断需求的版本是否存在
+        '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
+        If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
+            RealVersion = Version
+            Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
         End If
+        '获取详细下载信息
+        If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
+            ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
+            ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
+        Else
+            ClientUrl = Nothing
+            ClientSHA1 = Nothing
+        End If
+        '把所需的原版 Jar 添加进去
+        McLibListGet.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
 
     End Function
     ''' <summary>
     ''' 获取 Minecraft 某一版本忽视继承的支持库列表,即结果中没有继承项。
     ''' </summary>
-    Public Function McLibListGetWithJson(JsonObject As JObject, Optional KeepSameNameDifferentVersionResult As Boolean = False, Optional CustomMcFolder As String = Nothing, Optional JumpLoaderFolder As String = Nothing) As List(Of McLibToken)
+    Public Function McLibListGetWithJson(JsonObject As JObject, Optional KeepSameNameDifferentVersionResult As Boolean = False, Optional CustomMcFolder As String = Nothing) As List(Of McLibToken)
         CustomMcFolder = If(CustomMcFolder, PathMcFolder)
         Dim BasicArray As New List(Of McLibToken)
 
         '添加基础 Json 项
         Dim AllLibs As JArray = JsonObject("libraries")
-        '添加 JumpLoader Json 项
-        If JsonObject("jumploader") IsNot Nothing AndAlso JsonObject("jumploader")("jars") IsNot Nothing AndAlso JsonObject("jumploader")("jars")("maven") IsNot Nothing Then
-            For Each JumpLoaderToken In JsonObject("jumploader")("jars")("maven")
-                AllLibs.Add(JumpLoaderToken)
-            Next
-        End If
 
         '转换为 LibToken
         For Each Library As JObject In AllLibs.Children
@@ -1865,14 +1835,6 @@ OnLoaded:
             '检查是否需要(Rules)
             If Not McJsonRuleCheck(Library("rules")) Then Continue For
 
-            '检查 JumpLoader
-            Dim IsJumpLoader As Boolean = False
-            If Library("mavenPath") IsNot Nothing Then
-                IsJumpLoader = True
-                If Library("name") Is Nothing Then Library.Add("name", Library("mavenPath")) '这里的修改会导致原 Json 内容改变
-                If Library("repoUrl") IsNot Nothing AndAlso Library("url") Is Nothing Then Library.Add("url", Library("repoUrl"))
-            End If
-
             '获取根节点下的 url
             Dim RootUrl As String = Library("url")
             If RootUrl IsNot Nothing Then
@@ -1882,15 +1844,10 @@ OnLoaded:
             '根据是否本地化处理(Natives)
             If Library("natives") Is Nothing Then '没有 Natives
                 Dim LocalPath As String
-                If IsJumpLoader Then
-                    LocalPath = McLibGet(Library("name"), CustomMcFolder:=If(JumpLoaderFolder, CustomMcFolder))
-                Else
-                    LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder)
-                End If
+                LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder)
                 Try
                     If Library("downloads") IsNot Nothing AndAlso Library("downloads")("artifact") IsNot Nothing Then
                         BasicArray.Add(New McLibToken With {
-                            .IsJumpLoader = IsJumpLoader,
                             .OriginalName = Library("name"),
                             .Url = If(RootUrl, Library("downloads")("artifact")("url")),
                             .LocalPath = If(Library("downloads")("artifact")("path") Is Nothing, McLibGet(Library("name"),
@@ -1899,17 +1856,16 @@ OnLoaded:
                             .IsNatives = False,
                             .SHA1 = Library("downloads")("artifact")("sha1")?.ToString})
                     Else
-                        BasicArray.Add(New McLibToken With {.IsJumpLoader = IsJumpLoader, .OriginalName = Library("name"), .Url = RootUrl, .LocalPath = LocalPath, .Size = 0, .IsNatives = False, .SHA1 = Nothing})
+                        BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = LocalPath, .Size = 0, .IsNatives = False, .SHA1 = Nothing})
                     End If
                 Catch ex As Exception
                     Log(ex, "处理实际支持库列表失败(无 Natives," & If(Library("name"), "Nothing").ToString & ")")
-                    BasicArray.Add(New McLibToken With {.IsJumpLoader = IsJumpLoader, .OriginalName = Library("name"), .Url = RootUrl, .LocalPath = LocalPath, .Size = 0, .IsNatives = False, .SHA1 = Nothing})
+                    BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = LocalPath, .Size = 0, .IsNatives = False, .SHA1 = Nothing})
                 End Try
             ElseIf Library("natives")("windows") IsNot Nothing Then '有 Windows Natives
                 Try
                     If Library("downloads") IsNot Nothing AndAlso Library("downloads")("classifiers") IsNot Nothing AndAlso Library("downloads")("classifiers")("natives-windows") IsNot Nothing Then
                         BasicArray.Add(New McLibToken With {
-                             .IsJumpLoader = IsJumpLoader,
                              .OriginalName = Library("name"),
                              .Url = If(RootUrl, Library("downloads")("classifiers")("natives-windows")("url")),
                              .LocalPath = If(Library("downloads")("classifiers")("natives-windows")("path") Is Nothing,
@@ -1919,11 +1875,11 @@ OnLoaded:
                              .IsNatives = True,
                              .SHA1 = Library("downloads")("classifiers")("natives-windows")("sha1").ToString})
                     Else
-                        BasicArray.Add(New McLibToken With {.IsJumpLoader = IsJumpLoader, .OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
+                        BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
                     End If
                 Catch ex As Exception
                     Log(ex, "处理实际支持库列表失败(有 Natives," & If(Library("name"), "Nothing").ToString & ")")
-                    BasicArray.Add(New McLibToken With {.IsJumpLoader = IsJumpLoader, .OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
+                    BasicArray.Add(New McLibToken With {.OriginalName = Library("name"), .Url = RootUrl, .LocalPath = McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = 0, .IsNatives = True, .SHA1 = Nothing})
                 End Try
             End If
 
@@ -1940,7 +1896,7 @@ OnLoaded:
             Return GetFolderNameFromPath(GetPathFromFullPath(Token.LocalPath))
         End Function
         For i = 0 To BasicArray.Count - 1
-            Dim Key As String = BasicArray(i).Name & BasicArray(i).IsNatives.ToString & BasicArray(i).IsJumpLoader.ToString
+            Dim Key As String = BasicArray(i).Name & BasicArray(i).IsNatives.ToString
             If ResultArray.ContainsKey(Key) Then
                 Dim BasicArrayVersion As String = GetVersion(BasicArray(i))
                 Dim ResultArrayVersion As String = GetVersion(ResultArray(Key))
@@ -1964,7 +1920,7 @@ OnLoaded:
     ''' 获取版本缺失的支持库文件所对应的 NetTaskFile。
     ''' </summary>
     Public Function McLibFix(Version As McVersion) As List(Of NetFile)
-        If Not Version.IsLoaded Then Version.Load() '确保例如 JumpLoader 等项被合并入 Json
+        If Not Version.IsLoaded Then Version.Load()
         Dim Result As New List(Of NetFile)
 
         '更新此方法时需要同步更新 Forge 新版自动安装方法!
@@ -1978,7 +1934,7 @@ OnLoaded:
         End Try
 
         'Library 文件
-        Result.AddRange(McLibFixFromLibToken(McLibListGet(Version, False), JumpLoaderFolder:=Version.PathIndie & ".jumploader\"))
+        Result.AddRange(McLibFixFromLibToken(McLibListGet(Version, False)))
 
         '统一通行证文件
         If Setup.Get("VersionServerLogin", Version:=Version) = 3 Then
@@ -2053,7 +2009,7 @@ OnLoaded:
     ''' <summary>
     ''' 将 McLibToken 列表转换为 NetFile。无需下载的文件会被自动过滤。
     ''' </summary>
-    Public Function McLibFixFromLibToken(Libs As List(Of McLibToken), Optional CustomMcFolder As String = Nothing, Optional JumpLoaderFolder As String = Nothing) As List(Of NetFile)
+    Public Function McLibFixFromLibToken(Libs As List(Of McLibToken), Optional CustomMcFolder As String = Nothing) As List(Of NetFile)
         CustomMcFolder = If(CustomMcFolder, PathMcFolder)
         Dim Result As New List(Of NetFile)
         '获取
@@ -2065,18 +2021,23 @@ OnLoaded:
             Dim Urls As New List(Of String)
             If Token.Url Is Nothing AndAlso Token.Name = "net.minecraftforge:forge:universal" Then
                 '特判修复 Forge 部分 universal 文件缺失 URL(#5455)
-                Token.Url = "https://maven.minecraftforge.net" & Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries", "").Replace("\", "/")
+                Token.Url = "https://maven.minecraftforge.net" & Token.LocalPath.Replace(CustomMcFolder & "libraries", "").Replace("\", "/")
             End If
             If Token.Url IsNot Nothing Then
                 '获取 URL 的真实地址
                 Urls.Add(Token.Url)
                 If Token.Url.Contains("launcher.mojang.com/v1/objects") OrElse Token.Url.Contains("client.txt") OrElse
                    Token.Url.Contains(".tsrg") Then
-                    Urls.AddRange(DlSourceLauncherOrMetaGet(Token.Url).ToList()) 'Mappings(#4425)
+                    Urls.AddRange(DlSourceLauncherOrMetaGet(Token.Url)) 'Mappings(#4425)
                 End If
                 If Token.Url.Contains("maven") Then
-                    Urls.Insert(0, Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOfF("maven")), "https://bmclapi2.bangbang93.com/").
-                        Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven").Replace("maven.neoforged.net/releases", "maven"))
+                    Dim BmclapiUrl As String =
+                        Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOfF("maven")), "https://bmclapi2.bangbang93.com/").Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven").Replace("maven.neoforged.net/releases", "maven")
+                    If DlSourcePreferMojang Then
+                        Urls.Add(BmclapiUrl) '官方源优先
+                    Else
+                        Urls.Insert(0, BmclapiUrl) '镜像源优先
+                    End If
                 End If
             End If
             If Token.LocalPath.Contains("transformer-discovery-service") Then
@@ -2086,13 +2047,13 @@ OnLoaded:
                 Continue For
             ElseIf Token.LocalPath.Contains("optifine\OptiFine") Then
                 'OptiFine 主 Jar
-                Dim OptiFineBase As String = Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_")
+                Dim OptiFineBase As String = Token.LocalPath.Replace(CustomMcFolder & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_")
                 OptiFineBase = "/maven/com/optifine/" & OptiFineBase
                 If OptiFineBase.Contains("_pre") Then OptiFineBase = OptiFineBase.Replace("com/optifine/", "com/optifine/preview_")
                 Urls.Add("https://bmclapi2.bangbang93.com" & OptiFineBase)
             ElseIf Urls.Count <= 2 Then
                 '普通文件
-                Urls.AddRange(DlSourceLibraryGet("https://libraries.minecraft.net" & Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries", "").Replace("\", "/")))
+                Urls.AddRange(DlSourceLibraryGet("https://libraries.minecraft.net" & Token.LocalPath.Replace(CustomMcFolder & "libraries", "").Replace("\", "/")))
             End If
             Result.Add(New NetFile(Urls.Distinct, Token.LocalPath, Checker))
         Next
@@ -2275,7 +2236,7 @@ OnLoaded:
                 If File.Exists AndAlso (Token.Size = 0 OrElse Token.Size = File.Length) AndAlso
                     (Not CheckHash OrElse Token.Hash Is Nothing OrElse Token.Hash = GetFileSHA1(Token.LocalPath)) Then Continue For
                 '文件不存在,添加下载
-                Result.Add(New NetFile(DlSourceResourceGet("https://resources.download.minecraft.net/" & Left(Token.Hash, 2) & "/" & Token.Hash), Token.LocalPath, New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.Hash)))
+                Result.Add(New NetFile(DlSourceAssetsGet($"https://resources.download.minecraft.net/{Left(Token.Hash, 2)}/{Token.Hash}"), Token.LocalPath, New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.Hash)))
             Next
         Catch ex As Exception
             Log(ex, "获取版本缺失的资源文件下载列表失败")
@@ -2399,6 +2360,13 @@ NextEntry:
         End Function
     End Class
 
+    ''' <summary>
+    ''' 判断版本名是否类似正式版。
+    ''' </summary>
+    Public Function IsVersionNameLikeRelease(VerName As String) As Boolean
+        Return VerName.StartsWithF("1.") AndAlso Not (VerName.Contains("w") OrElse VerName.Contains("pre") OrElse VerName.Contains("rc") OrElse VerName.Contains("-"))
+    End Function
+
     ''' <summary>
     ''' 为邮箱地址或手机号账号进行部分打码。
     ''' </summary>

+ 6 - 2
Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb

@@ -837,7 +837,7 @@ Finished:
                         '仅当 Forge 1.13- 且文件夹名与版本号相同时,才加载该子文件夹下的 Mod
                         If Not (PageVersionLeft.Version IsNot Nothing AndAlso PageVersionLeft.Version.Version.HasForge AndAlso
                                 PageVersionLeft.Version.Version.McCodeMain < 13 AndAlso
-                                File.Directory.Name = "1." & PageVersionLeft.Version.Version.McCodeMain & "." & PageVersionLeft.Version.Version.McCodeSub) Then
+                                File.Directory.Name = $"1.{PageVersionLeft.Version.Version.McCodeMain}.{PageVersionLeft.Version.Version.McCodeSub}") Then
                             Continue For
                         End If
                     End If
@@ -1127,7 +1127,11 @@ Finished:
         Next
         WriteFile(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None)))
         '刷新边栏
-        RunInUi(Sub() FrmVersionMod?.RefreshBars())
+        If FrmVersionMod?.Filter = PageVersionMod.FilterType.CanUpdate Then
+            RunInUi(Sub() FrmVersionMod?.RefreshUI()) '同步 “可更新” 列表 (#4677)
+        Else
+            RunInUi(Sub() FrmVersionMod?.RefreshBars())
+        End If
     End Sub
     Public Function GetTargetModLoaders() As List(Of CompModLoaderType)
         Dim ModLoaders As New List(Of CompModLoaderType)

+ 94 - 148
Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb

@@ -109,7 +109,7 @@ Public Module ModModpack
         End Try
     End Function
 
-    Private Sub ExtractModpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase, LoaderProgressDelta As Double)
+    Private Sub ExtractModpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase, ProgressIncrement As Double)
         '解压文件
         Dim RetryCount As Integer = 1
         Dim Encode = Encoding.GetEncoding("GB18030")
@@ -117,15 +117,15 @@ Public Module ModModpack
         Try
 Retry:
             Loader.Progress = InitialProgress
-            '完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试
             DeleteDirectory(InstallTemp)
-            ExtractFile(FileAddress, InstallTemp, Encode, ProgressIncrementHandler:=Sub(Delta) Loader.Progress += Delta * LoaderProgressDelta)
+            ExtractFile(FileAddress, InstallTemp, Encode, ProgressIncrementHandler:=Sub(Delta) Loader.Progress += Delta * ProgressIncrement)
         Catch ex As Exception
             Log(ex, "第 " & RetryCount & " 次解压尝试失败")
             If TypeOf ex Is ArgumentException Then
                 Encode = Encoding.UTF8
                 Log("[ModPack] 已切换压缩包解压编码为 UTF8")
             End If
+            '完全不知道为啥会出现文件正在被另一进程使用的问题,总之加个重试
             If RetryCount < 5 Then
                 Thread.Sleep(RetryCount * 2000)
                 If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Exit Sub
@@ -135,6 +135,35 @@ Retry:
                 Throw New Exception("解压整合包文件失败", ex)
             End If
         End Try
+        Loader.Progress = InitialProgress + ProgressIncrement
+    End Sub
+    ''' <summary>
+    ''' 从整合包的 override 目录复制文件,同时设置 PCL 的配置文件与版本隔离。
+    ''' 对路径末尾是否为 \ 没有要求。
+    ''' </summary>
+    Private Sub CopyOverrideDirectory(OverridesFolder As String, VersionFolder As String, Loader As LoaderBase, ProgressIncrement As Double)
+        If Not OverridesFolder.EndsWithF("\") Then OverridesFolder += "\"
+        If Not VersionFolder.EndsWithF("\") Then VersionFolder += "\"
+        '复制文件
+        If Directory.Exists(OverridesFolder) Then
+            Log($"[ModPack] 处理整合包覆写文件夹:{OverridesFolder} → {VersionFolder}")
+            CopyDirectory(OverridesFolder, VersionFolder, Sub(Delta) Loader.Progress += Delta * ProgressIncrement)
+        Else
+            Log($"[ModPack] 整合包中没有覆写文件夹:{OverridesFolder}")
+            Loader.Progress += ProgressIncrement
+        End If
+        '设置 ini
+        Dim OverridesIni As String = $"{OverridesFolder}PCL\Setup.ini"
+        Dim VersionIni As String = $"{VersionFolder}PCL\Setup.ini"
+        If File.Exists(OverridesIni) Then
+            WriteIni(OverridesIni, "VersionArgumentIndie", 1) '开启版本隔离
+            WriteIni(OverridesIni, "VersionArgumentIndieV2", True)
+            CopyFile(OverridesIni, VersionIni) '覆写已有的 ini
+        Else
+            WriteIni(VersionIni, "VersionArgumentIndie", 1) '开启版本隔离
+            WriteIni(VersionIni, "VersionArgumentIndieV2", True)
+        End If
+        IniClearCache(VersionIni) '重置缓存,避免被安装过程中写入的 ini 覆盖
     End Sub
 
 #Region "不同类型整合包的安装方法"
@@ -184,7 +213,7 @@ Retry:
                 Log("[ModPack] 未知 Mod 加载器:" & Id)
             End If
         Next
-        '解压与配置文件
+        '解压
         Dim InstallTemp As String = RequestTaskTempFolder()
         Dim InstallLoaders As New List(Of LoaderBase)
         Dim OverrideHome As String = If(Json("overrides"), "")
@@ -192,21 +221,10 @@ Retry:
             InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
             Sub(Task As LoaderTask(Of String, Integer))
                 ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
-                Task.Progress = 0.6
-                '复制结果
-                Log("[ModPack] 整合包 override 目标:" & OverrideHome)
-                Dim OverrideRoot = InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome) '#5613
-                If Directory.Exists(OverrideRoot) Then
-                    Dim CopyTarget = $"{PathMcFolder}versions\{VersionName}"
-                    CopyDirectory(OverrideRoot, CopyTarget, Sub(Delta) Task.Progress += Delta * 0.35)
-                    Log($"[ModPack] 整合包 override 复制:{OverrideRoot} -> {CopyTarget}")
-                Else
-                    Log($"[ModPack] 整合包中未找到 override 文件夹:{OverrideRoot}")
-                End If
-                Task.Progress = 0.95
-                '开启版本隔离
-                WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndie", 1)
-                WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndieV2", True)
+                CopyOverrideDirectory(
+                    InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome), '#5613
+                    $"{PathMcFolder}versions\{VersionName}",
+                    Task, 0.4)
             End Sub) With {
             .ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
         End If
@@ -245,24 +263,24 @@ Retry:
                             Continue For
                         End If
                     End If
-                    '建立 CompFile
-                    Dim File As New CompFile(ModJson, CompType.Mod)
-                    If Not File.Available Then Continue For
                     '根据 modules 和文件名后缀判断资源类型
-                    Dim TargetFolder As String
+                    Dim TargetFolder As String, Type As CompType
                     If ModJson("modules").Any Then 'modules 可能返回 null(#1006)
                         Dim ModuleNames = CType(ModJson("modules"), JArray).Select(Function(l) l("name").ToString).ToList
                         If ModuleNames.Contains("META-INF") OrElse ModuleNames.Contains("mcmod.info") OrElse
-                           File.FileName.EndsWithF(".jar", True) Then
-                            TargetFolder = "mods"
+                           ModJson?("FileName")?.ToString?.EndsWithF(".jar", True) Then
+                            TargetFolder = "mods" : Type = CompType.Mod
                         ElseIf ModuleNames.Contains("pack.mcmeta") Then
-                            TargetFolder = "resourcepacks"
+                            TargetFolder = "resourcepacks" : Type = CompType.ResourcePack
                         Else
-                            TargetFolder = "shaderpacks"
+                            TargetFolder = "shaderpacks" : Type = CompType.Shader
                         End If
                     Else
-                        TargetFolder = "mods"
+                        TargetFolder = "mods" : Type = CompType.Mod
                     End If
+                    '建立 CompFile
+                    Dim File As New CompFile(ModJson, Type)
+                    If Not File.Available Then Continue For
                     '实际的添加
                     FileList.Add(Id, File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{TargetFolder}\"))
                     Task.Progress += 1 / (1 + ModList.Count)
@@ -285,16 +303,11 @@ Retry:
             .NeoForgeVersion = NeoForgeVersion,
             .FabricVersion = FabricVersion
         }
-        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
-        '构造 Libraries 加载器
-        Dim LoadersLib As New List(Of LoaderBase)
-        LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
-        LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
+        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
         '构造总加载器
         Dim Loaders As New List(Of LoaderBase)
         Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
         Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
         Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
         Sub(Task As LoaderTask(Of String, String))
             '设置图标
@@ -379,29 +392,20 @@ Retry:
             If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
             If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
         End If
-        '解压和配置文件
+        '解压
         Dim InstallTemp As String = RequestTaskTempFolder()
         Dim InstallLoaders As New List(Of LoaderBase)
         InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
         Sub(Task As LoaderTask(Of String, Integer))
-            ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
-            Task.Progress = 0.6
-            '复制 overrides 文件夹和 client-overrides 文件夹
-            If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
-                CopyDirectory(InstallTemp & ArchiveBaseFolder & "overrides", PathMcFolder & "versions\" & VersionName,
-                    Sub(Delta) Task.Progress += Delta * 0.25)
-            Else
-                Log("[ModPack] 整合包中未找到 override 目录,已跳过")
-            End If
-            Task.Progress = 0.85
-            If Directory.Exists(InstallTemp & ArchiveBaseFolder & "client-overrides") Then
-                CopyDirectory(InstallTemp & ArchiveBaseFolder & "client-overrides", PathMcFolder & "versions\" & VersionName,
-                    Sub(Delta) Task.Progress += Delta * 0.1)
-            End If
-            Task.Progress = 0.95
-            '开启版本隔离
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndie", 1)
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndieV2", True)
+            ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.5)
+            CopyOverrideDirectory(
+                InstallTemp & ArchiveBaseFolder & "overrides",
+                PathMcFolder & "versions\" & VersionName,
+                Task, 0.4)
+            CopyOverrideDirectory(
+                InstallTemp & ArchiveBaseFolder & "client-overrides",
+                PathMcFolder & "versions\" & VersionName,
+                Task, 0.1)
         End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
         '获取下载文件列表
         Dim FileList As New List(Of NetFile)
@@ -422,7 +426,12 @@ Retry:
             Dim Urls = File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToList
             Urls.AddRange(Urls.Select(Function(u) DlSourceModGet(u)).ToList)
             Urls = Urls.Distinct.ToList()
-            FileList.Add(New NetFile(Urls, PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString,
+            Dim TargetPath As String = $"{PathMcFolder}versions\{VersionName}\{File("path")}"
+            If Not IO.Path.GetFullPath(TargetPath).StartsWithF($"{PathMcFolder}versions\{VersionName}\", True) Then
+                MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!" & vbCrLf & "错误的文件:" & TargetPath, "文件路径校验失败", IsWarn:=True)
+                Throw New CancelledException
+            End If
+            FileList.Add(New NetFile(Urls, TargetPath,
                 New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString), True))
         Next
         If FileList.Any Then
@@ -438,20 +447,15 @@ Retry:
             .NeoForgeVersion = NeoForgeVersion,
             .FabricVersion = FabricVersion
         }
-        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
-        '构造 Libraries 加载器
-        Dim LoadersLib As New List(Of LoaderBase)
-        LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
-        LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
+        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
         '构造总加载器
         Dim Loaders As New List(Of LoaderBase)
         Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
         Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
         Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
         Sub(Task As LoaderTask(Of String, String))
             '设置图标
-            Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
+            Dim VersionFolder As String = $"{PathMcFolder}versions\{VersionName}\"
             If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
                 File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
                 WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
@@ -502,67 +506,38 @@ Retry:
         If Validate.Validate(VersionName) <> "" Then VersionName = ""
         If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
         If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
-        '解压与配置文件
+        '解压
         Dim InstallTemp As String = RequestTaskTempFolder()
         Dim InstallLoaders As New List(Of LoaderBase)
         InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
         Sub(Task As LoaderTask(Of String, Integer))
             ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
-            Task.Progress = 0.6
-            '复制结果
-            If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
-                CopyDirectory(InstallTemp & ArchiveBaseFolder & "minecraft", PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35)
-            Else
-                Log("[ModPack] 整合包中未找到 minecraft override 目录,已跳过")
-            End If
-            Task.Progress = 0.95
-            '开启版本隔离
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndie", 1)
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndieV2", True)
+            CopyOverrideDirectory(
+                InstallTemp & ArchiveBaseFolder & "minecraft",
+                PathMcFolder & "versions\" & VersionName,
+                Task, 0.4)
         End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
-        '构造加载器
+        '构造游戏本体安装加载器
         If Json("gameVersion") Is Nothing Then Throw New Exception("该 HMCL 整合包未提供游戏版本信息,无法安装!")
         Dim Request As New McInstallRequest With {
             .TargetVersionName = VersionName,
             .TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
             .MinecraftName = Json("gameVersion").ToString
         }
-        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
-        '构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
-        Dim LoadersLib As New List(Of LoaderBase)
-        LoadersLib.Add(New LoaderTask(Of String, String)("重命名版本 Json(副加载器)",
-        Sub()
-            Dim RealFileName As String = PathMcFolder & "versions\" & VersionName & "\" & VersionName & ".json"
-            Dim OldFileName As String = PathMcFolder & "versions\" & VersionName & "\pack.json"
-            If File.Exists(OldFileName) Then
-                '修改 id
-                Dim FileJson = GetJson(ReadFile(OldFileName))
-                FileJson("id") = VersionName
-                '替换文件
-                File.Delete(OldFileName)
-                WriteFile(RealFileName, FileJson.ToString)
-                Log("[ModPack] 已重命名版本 Json:" & RealFileName)
-            End If
-        End Sub) With {.ProgressWeight = 0.1, .Show = False})
-        LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
-        LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
+        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
         '构造总加载器
         Dim Loaders As New List(Of LoaderBase) From {
-            New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)},
-            New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)},
-            New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8}
+            New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)},
+            New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)}
         }
-
         '重复任务检查
         Dim LoaderName As String = "HMCL 整合包安装:" & VersionName & " "
         If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
             Hint("该整合包正在安装中!", HintType.Critical)
             Throw New CancelledException
         End If
-
         '启动
         Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
-        'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
         Loader.Start(Request.TargetVersionFolder)
         LoaderTaskbarAdd(Loader)
         FrmMain.BtnExtraDownload.ShowRefresh()
@@ -586,24 +561,17 @@ Retry:
         If Validate.Validate(VersionName) <> "" Then VersionName = ""
         If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
         If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
-        '解压、配置设置文件
+        '解压
         Dim InstallTemp As String = RequestTaskTempFolder()
         Dim SetupFile As String = $"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini"
         Dim InstallLoaders As New List(Of LoaderBase)
         InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
         Sub(Task As LoaderTask(Of String, Integer))
-            ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
-            Task.Progress = 0.6
-            '复制结果
-            If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then
-                CopyDirectory(InstallTemp & ArchiveBaseFolder & ".minecraft", PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35)
-            Else
-                Log("[ModPack] 整合包中未找到 override .minecraft 目录,已跳过")
-            End If
-            Task.Progress = 0.95
-            '开启版本隔离
-            WriteIni(SetupFile, "VersionArgumentIndie", 1)
-            WriteIni(SetupFile, "VersionArgumentIndieV2", True)
+            ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.55)
+            CopyOverrideDirectory(
+                InstallTemp & ArchiveBaseFolder & ".minecraft",
+                PathMcFolder & "versions\" & VersionName,
+                Task, 0.4)
             '读取 MMC 设置文件(#2655)
             Try
                 Dim MMCSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
@@ -682,16 +650,11 @@ Retry:
             End Select
         Next
         '构造加载器
-        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
-        '构造 Libraries 加载器
-        Dim LoadersLib As New List(Of LoaderBase)
-        LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
-        LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
+        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
         '构造总加载器
         Dim Loaders As New List(Of LoaderBase)
-        Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
+        Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
+        Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
 
         '重复任务检查
         Dim LoaderName As String = "MMC 整合包安装:" & VersionName & " "
@@ -728,24 +691,16 @@ Retry:
             If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
             If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
         End If
-        '解压与配置文件
+        '解压
         Dim InstallTemp As String = RequestTaskTempFolder()
         Dim InstallLoaders As New List(Of LoaderBase)
         InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
         Sub(Task As LoaderTask(Of String, Integer))
             ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
-            Task.Progress = 0.6
-            '复制结果
-            If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
-                CopyDirectory(InstallTemp & ArchiveBaseFolder & "overrides", PathMcFolder & "versions\" & VersionName,
-                    Sub(Delta) Task.Progress += 0.35 * Delta)
-            Else
-                Log("[ModPack] 整合包中未找到 overrides 目录,已跳过")
-            End If
-            Task.Progress = 0.95
-            '开启版本隔离
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndie", 1)
-            WriteIni($"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini", "VersionArgumentIndieV2", True)
+            CopyOverrideDirectory(
+                InstallTemp & ArchiveBaseFolder & "overrides",
+                PathMcFolder & "versions\" & VersionName,
+                Task, 0.4)
         End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
         '构造加载器
         If Json("addons") Is Nothing Then Throw New Exception("该 MCBBS 整合包未提供游戏版本附加信息,无法安装!")
@@ -767,16 +722,11 @@ Retry:
             .NeoForgeVersion = If(Addons.ContainsKey("neoforge"), Addons("neoforge"), Nothing),
             .FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing)
         }
-        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
-        '构造 Libraries 加载器
-        Dim LoadersLib As New List(Of LoaderBase)
-        LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
-        LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
+        Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request)
         '构造总加载器
         Dim Loaders As New List(Of LoaderBase)
-        Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
-        Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
+        Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallLoaders.Sum(Function(l) l.ProgressWeight)})
+        Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeLoaders.Sum(Function(l) l.ProgressWeight)})
 
         '重复任务检查
         Dim LoaderName As String = "MCBBS 整合包安装:" & VersionName & " "
@@ -830,13 +780,9 @@ Retry:
                     Log("[Modpack] 找到压缩包中附带的启动器:" & Launcher)
                     If MyMsgBox($"整合包里似乎自带了启动器,是否换用它继续安装?{vbCrLf}即将打开:{Launcher}", "换用整合包启动器?", "换用", "不换用") = 1 Then
                         OpenExplorer(TargetFolder)
-                        Try
-                            ShellOnly(Launcher, "--wait") '要求等待已有的 PCL 退出
-                            Log("[Modpack] 为换用整合包中的启动器启动,强制结束程序")
-                            FrmMain.EndProgram(False)
-                        Catch ex As Exception
-                            Log(ex, "打开文件失败", LogLevel.Msgbox)
-                        End Try
+                        ShellOnly(Launcher, "--wait") '要求等待已有的 PCL 退出
+                        Log("[Modpack] 为换用整合包中的启动器启动,强制结束程序")
+                        FrmMain.EndProgram(False)
                         Return
                     End If
                 Else

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

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

+ 0 - 0
Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml → Plain Craft Launcher 2/Pages/PageDownload/Comp/MyCompItem.xaml


+ 28 - 20
Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb → Plain Craft Launcher 2/Pages/PageDownload/Comp/MyCompItem.xaml.vb

@@ -92,7 +92,7 @@
             Log("[Control] 按下资源工程列表项:" & LabTitle.Text)
         End If
     End Sub
-    Private Sub ProjectClick(sender As MyCompItem, e As EventArgs) Handles Me.Click
+    Private Sub MyCompItem_Click(sender As MyCompItem, e As EventArgs) Handles Me.Click
         '记录当前展开的卡片标题(#2712)
         Dim Titles As New List(Of String)
         If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
@@ -103,29 +103,37 @@
             FrmMain.PageCurrent.Additional(1) = Titles
         End If
         '打开详情页
-        Dim TargetVersion As String
-        Dim TargetLoader As CompModLoaderType
-        If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
+        Dim TargetType As CompType
+        Dim TargetVersion As String = Nothing
+        Dim TargetLoader As CompModLoaderType = CompModLoaderType.Any
+        If FrmMain.PageCurrent.Page = FormMain.PageType.Download Then
+            '从下载页进入
+            Select Case FrmMain.PageCurrentSub
+                Case FormMain.PageSubType.DownloadMod
+                    TargetType = CompType.Mod
+                    TargetVersion = FrmDownloadMod.Content.Loader.Input.GameVersion
+                    TargetLoader = FrmDownloadMod.Content.Loader.Input.ModLoader
+                Case FormMain.PageSubType.DownloadPack
+                    TargetType = CompType.ModPack
+                    TargetVersion = FrmDownloadPack.Content.Loader.Input.GameVersion
+                Case FormMain.PageSubType.DownloadDataPack
+                    TargetType = CompType.DataPack
+                    TargetVersion = FrmDownloadDataPack.Content.Loader.Input.GameVersion
+                Case FormMain.PageSubType.DownloadResourcePack
+                    TargetType = CompType.ResourcePack
+                    TargetVersion = FrmDownloadResourcePack.Content.Loader.Input.GameVersion
+                Case FormMain.PageSubType.DownloadShader
+                    TargetType = CompType.Shader
+                    TargetVersion = FrmDownloadShader.Content.Loader.Input.GameVersion
+            End Select
+        Else
+            '从详情页进入(查看前置)
+            TargetType = CompType.Any '允许任意类别
             TargetVersion = FrmMain.PageCurrent.Additional(2)
             TargetLoader = FrmMain.PageCurrent.Additional(3)
-        Else
-            Select Case CType(sender.Tag, CompProject).Type
-                Case CompType.Mod
-                    TargetVersion = If(PageDownloadMod.Loader.Input.GameVersion, "")
-                    TargetLoader = PageDownloadMod.Loader.Input.ModLoader
-                Case CompType.ModPack
-                    TargetVersion = If(PageDownloadPack.Loader.Input.GameVersion, "")
-                Case CompType.DataPack
-                    TargetVersion = If(PageDownloadDataPack.Loader.Input.GameVersion, "")
-                Case CompType.Shader
-                    TargetVersion = If(PageDownloadShader.Loader.Input.GameVersion, "")
-                Case Else 'CompType.ResourcePack
-                    TargetVersion = If(PageDownloadResourcePack.Loader.Input.GameVersion, "")
-            End Select
         End If
-        If CType(sender.Tag, CompProject).Type <> CompType.Mod Then TargetLoader = CompModLoaderType.Any
         FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail,
-                           .Additional = {sender.Tag, New List(Of String), TargetVersion, TargetLoader}})
+                           .Additional = {sender.Tag, New List(Of String), TargetVersion, TargetLoader, TargetType}})
     End Sub
 
     '鼠标点击判定

+ 95 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml

@@ -0,0 +1,95 @@
+<local:MyScrollViewer x:Class="PageComp"
+      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+      xmlns:local="clr-namespace:PCL"
+      mc:Ignorable="d" 
+      VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
+    <StackPanel Orientation="Vertical" Margin="25">
+        <local:MyCard Title="搜索XXX" Margin="0,0,0,15" x:Name="PanAlways">
+            <StackPanel Margin="25,40,25,15">
+                <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
+                    <Grid.ColumnDefinitions>
+                        <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,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.21.5" />
+                        <local:MyComboBoxItem Content="1.21.1" />
+                        <local:MyComboBoxItem Content="1.20.6" />
+                        <local:MyComboBoxItem Content="1.20.1" />
+                        <local:MyComboBoxItem Content="1.19.4" />
+                        <local:MyComboBoxItem Content="1.19.2" />
+                        <local:MyComboBoxItem Content="1.18.2" />
+                        <local:MyComboBoxItem Content="1.16.5" />
+                        <local:MyComboBoxItem Content="1.12.2" />
+                        <local:MyComboBoxItem Content="1.10.2" />
+                        <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="NeoForge" Tag="6" />
+                        <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" />
+                </Grid>
+                <Grid Margin="0,4,0,6" Height="35">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                        <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                        <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                    </Grid.ColumnDefinitions>
+                    <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
+                    <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
+                    <local:MyButton x:Name="BtnSearchInstallModPack" Text="安装已有整合包" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="2"
+                                    ToolTip="在当前选择的 Minecraft 文件夹下安装整合包" ToolTipService.HorizontalOffset="-55" ToolTipService.VerticalOffset="5" />
+                </Grid>
+            </StackPanel>
+        </local:MyCard>
+        <StackPanel x:Name="PanContent">
+            <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
+            <local:MyCard x:Name="CardProjects" UseAnimation="False">
+                <StackPanel x:Name="PanProjects" Margin="12" />
+            </local:MyCard>
+            <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
+                <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
+                    <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
+                        Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
+                    <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
+                        Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
+                    <TextBlock x:Name="LabPage" Text="1" 
+                               Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
+                    <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
+                        Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
+                </StackPanel>
+            </local:MyCard>
+        </StackPanel>
+        <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
+            <local:MyLoading Text="正在获取XXX列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
+        </local:MyCard>
+    </StackPanel>
+</local:MyScrollViewer>

+ 110 - 31
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb → Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb

@@ -1,17 +1,79 @@
-Public Class PageDownloadMod
+Imports System.Windows.Markup
+
+<ContentProperty("SearchTags")>
+Public Class PageComp
+
+#Region "属性"
+
+    ''' <summary>
+    ''' 用于 XAML 快速设置的 Tag 下拉框列表。
+    ''' </summary>
+    Public ReadOnly Property SearchTags As ItemCollection
+        Get
+            Return ComboSearchTag.Items
+        End Get
+    End Property
+
+    ''' <summary>
+    ''' 英文前后不含空格的可读资源类型名,例如 "Mod"、"整合包"。
+    ''' </summary>
+    Public Property TypeName As String
+        Get
+            Return _TypeName
+        End Get
+        Set(Value As String)
+            If _TypeName = Value Then Return
+            _TypeName = Value
+            Loader.Name = $"社区资源获取:{Value}"
+        End Set
+    End Property
+    Private _TypeName As String = ""
 
-    Public Const PageSize = 40
     ''' <summary>
-    ''' 在切换到该页面时自动设置的目标版本。
+    ''' 英文前后含一个空格的可读资源类型名,例如 " Mod "、"整合包"。
+    ''' </summary>
+    Public Property TypeNameSpaced As String
+        Get
+            Return _TypeNameSpaced
+        End Get
+        Set(Value As String)
+            If _TypeNameSpaced = Value Then Return
+            _TypeNameSpaced = Value
+            PanAlways.Title = $"搜索{Value}"
+            Load.Text = $"正在获取{Value}列表"
+        End Set
+    End Property
+    Private _TypeNameSpaced As String = ""
+
+    ''' <summary>
+    ''' 该页面对应的资源类型。
+    ''' </summary>
+    Public Property PageType As CompType
+        Get
+            Return _Type
+        End Get
+        Set(Value As CompType)
+            If _Type = Value Then Return
+            _Type = Value
+            BtnSearchInstallModPack.Visibility = If(Value = CompType.ModPack, Visibility.Visible, Visibility.Collapsed)
+        End Set
+    End Property
+    Private _Type As CompType = -1
+
+#End Region
+
+#Region "加载"
+
+    ''' <summary>
+    ''' 在切换到页面时,应自动将筛选项设置为与该目标 MC 版本和加载器相同。
     ''' </summary>
     Public Shared TargetVersion As McVersion = Nothing
 
-    '加载器信息
-    Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject Mod", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
-    Public Shared Storage As New CompProjectStorage
-    Public Shared Page As Integer = 0
+    '在点击 MyCompItem 时会获取 Loader 的输入,以使资源详情页面可以应用相同的筛选项
+    Public Loader As New LoaderTask(Of CompProjectRequest, Integer)("社区资源获取:XXX", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
+
     Private IsLoaderInited As Boolean = False
-    Private Sub PageDownloadMod_Inited(sender As Object, e As EventArgs) Handles Me.Loaded
+    Private Sub PageCompControls_Inited(sender As Object, e As EventArgs) Handles Me.Loaded
         '不知道从 Initialized 改成 Loaded 会不会有问题,但用 Initialized 会导致初始的筛选器修改被覆盖回默认值
         If TargetVersion IsNot Nothing Then
             '设置目标
@@ -34,43 +96,55 @@
             TargetVersion = Nothing
             '如果已经完成请求,则重新开始
             If IsLoaderInited Then StartNewSearch()
-            PanScroll.ScrollToHome()
+            ScrollToHome()
         End If
         '加载器初始化
         If IsLoaderInited Then Return
         IsLoaderInited = True
-        PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
+        CType(Parent, MyPageRight).PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
         If McVersionHighest = -1 Then 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.Mod, Storage, (Page + 1) * PageSize)
-        If FrmDownloadMod IsNot Nothing Then
-            Dim ModLoader As CompModLoaderType = Val(FrmDownloadMod.ComboSearchLoader.SelectedItem.Tag)
-            Dim GameVersion As String = If(FrmDownloadMod.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
-                    If(FrmDownloadMod.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadMod.TextSearchVersion.Text.Contains("w"), FrmDownloadMod.TextSearchVersion.Text, Nothing))
+    Private Function LoaderInput() As CompProjectRequest
+        Dim Request As New CompProjectRequest(PageType, Storage, (Page + 1) * PageSize)
+        Dim GameVersion As String = If(TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
+                If(TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w"), TextSearchVersion.Text, Nothing))
+        Dim ModLoader As CompModLoaderType = CompModLoaderType.Any
+        If PageType = CompType.Mod Then '只有 Mod 考虑加载器
+            ModLoader = Val(ComboSearchLoader.SelectedItem.Tag)
             If GameVersion IsNot Nothing AndAlso GameVersion.Contains(".") AndAlso Val(GameVersion.Split(".")(1)) < 14 AndAlso '1.14-
                 ModLoader = CompModLoaderType.Forge Then '选择了 Forge
                 ModLoader = CompModLoaderType.Any '此时,视作没有筛选 Mod Loader(因为部分老 Mod 没有设置自己支持的加载器)
             End If
-            With Request
-                .SearchText = FrmDownloadMod.TextSearchName.Text
-                .GameVersion = GameVersion
-                .Tag = FrmDownloadMod.ComboSearchTag.SelectedItem.Tag
-                .ModLoader = ModLoader
-                .Source = CType(Val(FrmDownloadMod.ComboSearchSource.SelectedItem.Tag), CompSourceType)
-            End With
         End If
+        With Request
+            .SearchText = TextSearchName.Text
+            .GameVersion = GameVersion
+            .Tag = ComboSearchTag.SelectedItem.Tag
+            .ModLoader = ModLoader
+            .Source = CType(Val(ComboSearchSource.SelectedItem.Tag), CompSourceType)
+        End With
         Return Request
     End Function
 
+#End Region
+
+    Public Storage As New CompProjectStorage
+    ''' <summary>
+    ''' 每页展示的结果数量。
+    ''' </summary>
+    Public Const PageSize = 40
+    Public Page As Integer = 0
+
     '结果 UI 化
     Private Sub Load_OnFinish()
         Try
-            Log($"[Comp] 开始可视化 Mod 列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
+            Log($"[Comp] 开始可视化{TypeNameSpaced}列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
             '列表项
             PanProjects.Children.Clear()
             For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
-                PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, Loader.Input.ModLoader = CompModLoaderType.Any))
+                PanProjects.Children.Add(Storage.Results(i).ToCompItem(
+                    ShowMcVersionDesc:=Loader.Input.GameVersion Is Nothing,
+                    ShowLoaderDesc:=Loader.Input.ModLoader = CompModLoaderType.Any AndAlso (PageType = CompType.Mod OrElse PageType = CompType.ModPack)))
             Next
             '页码
             CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
@@ -94,9 +168,9 @@
                 HintError.Text = Storage.ErrorMessage
             End If
             '强制返回顶部
-            PanBack.ScrollToTop()
+            ScrollToTop()
         Catch ex As Exception
-            Log(ex, "可视化 Mod 列表出错", LogLevel.Feedback)
+            Log(ex, $"可视化{TypeNameSpaced}列表出错", LogLevel.Feedback)
         End Try
     End Sub
 
@@ -107,8 +181,8 @@
                 Dim ErrorMessage As String = ""
                 If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
                 If ErrorMessage.Contains("不是有效的 json 文件") Then
-                    Log("[Download] 下载的 Mod 列表 json 文件损坏,已自动重试", LogLevel.Debug)
-                    PageLoaderRestart()
+                    Log($"[Download] 下载的{TypeNameSpaced}列表 json 文件损坏,已自动重试", LogLevel.Debug)
+                    CType(Parent, MyPageRight).PageLoaderRestart()
                 End If
         End Select
     End Sub
@@ -128,7 +202,7 @@
         CardPages.IsEnabled = False
         Page = NewPage
         FrmMain.BackToTop()
-        Log($"[Download] Mod 切换到第 {Page + 1} 页")
+        Log($"[Download] {TypeName}:切换到第 {Page + 1} 页")
         RunInThread(
         Sub()
             Thread.Sleep(100) '等待向上滚的动画结束
@@ -166,7 +240,7 @@
         If Not TextSearchVersion.IsDropDownOpen Then UpdateSearchLoaderVisibility()
     End Sub
     Private Sub UpdateSearchLoaderVisibility() Handles TextSearchVersion.DropDownClosed
-        If TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w") Then
+        If PageType = CompType.Mod AndAlso (TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w")) Then
             ComboSearchLoader.Visibility = Visibility.Visible
             Grid.SetColumnSpan(TextSearchVersion, 1)
         Else
@@ -178,4 +252,9 @@
 
 #End Region
 
+    '安装已有整合包按钮
+    Private Sub BtnSearchInstallModPack_Click(sender As Object, e As EventArgs) Handles BtnSearchInstallModPack.Click
+        ModpackInstall()
+    End Sub
+
 End Class

+ 0 - 0
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml → Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml


+ 47 - 27
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb → Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb

@@ -4,23 +4,29 @@
 #Region "加载器"
 
     Private CompFileLoader As New LoaderTask(Of Integer, List(Of CompFile))(
-        "Comp File", Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet(Project.Id, Project.FromCurseForge))
+        "Comp File",
+        Sub(Task)
+            LoadTargetFromAdditional()
+            Task.Output = CompFilesGet(Project.Id, Project.FromCurseForge)
+        End Sub)
 
     '初始化加载器信息
     Private Sub PageDownloadCompDetail_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
-        Project = FrmMain.PageCurrent.Additional(0)
-        TargetVersion = FrmMain.PageCurrent.Additional(2)
-        TargetLoader = FrmMain.PageCurrent.Additional(3)
+        LoadTargetFromAdditional()
         PageLoaderInit(Load, PanLoad, PanMain, CardIntro, CompFileLoader, AddressOf Load_OnFinish)
     End Sub
-    Private Sub PageDownloadCompDetail_Loaded(sender As Object, e As EventArgs) Handles Me.Loaded
-        'Initialized 只会执行一次
+    Public Sub LoadTargetFromAdditional() Handles Me.Loaded
         Project = FrmMain.PageCurrent.Additional(0)
         TargetVersion = FrmMain.PageCurrent.Additional(2)
         TargetLoader = FrmMain.PageCurrent.Additional(3)
+        PageType = FrmMain.PageCurrent.Additional(4)
     End Sub
     Private Project As CompProject
     Private TargetVersion As String, TargetLoader As CompModLoaderType
+    ''' <summary>
+    ''' 当前页面应展示的内容类别。可能为 Any。
+    ''' </summary>
+    Private PageType As CompType
     '自动重试
     Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
         Select Case CompFileLoader.State
@@ -62,18 +68,30 @@
 
     Private VersionFilter As String
     Private IsMajorVersionFilter As Boolean '是否按大版本号筛选(1.21 / 1.20 / 1.19 / ...)而非小版本号(1.21.1 / 1.21 / 1.20.4 / ...)
+    '筛选类型相同的结果(Modrinth 会返回 Mod、服务端插件、数据包混合的列表)
+    Private Function GetResults() As List(Of CompFile)
+        Dim Results As List(Of CompFile) = CompFileLoader.Output
+        If PageType = CompType.Any Then
+            Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList
+        Else
+            Results = Results.Where(Function(r) r.Type = PageType).ToList
+        End If
+        Return Results
+    End Function
     Private Sub Load_OnFinish()
+        Dim Results = GetResults()
+
         '初始化筛选器
         Dim VersionFilters As List(Of String)
 
         '按小版本号筛选?
         IsMajorVersionFilter = False
-        VersionFilters = CompFileLoader.Output.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
+        VersionFilters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
             Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
         '按大版本号筛选?
         If VersionFilters.Count >= 9 Then
             IsMajorVersionFilter = True
-            VersionFilters = CompFileLoader.Output.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
+            VersionFilters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
                 Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
         End If
 
@@ -100,7 +118,7 @@
             '自动选择
             Dim ToCheck As MyRadioButton = Nothing
             If TargetVersion <> "" Then
-                Dim TargetFile = CompFileLoader.Output.FirstOrDefault(Function(v) v.GameVersions.Contains(TargetVersion))
+                Dim TargetFile = Results.FirstOrDefault(Function(v) v.GameVersions.Contains(TargetVersion))
                 If TargetFile IsNot Nothing Then
                     Dim TargetGroup = GetGroupedVersionName(TargetVersion, IsMajorVersionFilter, True)
                     For Each Button As MyRadioButton In PanFilter.Children
@@ -118,24 +136,26 @@
         UpdateFilterResult()
     End Sub
     Private Sub UpdateFilterResult()
+        Dim Results = GetResults()
+
         Dim TargetCardName As String = If(TargetVersion <> "" OrElse TargetLoader <> CompModLoaderType.Any,
             $"所选版本:{If(TargetLoader <> CompModLoaderType.Any, TargetLoader.ToString & " ", "")}{TargetVersion}", "")
         '归类到卡片下
         Dim Dict As New SortedDictionary(Of String, List(Of CompFile))(New CardSorter(TargetCardName))
         Dict.Add("其他版本", New List(Of CompFile))
         Dim SupportedLoaders As New List(Of Integer)([Enum].GetValues(GetType(CompModLoaderType)))
-        For Each Version As CompFile In CompFileLoader.Output
+        For Each Version As CompFile In Results
             For Each GameVersion In Version.GameVersions
                 '检查是否符合版本筛选器
                 If VersionFilter IsNot Nothing AndAlso
                     GetGroupedVersionName(GameVersion, IsMajorVersionFilter, True) <> VersionFilter Then Continue For
                 '决定添加到哪个卡片
-                Dim Ver As String = GetGroupedVersionName(GameVersion, False, False)
+                Dim VerName As String = GetGroupedVersionName(GameVersion, False, False)
                 '遍历加入的加载器列表
                 Dim Loaders As New List(Of String)
-                If Project.ModLoaders.Count > 1 AndAlso '至少有两个加载器
-                    Project.Type = CompType.Mod AndAlso '是 Mod
-                    Ver.StartsWith("1.") Then '不是 “快照版本” 之类的
+                If Project.ModLoaders.Count > 1 AndAlso '工程至少有两个加载器
+                    Version.Type = CompType.Mod AndAlso '是 Mod
+                    VerName.StartsWith("1.") Then '不是 “快照版本” 之类的
                     For Each Loader In Version.ModLoaders
                         If Loader = CompModLoaderType.Quilt AndAlso Setup.Get("ToolDownloadIgnoreQuilt") Then Continue For
                         If SupportedLoaders.Contains(Loader) Then Loaders.Add(Loader.ToString & " ")
@@ -144,16 +164,16 @@
                 If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
                 '实际添加
                 For Each Loader In Loaders
-                    Dim TargetCard As String = Loader & Ver
+                    Dim TargetCard As String = Loader & VerName
                     If Not Dict.ContainsKey(TargetCard) Then Dict.Add(TargetCard, New List(Of CompFile))
                     If Not Dict(TargetCard).Contains(Version) Then Dict(TargetCard).Add(Version)
                 Next
             Next
         Next
         '添加筛选的版本的卡片
-        If TargetCardName <> "" Then
+        If TargetCardName <> "" AndAlso (VersionFilter Is Nothing OrElse GetGroupedVersionName(TargetVersion, IsMajorVersionFilter, True).StartsWithF(VersionFilter)) Then
             Dict.Add(TargetCardName, New List(Of CompFile))
-            For Each Version As CompFile In CompFileLoader.Output
+            For Each Version As CompFile In Results
                 If Version.GameVersions.Contains(TargetVersion) AndAlso
                    (TargetLoader = CompModLoaderType.Any OrElse Version.ModLoaders.Contains(TargetLoader)) Then
                     '检查是否符合版本筛选器
@@ -169,7 +189,7 @@
             For Each Pair As KeyValuePair(Of String, List(Of CompFile)) In Dict
                 If Not Pair.Value.Any() Then Continue For
                 '增加卡片
-                Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(Project.Type = CompType.ModPack, 9, 8)} 'FUTURE: Res
+                Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(PageType = CompType.ModPack, 9, 8)} '9 是安装,8 是另存为
                 Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
                 NewCard.Children.Add(NewStack)
                 NewCard.SwapControl = NewStack
@@ -178,7 +198,7 @@
                 If Pair.Key = TargetCardName OrElse
                    (FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
                    CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
-                    MyCard.StackInstall(NewStack, If(Project.Type = CompType.ModPack, 9, 8), Pair.Key) 'FUTURE: Res
+                    MyCard.StackInstall(NewStack, If(PageType = CompType.ModPack, 9, 8), Pair.Key) '9 是安装,8 是另存为
                 Else
                     NewCard.IsSwaped = True
                 End If
@@ -237,7 +257,7 @@
         AniControlEnabled -= 1
     End Sub
 
-    '整合包下载(安装)
+    '整合包安装
     Public Sub Install_Click(sender As MyListItem, e As EventArgs)
         Try
 
@@ -282,24 +302,24 @@
             Log(ex, "下载资源整合包失败", LogLevel.Feedback)
         End Try
     End Sub
-    'Mod、资源包下载;整合包另存为
+    '资源下载;整合包另存为
     Public Shared CachedFolder As String = Nothing '仅在本次缓存的下载文件夹
     Public Sub Save_Click(sender As Object, e As EventArgs)
         Dim File As CompFile = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag
         RunInNewThread(
         Sub()
             Try
-                Dim Desc As String = "资源"
-                Select Case Project.Type
+                Dim Desc As String = Nothing
+                Select Case File.Type
                     Case CompType.ModPack : Desc = "整合包"
                     Case CompType.Mod : Desc = "Mod "
                     Case CompType.ResourcePack : Desc = "资源包"
-                    Case CompType.DataPack : Desc = If(File.FileName.EndsWith(".jar"), "Mod ", "数据包") '有些项目同时包含 Mod 版本和数据包版本
                     Case CompType.Shader : Desc = "光影包"
+                    Case CompType.DataPack : Desc = "数据包"
                 End Select
                 '确认默认保存位置
                 Dim DefaultFolder As String = Nothing
-                If Project.Type = CompType.Mod Then
+                If File.Type = CompType.Mod Then
                     '获取 Mod 所需的加载器种类
                     Dim AllowForge As Boolean? = Nothing, AllowFabric As Boolean? = Nothing
                     If File.ModLoaders.Any Then '从文件中获取
@@ -389,13 +409,13 @@
                     Dim Target As String
                     Target = SelectSaveFile("选择保存位置", FileName,
                         Desc & "文件|" &
-                        If(Project.Type = CompType.Mod,
+                        If(File.Type = CompType.Mod,
                             If(File.FileName.EndsWith(".litemod"), "*.litemod", "*.jar"),
                             If(File.FileName.EndsWith(".mrpack"), "*.mrpack", "*.zip")), DefaultFolder)
                     If Not Target.Contains("\") Then Exit Sub
                     '构造步骤加载器
                     Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
-                    If Target <> DefaultFolder AndAlso Project.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target)
+                    If Target <> DefaultFolder AndAlso File.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target)
                     Dim Loaders As New List(Of LoaderBase)
                     Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
                     '启动

+ 26 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadDataPack.xaml

@@ -0,0 +1,26 @@
+<local:MyPageRight
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+    mc:Ignorable="d" x:Class="PageDownloadDataPack"
+    PanScroll="{Binding ElementName=Content}">
+    <local:PageComp x:Name="Content" PageType="DataPack" TypeName="数据包" TypeNameSpaced="数据包">
+        <local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
+        <local:MyComboBoxItem Content="世界元素" Tag="/worldgen" />
+        <local:MyComboBoxItem Content="科技" Tag="6951/technology" />
+        <local:MyComboBoxItem Content="游戏机制" Tag="/game-mechanics" />
+        <local:MyComboBoxItem Content="运输" Tag="/transportation" />
+        <local:MyComboBoxItem Content="仓储" Tag="/storage" />
+        <local:MyComboBoxItem Content="魔法" Tag="6952/magic" />
+        <local:MyComboBoxItem Content="冒险" Tag="6948/adventure" />
+        <local:MyComboBoxItem Content="幻想" Tag="6949/" />
+        <local:MyComboBoxItem Content="装饰" Tag="/decoration" />
+        <local:MyComboBoxItem Content="生物" Tag="/mobs" />
+        <local:MyComboBoxItem Content="实用" Tag="6953/utility" />
+        <local:MyComboBoxItem Content="装备与工具" Tag="/equipment" />
+        <local:MyComboBoxItem Content="性能优化" Tag="/optimization" />
+        <local:MyComboBoxItem Content="服务器" Tag="/social" />
+        <local:MyComboBoxItem Content="支持库" Tag="6950/library" />
+        <local:MyComboBoxItem Content="Mod 相关" Tag="6946/" />
+    </local:PageComp>
+</local:MyPageRight>

+ 2 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadDataPack.xaml.vb

@@ -0,0 +1,2 @@
+Public Class PageDownloadDataPack
+End Class

+ 35 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadMod.xaml

@@ -0,0 +1,35 @@
+<local:MyPageRight
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+    mc:Ignorable="d" x:Class="PageDownloadMod"
+    PanScroll="{Binding ElementName=Content}">
+    <local:PageComp x:Name="Content" PageType="Mod" TypeName="Mod" TypeNameSpaced=" Mod ">
+        <local:MyComboBoxItem Content="全部" Tag="" IsSelected="True" />
+        <local:MyComboBoxItem Content="世界元素" Tag="406/worldgen" />
+        <local:MyComboBoxItem Content="生物群系" Tag="407/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="维度" Tag="410/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="矿物与资源" Tag="408/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="天然结构" Tag="409/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="科技" Tag="412/technology" />
+        <local:MyComboBoxItem Content="管道与物流" Tag="415/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="自动化" Tag="4843/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="能源" Tag="417/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="红石" Tag="4558/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="食物与烹饪" Tag="436/food" />
+        <local:MyComboBoxItem Content="农业" Tag="416/" Margin="15,0,0,0" />
+        <local:MyComboBoxItem Content="游戏机制" Tag="/game-mechanics" />
+        <local:MyComboBoxItem Content="运输" Tag="414/transportation" />
+        <local:MyComboBoxItem Content="仓储" Tag="420/storage" />
+        <local:MyComboBoxItem Content="魔法" Tag="419/magic" />
+        <local:MyComboBoxItem Content="冒险" Tag="422/adventure" />
+        <local:MyComboBoxItem Content="装饰" Tag="424/decoration" />
+        <local:MyComboBoxItem Content="生物" Tag="411/mobs" />
+        <local:MyComboBoxItem Content="实用" Tag="5191/utility" />
+        <local:MyComboBoxItem Content="装备与工具" Tag="434/equipment" />
+        <local:MyComboBoxItem Content="性能优化" Tag="/optimization" />
+        <local:MyComboBoxItem Content="信息显示" Tag="423/" />
+        <local:MyComboBoxItem Content="服务器" Tag="435/social" />
+        <local:MyComboBoxItem Content="支持库" Tag="421/library" />
+    </local:PageComp>
+</local:MyPageRight>

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

@@ -0,0 +1,2 @@
+Public Class PageDownloadMod
+End Class

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

@@ -0,0 +1,28 @@
+<local:MyPageRight
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+    mc:Ignorable="d" x:Class="PageDownloadPack"
+    PanScroll="{Binding ElementName=Content}">
+    <local:PageComp x:Name="Content" PageType="ModPack" TypeName="整合包" TypeNameSpaced="整合包">
+        <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 官方发布" ToolTipService.HorizontalOffset="70" />
+        <local:MyComboBoxItem Tag="4480/" Content="基于地图" />
+        <local:MyComboBoxItem Tag="4481/lightweight" Content="轻量整合" />
+        <local:MyComboBoxItem Tag="4482/" Content="大型整合" />
+    </local:PageComp>
+</local:MyPageRight>

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

@@ -0,0 +1,2 @@
+Public Class PageDownloadPack
+End Class

+ 45 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadResourcePack.xaml

@@ -0,0 +1,45 @@
+<local:MyPageRight
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+    mc:Ignorable="d" x:Class="PageDownloadResourcePack"
+    PanScroll="{Binding ElementName=Content}">
+    <local:PageComp x:Name="Content" PageType="ResourcePack" TypeName="资源包" TypeNameSpaced="资源包">
+        <local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
+
+        <local:MyComboBoxItem Content="风格" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="原版风" Tag="403/vanilla-like" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="写实风" Tag="400/realistic" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="现代风" Tag="401/" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="中世纪" Tag="402/" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="蒸汽朋克" Tag="399/" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="主题化" Tag="/themed" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="简洁" Tag="/simplistic" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="装饰" Tag="/decoration" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="战斗" Tag="/combat" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="实用" Tag="/utility" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="改良" Tag="/tweaks" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="神经病" Tag="/cursed" />
+
+        <local:MyComboBoxItem Content="特性" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含实体" Tag="/entities" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含声音" Tag="/audio" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含字体" Tag="5244/fonts" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含模型" Tag="/models" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含语言" Tag="/locale" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="含 UI" Tag="/gui" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="核心着色器" Tag="/core-shaders" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="动态效果" Tag="404/" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="兼容 Mod" Tag="4465/modded" />
+
+        <local:MyComboBoxItem Content="分辨率" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="8x 或更低" Tag="/8x-" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="16x" Tag="393/16x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="32x" Tag="394/32x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="48x" Tag="/48x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="64x" Tag="395/64x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="128x" Tag="396/128x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="256x" Tag="397/256x" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="512x 或更高" Tag="398/512x+" />
+    </local:PageComp>
+</local:MyPageRight>

+ 2 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadResourcePack.xaml.vb

@@ -0,0 +1,2 @@
+Public Class PageDownloadResourcePack
+End Class

+ 31 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml

@@ -0,0 +1,31 @@
+<local:MyPageRight
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+    mc:Ignorable="d" x:Class="PageDownloadShader"
+    PanScroll="{Binding ElementName=Content}">
+    <local:PageComp x:Name="Content" PageType="Shader" TypeName="光影包" TypeNameSpaced="光影包">
+        <local:MyComboBoxItem Tag="" Content="全部" IsSelected="True" />
+        
+        <local:MyComboBoxItem Content="风格" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="原版风" Tag="6555/vanilla-like" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="幻想风" Tag="6554/fantasy" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="写实风" Tag="6553/realistic" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="半写实风" Tag="/semi-realistic" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="卡通风" Tag="/cartoon" />
+
+        <local:MyComboBoxItem Content="特性" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="原版可用" Tag="/vanilla" ToolTip="作为资源包加载,可以直接在原版游戏里使用。" ToolTipService.HorizontalOffset="70" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="彩色光照" Tag="/colored-lighting" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="路径追踪" Tag="/path-tracing" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="PBR" Tag="/pbr" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="反射" Tag="/reflections" />
+
+        <local:MyComboBoxItem Content="性能负荷" IsEnabled="False" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="极低" Tag="/potato" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="低" Tag="/low" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="中" Tag="/medium" />
+        <local:MyComboBoxItem Margin="15,0,0,0" Content="高" Tag="/high" />
+        
+    </local:PageComp>
+</local:MyPageRight>

+ 2 - 0
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadShader.xaml.vb

@@ -0,0 +1,2 @@
+Public Class PageDownloadShader
+End Class

+ 42 - 36
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -1188,8 +1188,8 @@ Retry:
                     outputWaitHandle.WaitOne(10000)
                     errorWaitHandle.WaitOne(10000)
                     process.Dispose()
-                    '检查是否安装成功:最后两行中是否有 true(true 可能在倒数第二行,见 #832)
-                    If LastResults.Last = "true" OrElse (LastResults.Count >= 2 AndAlso LastResults(LastResults.Count - 2) = "true") Then Exit Sub
+                    '检查是否安装成功:最后 5 行中是否有 true(true 可能在倒数数行,见 #832)
+                    If LastResults.Reverse().Take(5).Any(Function(l) l = "true") Then Exit Sub
                     Log(Join(LastResults, vbCrLf))
                     Dim LastLines As String = ""
                     For i As Integer = Math.Max(0, LastResults.Count - 5) To LastResults.Count - 1 '最后 5 行
@@ -1346,7 +1346,7 @@ Retry:
                         Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
                         Dim ClientMappings As JToken = RawJson("downloads")("client_mappings")
                         Libs.Add(New McLibToken With {
-                                 .IsJumpLoader = False, .IsNatives = False, .LocalPath = Address, .OriginalName = OriginalName,
+                                 .IsNatives = False, .LocalPath = Address, .OriginalName = OriginalName,
                                  .Url = ClientMappings("url"), .Size = ClientMappings("size"), .SHA1 = ClientMappings("sha1")})
                         Log($"[Download] 需要下载 Mappings:{ClientMappings("url")} (SHA1: {ClientMappings("sha1")})")
                     End If
@@ -2055,7 +2055,7 @@ Retry:
     ''' 获取合并安装加载器列表,并进行前期的缓存清理与 Java 检查工作。
     ''' </summary>
     ''' <exception cref="CancelledException" />
-    Public Function McInstallLoader(Request As McInstallRequest, Optional DontFixLibraries As Boolean = False) As List(Of LoaderBase)
+    Public Function McInstallLoader(Request As McInstallRequest) As List(Of LoaderBase)
         '获取缓存目录(安装 Mod 加载器的文件夹不能包含空格)
         Dim TempMcFolder As String = RequestTaskTempFolder(Request.OptiFineEntry IsNot Nothing OrElse Request.ForgeEntry IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing)
 
@@ -2086,17 +2086,13 @@ Retry:
         Dim LiteLoaderFolder As String = Nothing
         If Request.LiteLoaderEntry IsNot Nothing Then LiteLoaderFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-LiteLoader"
 
-        '决定版本隔离情况(#5970)
-        Dim Modable As Boolean = Request.FabricVersion IsNot Nothing OrElse Request.ForgeEntry IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing
-        Dim Version As New McVersion(VersionFolder)
-        Version.InitPathIndie(Modable)
-        Dim ModsFolder As String = If(Modable, VersionFolder, PathMcFolder) & "mods\"
-
         '判断 OptiFine 是否作为 Mod 进行下载
+        Dim Modable As Boolean = Request.FabricVersion IsNot Nothing OrElse Request.ForgeEntry IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing
+        Dim ModsTempFolder As String = TempMcFolder & "mods\"
         Dim OptiFineAsMod As Boolean = Request.OptiFineEntry IsNot Nothing AndAlso Modable '选择了 OptiFine 与任意 Mod 加载器
         If OptiFineAsMod Then
             Log("[Download] OptiFine 将作为 Mod 进行下载")
-            OptiFineFolder = ModsFolder
+            OptiFineFolder = ModsTempFolder
         End If
 
         '记录日志
@@ -2118,57 +2114,67 @@ Retry:
         LoaderList.Add(New LoaderTask(Of Integer, Integer)("添加忽略标识", Sub() WriteFile(VersionFolder & ".pclignore", "用于临时地在 PCL 的版本列表中屏蔽此版本。")) With {.Show = False, .Block = False})
         'Fabric API
         If Request.FabricApi IsNot Nothing Then
-            LoaderList.Add(New LoaderDownload("下载 Fabric API", New List(Of NetFile) From {Request.FabricApi.ToNetFile(ModsFolder)}) With {.ProgressWeight = 3, .Block = False})
+            LoaderList.Add(New LoaderDownload("下载 Fabric API", New List(Of NetFile) From {Request.FabricApi.ToNetFile(ModsTempFolder)}) With {.ProgressWeight = 3, .Block = False})
         End If
         'OptiFabric
         If Request.OptiFabric IsNot Nothing Then
-            LoaderList.Add(New LoaderDownload("下载 OptiFabric", New List(Of NetFile) From {Request.OptiFabric.ToNetFile(ModsFolder)}) With {.ProgressWeight = 3, .Block = False})
+            LoaderList.Add(New LoaderDownload("下载 OptiFabric", New List(Of NetFile) From {Request.OptiFabric.ToNetFile(ModsTempFolder)}) With {.ProgressWeight = 3, .Block = False})
         End If
         '原版
-        Dim ClientLoader = New LoaderCombo(Of String)("下载原版 " & Request.MinecraftName, McDownloadClientLoader(Request.MinecraftName, Request.MinecraftJson, Request.TargetVersionName)) With {.Show = False, .ProgressWeight = 39, .Block = Request.ForgeVersion Is Nothing AndAlso Request.OptiFineEntry Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing}
+        Dim ClientLoader = New LoaderCombo(Of String)("下载原版 " & Request.MinecraftName, McDownloadClientLoader(Request.MinecraftName, Request.MinecraftJson, Request.TargetVersionName)) With {.Show = False, .ProgressWeight = 39,
+            .Block = Request.ForgeVersion Is Nothing AndAlso Request.NeoForgeVersion Is Nothing AndAlso Request.OptiFineEntry Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing}
         LoaderList.Add(ClientLoader)
         'OptiFine
         If Request.OptiFineEntry IsNot Nothing Then
             If OptiFineAsMod Then
-                LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineSaveLoader(Request.OptiFineEntry, OptiFineFolder & Request.OptiFineEntry.NameFile)) With {.Show = False, .ProgressWeight = 16, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
+                LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineSaveLoader(Request.OptiFineEntry, OptiFineFolder & Request.OptiFineEntry.NameFile)) With {.Show = False, .ProgressWeight = 16,
+                    .Block = Request.ForgeVersion Is Nothing AndAlso Request.NeoForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
             Else
-                LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineLoader(Request.OptiFineEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 24, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
+                LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineLoader(Request.OptiFineEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 24,
+                    .Block = Request.ForgeVersion Is Nothing AndAlso Request.NeoForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
             End If
         End If
         'Forge
         If Request.ForgeVersion IsNot Nothing Then
-            LoaderList.Add(New LoaderCombo(Of String)("下载 Forge " & Request.ForgeVersion, McDownloadForgelikeLoader(False, Request.ForgeVersion, "forge-" & Request.ForgeVersion, Request.MinecraftName, Request.ForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder)) With {.Show = False, .ProgressWeight = 25, .Block = Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing AndAlso Request.NeoForgeEntry Is Nothing})
+            LoaderList.Add(New LoaderCombo(Of String)("下载 Forge " & Request.ForgeVersion, McDownloadForgelikeLoader(False, Request.ForgeVersion, "forge-" & Request.ForgeVersion, Request.MinecraftName, Request.ForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder)) With {.Show = False, .ProgressWeight = 25,
+                .Block = Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing AndAlso Request.NeoForgeEntry Is Nothing})
         End If
         'NeoForge
         If Request.NeoForgeVersion IsNot Nothing Then
-            LoaderList.Add(New LoaderCombo(Of String)("下载 NeoForge " & Request.NeoForgeVersion, McDownloadForgelikeLoader(True, Request.NeoForgeVersion, "neoforge-" & Request.NeoForgeVersion, Request.MinecraftName, Request.NeoForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder)) With {.Show = False, .ProgressWeight = 25, .Block = Request.ForgeEntry Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
+            LoaderList.Add(New LoaderCombo(Of String)("下载 NeoForge " & Request.NeoForgeVersion, McDownloadForgelikeLoader(True, Request.NeoForgeVersion, "neoforge-" & Request.NeoForgeVersion, Request.MinecraftName, Request.NeoForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder)) With {.Show = False, .ProgressWeight = 25,
+                .Block = Request.ForgeEntry Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
         End If
         'LiteLoader
         If Request.LiteLoaderEntry IsNot Nothing Then
-            LoaderList.Add(New LoaderCombo(Of String)("下载 LiteLoader " & Request.MinecraftName, McDownloadLiteLoaderLoader(Request.LiteLoaderEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 1, .Block = Request.FabricVersion Is Nothing})
+            LoaderList.Add(New LoaderCombo(Of String)("下载 LiteLoader " & Request.MinecraftName, McDownloadLiteLoaderLoader(Request.LiteLoaderEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 1,
+                .Block = Request.FabricVersion Is Nothing})
         End If
         'Fabric
         If Request.FabricVersion IsNot Nothing Then
-            LoaderList.Add(New LoaderCombo(Of String)("下载 Fabric " & Request.FabricVersion, McDownloadFabricLoader(Request.FabricVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2, .Block = True})
+            LoaderList.Add(New LoaderCombo(Of String)("下载 Fabric " & Request.FabricVersion, McDownloadFabricLoader(Request.FabricVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2,
+                .Block = True})
         End If
         '合并安装
         LoaderList.Add(New LoaderTask(Of String, String)("安装游戏",
-            Sub(Task As LoaderTask(Of String, String))
-                InstallMerge(VersionFolder, VersionFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, NeoForgeFolder, Request.NeoForgeVersion, FabricFolder, LiteLoaderFolder)
-                Task.Progress = 0.3
-                If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries")
-                If Directory.Exists(TempMcFolder & "mods") Then CopyDirectory(TempMcFolder & "mods", ModsFolder)
-                '新建 mods 文件夹
-                If Request.ForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing Then
-                    Directory.CreateDirectory(ModsFolder)
-                    Log("[Download] 自动创建 mods 文件夹:" & ModsFolder)
-                End If
-            End Sub) With {.ProgressWeight = 2, .Block = True})
+        Sub(Task As LoaderTask(Of String, String))
+            '合并 JSON
+            MergeJson(VersionFolder, VersionFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, NeoForgeFolder, Request.NeoForgeVersion, FabricFolder, LiteLoaderFolder)
+            Task.Progress = 0.2
+            '迁移文件
+            If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries")
+            Task.Progress = 0.8
+            Dim ModsFolder = New McVersion(VersionFolder).PathIndie & "mods\" '版本隔离信息在此时被决定
+            If Directory.Exists(ModsTempFolder) Then
+                CopyDirectory(ModsTempFolder, ModsFolder)
+            ElseIf Modable Then
+                Directory.CreateDirectory(ModsFolder)
+                Log("[Download] 自动创建 mods 文件夹:" & ModsFolder)
+            End If
+        End Sub) With {.ProgressWeight = 2, .Block = True})
         '补全文件
-        If Not DontFixLibraries AndAlso
-        (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.BeforeFirst(".") >= 20) OrElse Request.NeoForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then
+        If Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.BeforeFirst(".") >= 20) OrElse Request.NeoForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing Then
             Dim LoadersLib As New List(Of LoaderBase)
-            LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
+            LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
             LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
             LoaderList.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
         End If
@@ -2179,9 +2185,9 @@ Retry:
     End Function
 
     ''' <summary>
-    ''' 将多个版本 Json 进行合并,如果目标已存在则直接覆盖。失败会抛出异常。
+    ''' 将多个版本 JSON 进行合并,如果目标已存在则直接覆盖。失败会抛出异常。
     ''' </summary>
-    Private Sub InstallMerge(OutputFolder As String, MinecraftFolder As String, Optional OptiFineFolder As String = Nothing, Optional OptiFineAsMod As Boolean = False, Optional ForgeFolder As String = Nothing, Optional ForgeVersion As String = Nothing, Optional NeoForgeFolder As String = Nothing, Optional NeoForgeVersion As String = Nothing, Optional FabricFolder As String = Nothing, Optional LiteLoaderFolder As String = Nothing)
+    Private Sub MergeJson(OutputFolder As String, MinecraftFolder As String, Optional OptiFineFolder As String = Nothing, Optional OptiFineAsMod As Boolean = False, Optional ForgeFolder As String = Nothing, Optional ForgeVersion As String = Nothing, Optional NeoForgeFolder As String = Nothing, Optional NeoForgeVersion As String = Nothing, Optional FabricFolder As String = Nothing, Optional LiteLoaderFolder As String = Nothing)
         Log("[Download] 开始进行版本合并,输出:" & OutputFolder & ",Minecraft:" & MinecraftFolder &
             If(OptiFineFolder IsNot Nothing, ",OptiFine:" & OptiFineFolder, "") &
             If(ForgeFolder IsNot Nothing, ",Forge:" & ForgeFolder, "") &

+ 0 - 111
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadDataPack.xaml

@@ -1,111 +0,0 @@
-<local:MyPageRight
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-    mc:Ignorable="d" x:Class="PageDownloadDataPack"
-    PanScroll="{Binding ElementName=PanBack}">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel Orientation="Vertical" Margin="25">
-            <local:MyCard Title="搜索数据包" Margin="0,0,0,15" x:Name="PanAlways">
-                <StackPanel Margin="25,40,25,15">
-                    <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
-                        <Grid.ColumnDefinitions>
-                            <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,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" 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.20.6" />
-                            <local:MyComboBoxItem Content="1.20.4" />
-                            <local:MyComboBoxItem Content="1.20.1" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <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.12.2" />
-                            <local:MyComboBoxItem Content="1.11.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <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 Content="冒险" Tag="6948/adventure" />
-                            <local:MyComboBoxItem Content="Cursed" Tag="/cursed" />
-                            <local:MyComboBoxItem Content="装饰" Tag="6554/decoration" />
-                            <local:MyComboBoxItem Content="经济" Tag="6553/economy" />
-                            <local:MyComboBoxItem Content="装备" Tag="/equipment" />
-                            <local:MyComboBoxItem Content="食物" Tag="6555/food" />
-                            <local:MyComboBoxItem Content="游戏机制" Tag="/game-mechanics" />
-                            <local:MyComboBoxItem Content="支持库" Tag="6950/library" />
-                            <local:MyComboBoxItem Content="魔法" Tag="6952/magic" />
-                            <local:MyComboBoxItem Content="管理" Tag="/management" />
-                            <local:MyComboBoxItem Content="小游戏" Tag="/minigame" />
-                            <local:MyComboBoxItem Content="生物" Tag="/mobs" />
-                            <local:MyComboBoxItem Content="优化" Tag="/optimization" />
-                            <local:MyComboBoxItem Content="社交" Tag="/social" />
-                            <local:MyComboBoxItem Content="存储" Tag="/storage" />
-                            <local:MyComboBoxItem Content="科技" Tag="6951/technology" />
-                            <local:MyComboBoxItem Content="交通" Tag="/transportation" />
-                            <local:MyComboBoxItem Content="实用工具" Tag="6953/utility" />
-                            <local:MyComboBoxItem Content="世界生成" Tag="/worldgen" />
-                            <local:MyComboBoxItem Content="Mod 支持" Tag="6946/" />
-                            <local:MyComboBoxItem Content="杂项" Tag="6947/" />
-                            <local:MyComboBoxItem Content="幻想" Tag="6949/" />
-                        </local:MyComboBox>
-                    </Grid>
-                    <Grid Margin="0,4,0,6" Height="35">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <StackPanel x:Name="PanContent">
-                <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
-                <local:MyCard x:Name="CardProjects" UseAnimation="False">
-                    <StackPanel x:Name="PanProjects" Margin="12" />
-                </local:MyCard>
-                <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
-                    <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
-                        <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
-                            Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
-                        <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
-                            Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
-                        <TextBlock x:Name="LabPage" Text="1" 
-                                   Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
-                        <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
-                            Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
-                    </StackPanel>
-                </local:MyCard>
-            </StackPanel>
-            <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
-                <local:MyLoading Text="正在获取数据包列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>

+ 0 - 122
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadDataPack.xaml.vb

@@ -1,122 +0,0 @@
-Public Class PageDownloadDataPack
-
-    Public Const PageSize = 40
-
-    '加载器信息
-    Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject Shader", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
-    Public Shared Storage As New CompProjectStorage
-    Public Shared Page As Integer = 0
-    Private Sub PageDownloadShader_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
-        If McVersionHighest = -1 Then 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.DataPack, Storage, (Page + 1) * PageSize)
-        If FrmDownloadDataPack IsNot Nothing Then
-            With Request
-                .SearchText = FrmDownloadDataPack.TextSearchName.Text
-                .GameVersion = If(FrmDownloadDataPack.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
-                    If(FrmDownloadDataPack.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadDataPack.TextSearchVersion.Text.Contains("w"), FrmDownloadDataPack.TextSearchVersion.Text, Nothing))
-                .Tag = FrmDownloadDataPack.ComboSearchTag.SelectedItem.Tag
-                .Source = CType(Val(FrmDownloadDataPack.ComboSearchSource.SelectedItem.Tag), CompSourceType)
-            End With
-        End If
-        Return Request
-    End Function
-
-    '结果 UI 化
-    Private Sub Load_OnFinish()
-        Try
-            Log($"[Comp] 开始可视化数据包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
-            '列表项
-            PanProjects.Children.Clear()
-            For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
-                PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, False))
-            Next
-            '页码
-            CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
-                                      Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
-                                      Visibility.Visible, Visibility.Collapsed)
-            LabPage.Text = Page + 1
-            BtnPageFirst.IsEnabled = Page > 1
-            BtnPageFirst.Opacity = If(BtnPageFirst.IsEnabled, 1, 0.2)
-            BtnPageLeft.IsEnabled = Page > 0
-            BtnPageLeft.Opacity = If(BtnPageLeft.IsEnabled, 1, 0.2)
-            BtnPageRight.IsEnabled = Storage.Results.Count > PageSize * (Page + 1) OrElse
-                                     Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
-            BtnPageRight.Opacity = If(BtnPageRight.IsEnabled, 1, 0.2)
-            '错误信息
-            If Storage.ErrorMessage Is Nothing Then
-                HintError.Visibility = Visibility.Collapsed
-            Else
-                HintError.Visibility = Visibility.Visible
-                HintError.Text = Storage.ErrorMessage
-            End If
-            '强制返回顶部
-            PanBack.ScrollToTop()
-        Catch ex As Exception
-            Log(ex, "可视化数据包列表出错", LogLevel.Feedback)
-        End Try
-    End Sub
-
-    '自动重试
-    Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
-        Select Case Loader.State
-            Case LoadState.Failed
-                Dim ErrorMessage As String = ""
-                If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
-                If ErrorMessage.Contains("不是有效的 JSON 文件") Then
-                    Log("[Download] 下载的数据包列表 JSON 文件损坏,已自动重试", LogLevel.Debug)
-                    PageLoaderRestart()
-                End If
-        End Select
-    End Sub
-
-    '切换页码
-
-    Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
-        ChangePage(0)
-    End Sub
-    Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
-        ChangePage(Page - 1)
-    End Sub
-    Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
-        ChangePage(Page + 1)
-    End Sub
-    Private Sub ChangePage(NewPage As Integer)
-        CardPages.IsEnabled = False
-        Page = NewPage
-        FrmMain.BackToTop()
-        Log($"[Download] 数据包切换到第 {Page + 1} 页")
-        RunInThread(Sub()
-                        Thread.Sleep(100) '等待向上滚的动画结束
-                        RunInUi(Sub() CardPages.IsEnabled = True)
-                        Loader.Start()
-                    End Sub)
-    End Sub
-
-#Region "搜索"
-
-    '搜索按钮
-    Private Sub StartNewSearch() Handles BtnSearchRun.Click
-        Page = 0
-        If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
-        Loader.Start()
-    End Sub
-    Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
-        If e.Key = Key.Enter Then StartNewSearch()
-    End Sub
-
-    '重置按钮
-    Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click
-        TextSearchName.Text = ""
-        TextSearchVersion.Text = "全部 (也可自行输入)"
-        TextSearchVersion.SelectedIndex = 0
-        ComboSearchSource.SelectedIndex = 0
-        ComboSearchTag.SelectedIndex = 0
-        Loader.LastFinishedTime = 0 '要求强制重新开始
-    End Sub
-
-#End Region
-
-End Class

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

@@ -101,12 +101,12 @@
                 FabricApi_Loaded()
                 OptiFabric_Loaded()
                 SelectReload()
+                PanMinecraft.Visibility = Visibility.Collapsed
             End Sub, After:=True),
             AaOpacity(PanSelect, 1 - PanSelect.Opacity, 90, 100),
             AaTranslateX(PanSelect, -CType(PanSelect.RenderTransform, TranslateTransform).X, 200, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
             AaCode(
             Sub()
-                PanMinecraft.Visibility = Visibility.Collapsed
                 PanBack.IsHitTestVisible = True
                 '初始化 Binding
                 If IsFirstLoaded Then Exit Sub
@@ -1254,9 +1254,9 @@
         '确认版本隔离
         If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
            (Setup.Get("LaunchArgumentIndieV2") = 0 OrElse Setup.Get("LaunchArgumentIndieV2") = 2) Then
-            If MyMsgBox("你尚未开启版本隔离,这会导致多个版本共用同一个 Mod 文件夹。" & vbCrLf &
-                        "因此游戏可能会因为读取到与当前版本不符的 Mod 而崩溃。" & vbCrLf &
-                        "推荐在开始下载前,在 设置 → 启动选项 → 版本隔离 中开启版本隔离!", "版本隔离提示", "取消下载", "继续") = 1 Then
+            If MyMsgBox("你尚未开启版本隔离,多个 MC 版本会共用同一个 Mod 文件夹。" & vbCrLf &
+                        "因此,游戏可能会因为读取到与当前版本不符的 Mod 而崩溃。" & vbCrLf &
+                        "推荐先在 设置 → 启动选项 → 默认版本隔离 中开启版本隔离!", "版本隔离提示", "取消下载", "继续") = 1 Then
                 Exit Sub
             End If
         End If

+ 32 - 32
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml

@@ -71,38 +71,38 @@
             <TextBlock Text="社区资源" Margin="13,30,5,4" Opacity="0.6" FontSize="12" />
             <local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="11" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod"
                           LogoScale="0.97" Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z">
-            <local:MyListItem.Buttons>
-                <x:Array Type="{x:Type local:MyIconButton}">
-                    <local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
-                </x:Array>
-            </local:MyListItem.Buttons>
-        </local:MyListItem>
-        <local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="12" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
-                          LogoScale="0.98" Logo="M511.488 995.328a128.654222 128.654222 0 0 1-57.116444-13.112889L70.769778 791.808a126.833778 126.833778 0 0 1-70.769778-113.777778V311.608889a126.179556 126.179556 0 0 1 15.36-60.103111V248.604444c1.479111-2.901333 3.356444-5.603556 5.518222-8.021333a127.630222 127.630222 0 0 1 49.891556-42.325333L454.371556 13.368889a128.739556 128.739556 0 0 1 112.981333 0l383.601778 190.407111a126.862222 126.862222 0 0 1 72.049777 113.379556v360.874666a126.805333 126.805333 0 0 1-70.769777 115.939556L568.604444 984.32c-17.92 7.964444-37.461333 11.747556-57.116444 11.008z m42.638222-470.897778v370.204445l360.192-178.545778c14.449778-7.253333 23.552-21.987556 23.438222-38.087111v-335.928889L554.097778 524.430222zM85.248 330.666667v347.335111a42.268444 42.268444 0 0 0 23.438222 38.087111l360.192 178.545778V523.576889L85.248 330.666667zM135.537778 260.835556l375.950222 189.952 137.671111-65.564445L286.435556 188.074667 135.537778 260.864z m245.105778-118.471112l363.576888 197.973334 150.897778-71.480889-365.283555-180.224a42.922667 42.922667 0 0 0-37.518223 0l-111.672888 53.731555z">
-            <local:MyListItem.Buttons>
-                <x:Array Type="{x:Type local:MyIconButton}">
-                    <local:MyIconButton Tag="12" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
-                </x:Array>
-            </local:MyListItem.Buttons>
-        </local:MyListItem>
-        <local:MyListItem x:Name="ItemResourcePack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="13" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="资源包"
-                  LogoScale="0.98" Logo="M913.066667 264.533333l-371.2-209.066666c-25.6-12.8-59.733333-12.8-85.333334 0L89.6 264.533333C34.133333 298.666667 34.133333 379.733333 89.6 413.866667l371.2 209.066666c25.6 12.8 59.733333 12.8 85.333333 0l371.2-209.066666c55.466667-34.133333 55.466667-119.466667-4.266666-149.333334z m-413.866667 281.6L132.266667 337.066667 499.2 128l371.2 209.066667-371.2 209.066666z M46.933333 516.266667c12.8-21.333333 38.4-25.6 59.733334-17.066667l384 221.866667c12.8 8.533333 29.866667 8.533333 42.666666 0l388.266667-217.6c21.333333-12.8 46.933333-4.266667 59.733333 17.066666 12.8 21.333333 4.266667 46.933333-17.066666 59.733334l-388.266667 217.6c-38.4 21.333333-89.6 21.333333-128 0l-384-221.866667c-21.333333-12.8-25.6-38.4-17.066667-59.733333z M106.666667 669.866667c-21.333333-12.8-46.933333-4.266667-59.733334 17.066666-12.8 21.333333-4.266667 46.933333 17.066667 59.733334l388.266667 217.6c38.4 21.333333 85.333333 21.333333 128 0l379.733333-217.6c21.333333-12.8 25.6-38.4 17.066667-59.733334-12.8-21.333333-38.4-25.6-59.733334-17.066666l-379.733333 217.6c-12.8 8.533333-29.866667 8.533333-42.666667 0l-388.266666-217.6z">
-            <local:MyListItem.Buttons>
-                <x:Array Type="{x:Type local:MyIconButton}">
-                    <local:MyIconButton Tag="13" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
-                </x:Array>
-            </local:MyListItem.Buttons>
-        </local:MyListItem>
-        <local:MyListItem x:Name="ItemShader" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="14" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="光影包"
-              LogoScale="0.98" Logo="M640 810.666667 384 810.666667c-25.6 0-42.666667-17.066667-42.666667-42.666667l0-21.333333c0-59.733333-25.6-119.466667-72.533333-166.4C200.533333 512 166.4 422.4 170.666667 328.533333 179.2 149.333333 328.533333 4.266667 507.733333 0L512 0c187.733333 0 341.333333 153.6 341.333333 341.333333 0 89.6-34.133333 179.2-102.4 243.2-46.933333 42.666667-68.266667 102.4-68.266667 162.133333L682.666667 768C682.666667 793.6 665.6 810.666667 640 810.666667zM426.666667 725.333333l170.666667 0c4.266667-76.8 38.4-145.066667 93.866667-204.8C742.4 473.6 768 409.6 768 341.333333c0-140.8-115.2-256-256-256l-4.266667 0C375.466667 89.6 260.266667 196.266667 256 332.8 251.733333 401.066667 281.6 469.333333 328.533333 520.533333 388.266667 580.266667 422.4 652.8 426.666667 725.333333z M512 1024 512 1024c-93.866667 0-170.666667-76.8-170.666667-170.666667l0-85.333333c0-25.6 17.066667-42.666667 42.666667-42.666667l256 0c25.6 0 42.666667 17.066667 42.666667 42.666667l0 85.333333C682.666667 947.2 605.866667 1024 512 1024zM426.666667 810.666667l0 42.666667c0 46.933333 38.4 85.333333 85.333333 85.333333L512 938.666667c46.933333 0 85.333333-38.4 85.333333-85.333333l0-42.666667L426.666667 810.666667z M384 384C358.4 384 341.333333 366.933333 341.333333 341.333333c0-93.866667 76.8-170.666667 170.666667-170.666667 25.6 0 42.666667 17.066667 42.666667 42.666667s-17.066667 42.666667-42.666667 42.666667c-46.933333 0-85.333333 38.4-85.333333 85.333333C426.666667 366.933333 409.6 384 384 384z">
-            <local:MyListItem.Buttons>
-                <x:Array Type="{x:Type local:MyIconButton}">
-                    <local:MyIconButton Tag="14" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
-                </x:Array>
-            </local:MyListItem.Buttons>
-        </local:MyListItem>
-            <local:MyListItem x:Name="ItemDataPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="15" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="数据包"
-              LogoScale="0.98" Logo="M511.488 995.328a128.654222 128.654222 0 0 1-57.116444-13.112889L70.769778 791.808a126.833778 126.833778 0 0 1-70.769778-113.777778V311.608889a126.179556 126.179556 0 0 1 15.36-60.103111V248.604444c1.479111-2.901333 3.356444-5.603556 5.518222-8.021333a127.630222 127.630222 0 0 1 49.891556-42.325333L454.371556 13.368889a128.739556 128.739556 0 0 1 112.981333 0l383.601778 190.407111a126.862222 126.862222 0 0 1 72.049777 113.379556v360.874666a126.805333 126.805333 0 0 1-70.769777 115.939556L568.604444 984.32c-17.92 7.964444-37.461333 11.747556-57.116444 11.008z m42.638222-470.897778v370.204445l360.192-178.545778c14.449778-7.253333 23.552-21.987556 23.438222-38.087111v-335.928889L554.097778 524.430222zM85.248 330.666667v347.335111a42.268444 42.268444 0 0 0 23.438222 38.087111l360.192 178.545778V523.576889L85.248 330.666667zM135.537778 260.835556l375.950222 189.952 137.671111-65.564445L286.435556 188.074667 135.537778 260.864z m245.105778-118.471112l363.576888 197.973334 150.897778-71.480889-365.283555-180.224a42.922667 42.922667 0 0 0-37.518223 0l-111.672888 53.731555z">
+                <local:MyListItem.Buttons>
+                    <x:Array Type="{x:Type local:MyIconButton}">
+                        <local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                    </x:Array>
+                </local:MyListItem.Buttons>
+            </local:MyListItem>
+            <local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="12" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
+                          LogoScale="0.98" Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z">
+                <local:MyListItem.Buttons>
+                    <x:Array Type="{x:Type local:MyIconButton}">
+                        <local:MyIconButton Tag="12" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                    </x:Array>
+                </local:MyListItem.Buttons>
+            </local:MyListItem>
+            <local:MyListItem x:Name="ItemDataPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="13" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="数据包"
+                  LogoScale="0.96" Logo="M445 545c18 0 33 14 33 33v149a182 182 0 1 1-182-182z m282 0a182 182 0 1 1-182 182v-149c0-18 14-33 33-33zM412 611H296a116 116 0 1 0 116 116V611z m315 0H611v116a116 116 0 1 0 116-116zM296 113a182 182 0 0 1 182 182v149a33 33 0 0 1-33 33H296a182 182 0 0 1 0-365z m431 0a182 182 0 0 1 0 365h-149a33 33 0 0 1-33-33V296A182 182 0 0 1 727 113zM296 180a116 116 0 0 0 0 232H412V296c0-62-48-112-109-115z m431 0a116 116 0 0 0-116 116V412h116c62 0 112-48 115-109l0-6a116 116 0 0 0-116-116z">
+                <local:MyListItem.Buttons>
+                    <x:Array Type="{x:Type local:MyIconButton}">
+                        <local:MyIconButton Tag="13" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                    </x:Array>
+                </local:MyListItem.Buttons>
+            </local:MyListItem>
+            <local:MyListItem x:Name="ItemResourcePack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="14" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="资源包"
+                  LogoScale="0.81" Logo="M884.4 130.6H140.6c-41.9 0-76 34.1-76 76v613.3c0 41.9 34.1 76 76 76h743.8c41.9 0 76-34.1 76-76V206.6c0-41.9-34.1-76-76-76z m-743.8 72h743.8c2.2 0 4 1.8 4 4v371.3L796 461.7c-21.7-27.3-54.1-43.7-88.8-45-34.7-1.3-68.1 12.7-91.7 38.3L454 630c-2.8 3-7.2 3.5-10.5 1.3l-103-70.3c-31.5-21.5-72.8-22.5-105.3-2.7l-98.6 60.1V206.6c0-2.2 1.8-4 4-4z m743.8 621.3H140.6c-2.2 0-4-1.8-4-4V702.7l136.1-83c8.4-5.1 19.1-4.8 27.2 0.7l103 70.3c15.9 10.8 35.1 15.6 54.2 13.4 19-2.2 36.7-11.2 49.8-25.3l161.6-175c9.5-10.3 22.3-15.7 36.2-15.1 13.9 0.5 26.3 6.8 35.1 17.8l148.7 187v126.3c-0.1 2.3-1.9 4.1-4.1 4.1z M231.8 400.6a69.6 69.6 0 1 0 98.4-98.4 69.6 69.6 0 1 0-98.4 98.4Z">
+                <local:MyListItem.Buttons>
+                    <x:Array Type="{x:Type local:MyIconButton}">
+                        <local:MyIconButton Tag="14" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                    </x:Array>
+                </local:MyListItem.Buttons>
+            </local:MyListItem>
+            <local:MyListItem x:Name="ItemShader" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="15" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="光影包"
+                  LogoScale="1.04" Logo="M512 0c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42V42c0-25 17-42 42-42zM512 853c25 0 42 17 42 42v85c0 25-17 42-42 42s-42-17-42-42v-85c0-25 17-42 42-42zM209 149c-17-17-42-17-59 0-17 17-17 42 0 59l59 59c17 17 42 17 59 0 17-17 17-42 0-59L209 149zM755 755c17-17 42-17 59 0l59 59c17 17 17 42 0 59s-42 17-59 0l-59-59c-17-17-17-46 0-59zM42 469c-25 0-42 17-42 42s17 42 42 42h85c25 0 42-17 42-42s-17-42-42-42H42zM853 512c0-25 17-42 42-42h85c25 0 42 17 42 42s-17 42-42 42h-85c-25 0-42-17-42-42zM149 814c-17 17-17 42 0 59 17 17 42 17 59 0l59-59c17-17 17-42 0-59-17-17-42-17-59 0l-59 59zM755 268c-17-17-17-42 0-59l59-59c17-17 42-17 59 0s17 42 0 59l-59 59c-17 17-46 17-59 0z M512 213c-166 0-298 132-298 298s132 298 298 298 298-132 298-298-132-298-298-298z m0 512c-119 0-213-93-213-213s93-213 213-213 213 93 213 213-93 213-213 213z">
                 <local:MyListItem.Buttons>
                     <x:Array Type="{x:Type local:MyIconButton}">
                         <local:MyIconButton Tag="15" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />

+ 39 - 26
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb

@@ -11,7 +11,7 @@
     ''' <summary>
     ''' 勾选事件改变页面。
     ''' </summary>
-    Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemClient.Check, ItemOptiFine.Check, ItemForge.Check, ItemNeoForge.Check, ItemLiteLoader.Check, ItemMod.Check, ItemFabric.Check, ItemPack.Check, ItemDataPack.Check, ItemShader.Check, ItemResourcePack.Check
+    Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemClient.Check, ItemOptiFine.Check, ItemForge.Check, ItemNeoForge.Check, ItemLiteLoader.Check, ItemMod.Check, ItemFabric.Check, ItemPack.Check, ItemResourcePack.Check, ItemShader.Check, ItemDataPack.Check
         '尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0
         '若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False)
         If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag))
@@ -47,15 +47,15 @@
             Case FormMain.PageSubType.DownloadPack
                 If FrmDownloadPack Is Nothing Then FrmDownloadPack = New PageDownloadPack
                 Return FrmDownloadPack
-            Case FormMain.PageSubType.DownloadDataPack
-                If FrmDownloadDataPack Is Nothing Then FrmDownloadDataPack = New PageDownloadDataPack
-                Return FrmDownloadDataPack
-            Case FormMain.PageSubType.DownloadShader
-                If FrmDownloadShader Is Nothing Then FrmDownloadShader = New PageDownloadShader
-                Return FrmDownloadShader
             Case FormMain.PageSubType.DownloadResourcePack
                 If FrmDownloadResourcePack Is Nothing Then FrmDownloadResourcePack = New PageDownloadResourcePack
                 Return FrmDownloadResourcePack
+            Case FormMain.PageSubType.DownloadShader
+                If FrmDownloadShader Is Nothing Then FrmDownloadShader = New PageDownloadShader
+                Return FrmDownloadShader
+            Case FormMain.PageSubType.DownloadDataPack
+                If FrmDownloadDataPack Is Nothing Then FrmDownloadDataPack = New PageDownloadDataPack
+                Return FrmDownloadDataPack
             Case Else
                 Throw New Exception("未知的下载子页面种类:" & ID)
         End Select
@@ -119,37 +119,50 @@
                 DlOptiFabricLoader.Start(IsForceRestart:=True)
                 ItemInstall.Checked = True
             Case FormMain.PageSubType.DownloadMod
-                PageDownloadMod.Storage = New CompProjectStorage
-                PageDownloadMod.Page = 0
                 CompProjectCache.Clear()
                 CompFilesCache.Clear()
-                If FrmDownloadMod IsNot Nothing Then FrmDownloadMod.PageLoaderRestart()
+                If FrmDownloadMod IsNot Nothing Then
+                    FrmDownloadMod.Content.Storage = New CompProjectStorage
+                    FrmDownloadMod.Content.Page = 0
+                    FrmDownloadMod.PageLoaderRestart()
+                End If
                 ItemMod.Checked = True
             Case FormMain.PageSubType.DownloadPack
-                PageDownloadPack.Storage = New CompProjectStorage
-                PageDownloadPack.Page = 0
                 CompProjectCache.Clear()
                 CompFilesCache.Clear()
-                If FrmDownloadPack IsNot Nothing Then FrmDownloadPack.PageLoaderRestart()
+                If FrmDownloadPack IsNot Nothing Then
+                    FrmDownloadPack.Content.Storage = New CompProjectStorage
+                    FrmDownloadPack.Content.Page = 0
+                    FrmDownloadPack.PageLoaderRestart()
+                End If
                 ItemPack.Checked = True
-            Case FormMain.PageSubType.DownloadDataPack
-                PageDownloadDataPack.Storage = New CompProjectStorage
-                PageDownloadDataPack.Page = 0
+            Case FormMain.PageSubType.DownloadResourcePack
                 CompProjectCache.Clear()
-                If FrmDownloadDataPack IsNot Nothing Then FrmDownloadDataPack.PageLoaderRestart()
-                ItemDataPack.Checked = True
+                CompFilesCache.Clear()
+                If FrmDownloadResourcePack IsNot Nothing Then
+                    FrmDownloadResourcePack.Content.Storage = New CompProjectStorage
+                    FrmDownloadResourcePack.Content.Page = 0
+                    FrmDownloadResourcePack.PageLoaderRestart()
+                End If
+                ItemResourcePack.Checked = True
             Case FormMain.PageSubType.DownloadShader
-                PageDownloadShader.Storage = New CompProjectStorage
-                PageDownloadShader.Page = 0
                 CompProjectCache.Clear()
-                If FrmDownloadShader IsNot Nothing Then FrmDownloadShader.PageLoaderRestart()
+                CompFilesCache.Clear()
+                If FrmDownloadShader IsNot Nothing Then
+                    FrmDownloadShader.Content.Storage = New CompProjectStorage
+                    FrmDownloadShader.Content.Page = 0
+                    FrmDownloadShader.PageLoaderRestart()
+                End If
                 ItemShader.Checked = True
-            Case FormMain.PageSubType.DownloadResourcePack
-                PageDownloadResourcePack.Storage = New CompProjectStorage
-                PageDownloadResourcePack.Page = 0
+            Case FormMain.PageSubType.DownloadDataPack
                 CompProjectCache.Clear()
-                If FrmDownloadResourcePack IsNot Nothing Then FrmDownloadResourcePack.PageLoaderRestart()
-                ItemResourcePack.Checked = True
+                CompFilesCache.Clear()
+                If FrmDownloadDataPack IsNot Nothing Then
+                    FrmDownloadDataPack.Content.Storage = New CompProjectStorage
+                    FrmDownloadDataPack.Content.Page = 0
+                    FrmDownloadDataPack.PageLoaderRestart()
+                End If
+                ItemDataPack.Checked = True
             Case FormMain.PageSubType.DownloadClient
                 DlClientListLoader.Start(IsForceRestart:=True)
                 ItemClient.Checked = True

+ 0 - 122
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml

@@ -1,122 +0,0 @@
-<local:MyPageRight
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-    mc:Ignorable="d" x:Class="PageDownloadMod"
-    PanScroll="{Binding ElementName=PanBack}">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel Orientation="Vertical" Margin="25">
-            <local:MyCard Title="搜索 Mod" Margin="0,0,0,15" x:Name="PanAlways">
-                <StackPanel Margin="25,40,25,15">
-                    <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
-                        <Grid.ColumnDefinitions>
-                            <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,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.21.5" />
-                            <local:MyComboBoxItem Content="1.21.4" />
-                            <local:MyComboBoxItem Content="1.21.1" />
-                            <local:MyComboBoxItem Content="1.20.6" />
-                            <local:MyComboBoxItem Content="1.20.1" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <local:MyComboBoxItem Content="1.18.2" />
-                            <local:MyComboBoxItem Content="1.17.1" />
-                            <local:MyComboBoxItem Content="1.16.5" />
-                            <local:MyComboBoxItem Content="1.14.4" />
-                            <local:MyComboBoxItem Content="1.12.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <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:MyComboBoxItem Content="NeoForge" Tag="6" />
-                        </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" />
-                            <local:MyComboBoxItem Content="维度" Tag="410/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="矿物与资源" Tag="408/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="天然结构" Tag="409/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="科技" Tag="412/technology" />
-                            <local:MyComboBoxItem Content="管道与物流" Tag="415/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="自动化" Tag="4843/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="能源" Tag="417/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="红石" Tag="4558/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="食物与烹饪" Tag="436/food" />
-                            <local:MyComboBoxItem Content="农业" Tag="416/" Margin="15,0,0,0" />
-                            <local:MyComboBoxItem Content="游戏机制" Tag="/game-mechanics" />
-                            <local:MyComboBoxItem Content="运输" Tag="414/transportation" />
-                            <local:MyComboBoxItem Content="仓储" Tag="420/storage" />
-                            <local:MyComboBoxItem Content="魔法" Tag="419/magic" />
-                            <local:MyComboBoxItem Content="冒险" Tag="422/adventure" />
-                            <local:MyComboBoxItem Content="装饰" Tag="424/decoration" />
-                            <local:MyComboBoxItem Content="生物" Tag="411/mobs" />
-                            <local:MyComboBoxItem Content="装备与工具" Tag="434/equipment" />
-                            <local:MyComboBoxItem Content="性能优化" Tag="/optimization" />
-                            <local:MyComboBoxItem Content="信息显示" Tag="423/" />
-                            <local:MyComboBoxItem Content="服务器" Tag="435/social" />
-                            <local:MyComboBoxItem Content="改良" Tag="5191/utility" />
-                            <local:MyComboBoxItem Content="支持库" Tag="421/library" />
-                        </local:MyComboBox>
-                    </Grid>
-                    <Grid Margin="0,4,0,6" Height="35">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <StackPanel x:Name="PanContent">
-                <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
-                <local:MyCard x:Name="CardProjects" UseAnimation="False">
-                    <StackPanel x:Name="PanProjects" Margin="12" />
-                </local:MyCard>
-                <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
-                    <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
-                        <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
-                            Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
-                        <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
-                            Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
-                        <TextBlock x:Name="LabPage" Text="1" 
-                                   Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
-                        <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
-                            Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
-                    </StackPanel>
-                </local:MyCard>
-            </StackPanel>
-            <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
-                <local:MyLoading Text="正在获取 Mod 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>

+ 0 - 111
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml

@@ -1,111 +0,0 @@
-<local:MyPageRight
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-    mc:Ignorable="d" x:Class="PageDownloadPack"
-    PanScroll="{Binding ElementName=PanBack}">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel Orientation="Vertical" Margin="25">
-            <local:MyCard Title="搜索整合包" Margin="0,0,0,15" x:Name="PanAlways">
-                <StackPanel Margin="25,40,25,15">
-                    <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
-                        <Grid.ColumnDefinitions>
-                            <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,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" 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.21.5" />
-                            <local:MyComboBoxItem Content="1.21.4" />
-                            <local:MyComboBoxItem Content="1.21.1" />
-                            <local:MyComboBoxItem Content="1.20.6" />
-                            <local:MyComboBoxItem Content="1.20.1" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <local:MyComboBoxItem Content="1.18.2" />
-                            <local:MyComboBoxItem Content="1.17.1" />
-                            <local:MyComboBoxItem Content="1.16.5" />
-                            <local:MyComboBoxItem Content="1.14.4" />
-                            <local:MyComboBoxItem Content="1.12.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <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 官方发布" ToolTipService.HorizontalOffset="70" />
-                            <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>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
-                        <local:MyButton x:Name="BtnSearchInstall" Text="安装已有整合包" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="2" 
-                                        ToolTip="在当前选择的 Minecraft 文件夹下安装整合包" ToolTipService.HorizontalOffset="-55" ToolTipService.VerticalOffset="5" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <StackPanel x:Name="PanContent">
-                <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
-                <local:MyCard x:Name="CardProjects" UseAnimation="False">
-                    <StackPanel x:Name="PanProjects" Margin="12" />
-                </local:MyCard>
-                <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
-                    <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
-                        <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
-                            Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
-                        <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
-                            Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
-                        <TextBlock x:Name="LabPage" Text="1" 
-                                   Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
-                        <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
-                            Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
-                    </StackPanel>
-                </local:MyCard>
-            </StackPanel>
-            <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
-                <local:MyLoading Text="正在获取整合包列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>

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

@@ -1,128 +0,0 @@
-Public Class PageDownloadPack
-
-    Public Const PageSize = 40
-
-    '加载器信息
-    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 PageDownloadPack_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
-        If McVersionHighest = -1 Then 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 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
-    End Function
-
-    '结果 UI 化
-    Private Sub Load_OnFinish()
-        Try
-            Log($"[Comp] 开始可视化整合包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
-            '列表项
-            PanProjects.Children.Clear()
-            For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
-                PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, True))
-            Next
-            '页码
-            CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
-                                      Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
-                                      Visibility.Visible, Visibility.Collapsed)
-            LabPage.Text = Page + 1
-            BtnPageFirst.IsEnabled = Page > 1
-            BtnPageFirst.Opacity = If(Page > 1, 1, 0.2)
-            BtnPageLeft.IsEnabled = Page > 0
-            BtnPageLeft.Opacity = If(Page > 0, 1, 0.2)
-            Dim IsRightEnabled As Boolean = '由于 WPF 的未知 bug,读取到的 IsEnabled 可能是错误的值(#3319)
-                Storage.Results.Count > PageSize * (Page + 1) OrElse
-                Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
-            BtnPageRight.IsEnabled = IsRightEnabled
-            BtnPageRight.Opacity = If(IsRightEnabled, 1, 0.2)
-            '错误信息
-            If Storage.ErrorMessage Is Nothing Then
-                HintError.Visibility = Visibility.Collapsed
-            Else
-                HintError.Visibility = Visibility.Visible
-                HintError.Text = Storage.ErrorMessage
-            End If
-            '强制返回顶部
-            PanBack.ScrollToTop()
-        Catch ex As Exception
-            Log(ex, "可视化整合包列表出错", LogLevel.Feedback)
-        End Try
-    End Sub
-
-    '自动重试
-    Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
-        Select Case Loader.State
-            Case LoadState.Failed
-                Dim ErrorMessage As String = ""
-                If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
-                If ErrorMessage.Contains("不是有效的 json 文件") Then
-                    Log("[Download] 下载的整合包列表 json 文件损坏,已自动重试", LogLevel.Debug)
-                    PageLoaderRestart()
-                End If
-        End Select
-    End Sub
-
-    '切换页码
-
-    Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
-        ChangePage(0)
-    End Sub
-    Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
-        ChangePage(Page - 1)
-    End Sub
-    Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
-        ChangePage(Page + 1)
-    End Sub
-    Private Sub ChangePage(NewPage As Integer)
-        CardPages.IsEnabled = False
-        Page = NewPage
-        FrmMain.BackToTop()
-        Log($"[Download] 整合包切换到第 {Page + 1} 页")
-        RunInThread(Sub()
-                        Thread.Sleep(100) '等待向上滚的动画结束
-                        RunInUi(Sub() CardPages.IsEnabled = True)
-                        Loader.Start()
-                    End Sub)
-    End Sub
-
-    Private Sub BtnSearchInstall_Click(sender As Object, e As EventArgs) Handles BtnSearchInstall.Click
-        ModpackInstall()
-    End Sub
-
-#Region "搜索"
-
-    '搜索按钮
-    Private Sub StartNewSearch() Handles BtnSearchRun.Click
-        Page = 0
-        If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
-        Loader.Start()
-    End Sub
-    Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
-        If e.Key = Key.Enter Then StartNewSearch()
-    End Sub
-
-    '重置按钮
-    Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click
-        TextSearchName.Text = ""
-        TextSearchVersion.Text = "全部 (也可自行输入)"
-        TextSearchVersion.SelectedIndex = 0
-        ComboSearchSource.SelectedIndex = 0
-        ComboSearchTag.SelectedIndex = 0
-        Loader.LastFinishedTime = 0 '要求强制重新开始
-    End Sub
-
-#End Region
-
-End Class

+ 0 - 126
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml

@@ -1,126 +0,0 @@
-<local:MyPageRight
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-    mc:Ignorable="d" x:Class="PageDownloadResourcePack"
-    PanScroll="{Binding ElementName=PanBack}">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel Orientation="Vertical" Margin="25">
-            <local:MyCard Title="搜索资源包" Margin="0,0,0,15" x:Name="PanAlways">
-                <StackPanel Margin="25,40,25,15">
-                    <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
-                        <Grid.ColumnDefinitions>
-                            <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,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" 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.20.6" />
-                            <local:MyComboBoxItem Content="1.20.4" />
-                            <local:MyComboBoxItem Content="1.20.1" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <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.12.2" />
-                            <local:MyComboBoxItem Content="1.11.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <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 Content="8x-" Tag="/8x-" />
-                            <local:MyComboBoxItem Content="16x" Tag="393/16x" />
-                            <local:MyComboBoxItem Content="32x" Tag="394/32x" />
-                            <local:MyComboBoxItem Content="48x" Tag="/48x" />
-                            <local:MyComboBoxItem Content="64x" Tag="395/64x" />
-                            <local:MyComboBoxItem Content="128x" Tag="396/128x" />
-                            <local:MyComboBoxItem Content="256x" Tag="397/256x" />
-                            <local:MyComboBoxItem Content="512x+" Tag="398/512x+" />
-                            <local:MyComboBoxItem Content="声音" Tag="/audio" />
-                            <local:MyComboBoxItem Content="方块" Tag="/blocks" />
-                            <local:MyComboBoxItem Content="战斗" Tag="/combat" />
-                            <local:MyComboBoxItem Content="核心着色器" Tag="/core-shaders" />
-                            <local:MyComboBoxItem Content="Cursed" Tag="/cursed" />
-                            <local:MyComboBoxItem Content="装饰" Tag="/decoration" />
-                            <local:MyComboBoxItem Content="实体" Tag="/entities" />
-                            <local:MyComboBoxItem Content="环境" Tag="/environment" />
-                            <local:MyComboBoxItem Content="装备" Tag="/equipment" />
-                            <local:MyComboBoxItem Content="字体" Tag="/fonts" />
-                            <local:MyComboBoxItem Content="GUI" Tag="/gui" />
-                            <local:MyComboBoxItem Content="物品" Tag="/items" />
-                            <local:MyComboBoxItem Content="本地化" Tag="/locale" />
-                            <local:MyComboBoxItem Content="Modded" Tag="4465/modded" />
-                            <local:MyComboBoxItem Content="模型" Tag="/models" />
-                            <local:MyComboBoxItem Content="写实" Tag="400/realistic" />
-                            <local:MyComboBoxItem Content="扁平" Tag="/simplistic" />
-                            <local:MyComboBoxItem Content="主题" Tag="/themed" />
-                            <local:MyComboBoxItem Content="优化" Tag="/tweaks" />
-                            <local:MyComboBoxItem Content="实用" Tag="/utility" />
-                            <local:MyComboBoxItem Content="类原生" Tag="/vanilla-like" />
-                            <local:MyComboBoxItem Content="动态效果" Tag="404/" />
-                            <local:MyComboBoxItem Content="传统" Tag="403/" />
-                            <local:MyComboBoxItem Content="中世纪" Tag="402/" />
-                            <local:MyComboBoxItem Content="蒸汽朋克" Tag="399/" />
-                            <local:MyComboBoxItem Content="现代" Tag="401/" />
-                            <local:MyComboBoxItem Content="字体包" Tag="5244/" />
-                            <local:MyComboBoxItem Content="含有数据包" Tag="5193/" />
-                            <local:MyComboBoxItem Content="其他" Tag="405/" />
-                        </local:MyComboBox>
-                    </Grid>
-                    <Grid Margin="0,4,0,6" Height="35">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <StackPanel x:Name="PanContent">
-                <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
-                <local:MyCard x:Name="CardProjects" UseAnimation="False">
-                    <StackPanel x:Name="PanProjects" Margin="12" />
-                </local:MyCard>
-                <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
-                    <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
-                        <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
-                            Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
-                        <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
-                            Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
-                        <TextBlock x:Name="LabPage" Text="1" 
-                                   Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
-                        <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
-                            Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
-                    </StackPanel>
-                </local:MyCard>
-            </StackPanel>
-            <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
-                <local:MyLoading Text="正在获取资源包列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>

+ 0 - 124
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml.vb

@@ -1,124 +0,0 @@
-Public Class PageDownloadResourcePack
-
-    Public Const PageSize = 40
-
-    '加载器信息
-    Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject ResourcePack", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
-    Public Shared Storage As New CompProjectStorage
-    Public Shared Page As Integer = 0
-    Private Sub PageDownloadResourcePack_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
-        If McVersionHighest = -1 Then 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.ResourcePack, Storage, (Page + 1) * PageSize)
-        If FrmDownloadResourcePack IsNot Nothing Then
-            With Request
-                .SearchText = FrmDownloadResourcePack.TextSearchName.Text
-                .GameVersion = If(FrmDownloadResourcePack.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
-                    If(FrmDownloadResourcePack.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadResourcePack.TextSearchVersion.Text.Contains("w"), FrmDownloadResourcePack.TextSearchVersion.Text, Nothing))
-                .Tag = FrmDownloadResourcePack.ComboSearchTag.SelectedItem.Tag
-                .Source = CType(Val(FrmDownloadResourcePack.ComboSearchSource.SelectedItem.Tag), CompSourceType)
-            End With
-        End If
-        Return Request
-    End Function
-
-    '结果 UI 化
-    Private Sub Load_OnFinish()
-        Try
-            Log($"[Comp] 开始可视化资源包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
-            '列表项
-            PanProjects.Children.Clear()
-            For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
-                PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, False))
-            Next
-            '页码
-            CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
-                                      Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
-                                      Visibility.Visible, Visibility.Collapsed)
-            LabPage.Text = Page + 1
-            BtnPageFirst.IsEnabled = Page > 1
-            BtnPageFirst.Opacity = If(Page > 1, 1, 0.2)
-            BtnPageLeft.IsEnabled = Page > 0
-            BtnPageLeft.Opacity = If(Page > 0, 1, 0.2)
-            Dim IsRightEnabled As Boolean = '由于 WPF 的未知 bug,读取到的 IsEnabled 可能是错误的值(#3319)
-                Storage.Results.Count > PageSize * (Page + 1) OrElse
-                Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
-            BtnPageRight.IsEnabled = IsRightEnabled
-            BtnPageRight.Opacity = If(IsRightEnabled, 1, 0.2)
-            '错误信息
-            If Storage.ErrorMessage Is Nothing Then
-                HintError.Visibility = Visibility.Collapsed
-            Else
-                HintError.Visibility = Visibility.Visible
-                HintError.Text = Storage.ErrorMessage
-            End If
-            '强制返回顶部
-            PanBack.ScrollToTop()
-        Catch ex As Exception
-            Log(ex, "可视化资源包列表出错", LogLevel.Feedback)
-        End Try
-    End Sub
-
-    '自动重试
-    Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
-        Select Case Loader.State
-            Case LoadState.Failed
-                Dim ErrorMessage As String = ""
-                If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
-                If ErrorMessage.Contains("不是有效的 json 文件") Then
-                    Log("[Download] 下载的资源包列表 json 文件损坏,已自动重试", LogLevel.Debug)
-                    PageLoaderRestart()
-                End If
-        End Select
-    End Sub
-
-    '切换页码
-
-    Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
-        ChangePage(0)
-    End Sub
-    Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
-        ChangePage(Page - 1)
-    End Sub
-    Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
-        ChangePage(Page + 1)
-    End Sub
-    Private Sub ChangePage(NewPage As Integer)
-        CardPages.IsEnabled = False
-        Page = NewPage
-        FrmMain.BackToTop()
-        Log($"[Download] 资源包切换到第 {Page + 1} 页")
-        RunInThread(Sub()
-                        Thread.Sleep(100) '等待向上滚的动画结束
-                        RunInUi(Sub() CardPages.IsEnabled = True)
-                        Loader.Start()
-                    End Sub)
-    End Sub
-
-#Region "搜索"
-
-    '搜索按钮
-    Private Sub StartNewSearch() Handles BtnSearchRun.Click
-        Page = 0
-        If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
-        Loader.Start()
-    End Sub
-    Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
-        If e.Key = Key.Enter Then StartNewSearch()
-    End Sub
-
-    '重置按钮
-    Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click
-        TextSearchName.Text = ""
-        TextSearchVersion.Text = "全部 (也可自行输入)"
-        TextSearchVersion.SelectedIndex = 0
-        ComboSearchSource.SelectedIndex = 0
-        ComboSearchTag.SelectedIndex = 0
-        Loader.LastFinishedTime = 0 '要求强制重新开始
-    End Sub
-
-#End Region
-
-End Class

+ 0 - 115
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml

@@ -1,115 +0,0 @@
-<local:MyPageRight
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-    mc:Ignorable="d" x:Class="PageDownloadShader"
-    PanScroll="{Binding ElementName=PanBack}">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel Orientation="Vertical" Margin="25">
-            <local:MyCard Title="搜索光影包" Margin="0,0,0,15" x:Name="PanAlways">
-                <StackPanel Margin="25,40,25,15">
-                    <Grid Margin="0,0,0,11" HorizontalAlignment="Stretch">
-                        <Grid.ColumnDefinitions>
-                            <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,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" 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.20.6" />
-                            <local:MyComboBoxItem Content="1.20.4" />
-                            <local:MyComboBoxItem Content="1.20.1" />
-                            <local:MyComboBoxItem Content="1.19.4" />
-                            <local:MyComboBoxItem Content="1.19.2" />
-                            <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.12.2" />
-                            <local:MyComboBoxItem Content="1.11.2" />
-                            <local:MyComboBoxItem Content="1.10.2" />
-                            <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 Content="卡通" Tag="/cartoon" />
-                            <local:MyComboBoxItem Content="Cursed" Tag="/cursed" />
-                            <local:MyComboBoxItem Content="幻想" Tag="6554/fantasy" />
-                            <local:MyComboBoxItem Content="写实" Tag="6553/realistic" />
-                            <local:MyComboBoxItem Content="半写实" Tag="/semi-realistic" />
-                            <local:MyComboBoxItem Content="原版风" Tag="6555/vanilla-like" />
-
-                            <local:MyComboBoxItem Content="大气环境" Tag="/atmosphere" />
-                            <local:MyComboBoxItem Content="植被" Tag="/bloom" />
-                            <local:MyComboBoxItem Content="光源着色" Tag="/colored-lighting" />
-                            <local:MyComboBoxItem Content="树叶" Tag="/foliage" />
-                            <local:MyComboBoxItem Content="路径追踪" Tag="/path-tracing" />
-                            <local:MyComboBoxItem Content="PBR" Tag="/pbr" />
-                            <local:MyComboBoxItem Content="反射" Tag="/reflections" />
-                            <local:MyComboBoxItem Content="阴影" Tag="/shadows" />
-
-                            <local:MyComboBoxItem Content="土豆画质" Tag="/potato" />
-                            <local:MyComboBoxItem Content="低性能影响" Tag="/low" />
-                            <local:MyComboBoxItem Content="中性能影响" Tag="/medium" />
-                            <local:MyComboBoxItem Content="高性能影响" Tag="/high" />
-                            <local:MyComboBoxItem Content="极致画质" Tag="/screenshot" />
-
-                            <local:MyComboBoxItem Content="Canvas" Tag="/canvas" />
-                            <local:MyComboBoxItem Content="Iris" Tag="/iris" />
-                            <local:MyComboBoxItem Content="OptiFine" Tag="/optifine" />
-                            <local:MyComboBoxItem Content="原版光影" Tag="/vanilla" />
-                        </local:MyComboBox>
-                    </Grid>
-                    <Grid Margin="0,4,0,6" Height="35">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnSearchRun" Text="搜索" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" />
-                        <local:MyButton x:Name="BtnSearchReset" Text="重置条件" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Column="1" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <StackPanel x:Name="PanContent">
-                <local:MyHint x:Name="HintError" Text="无法连接到 Modrinth,所以目前仅显示了来自 CurseForge 的内容,结果可能不全。&#xa;请尝试使用 VPN 或加速器以改善网络。" IsWarn="True" Margin="0,0,0,15" />
-                <local:MyCard x:Name="CardProjects" UseAnimation="False">
-                    <StackPanel x:Name="PanProjects" Margin="12" />
-                </local:MyCard>
-                <local:MyCard Margin="0,15,0,0" x:Name="CardPages" HorizontalAlignment="Center">
-                    <StackPanel Orientation="Horizontal" Height="23" Margin="10,7">
-                        <local:MyIconButton x:Name="BtnPageFirst" IsEnabled="False" Opacity="0.2" Margin="0,0.5,5,0.5"
-                            Logo="M496.213333 329.856L315.306667 510.848l180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z m298.666667 0l-180.949333 180.992 180.992 181.077333a42.666667 42.666667 0 1 1-60.330667 60.330667l-211.2-211.2a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.373333-27.648l211.2-211.2a42.666667 42.666667 0 0 1 60.373334 60.330667z" />
-                        <local:MyIconButton x:Name="BtnPageLeft" IsEnabled="False" Opacity="0.2" Margin="0,0,5,0"
-                            Logo="M650.752 278.357333l-241.322667 241.365334 241.322667 241.365333a42.666667 42.666667 0 1 1-60.330667 60.330667l-271.530666-271.530667a42.453333 42.453333 0 0 1-11.818667-22.613333l-0.597333-5.034667v-5.034667a42.496 42.496 0 0 1 12.416-27.648l271.530666-271.530666a42.666667 42.666667 0 0 1 60.330667 60.330666z" />
-                        <TextBlock x:Name="LabPage" Text="1" 
-                                   Margin="8,0,13,0" FontSize="15" VerticalAlignment="Center" Foreground="{DynamicResource ColorBrush3}" />
-                        <local:MyIconButton x:Name="BtnPageRight" Margin="0,0,30,0"
-                            Logo="M404.309333 278.357333l241.322667 241.365334-241.322667 241.365333a42.666667 42.666667 0 1 0 60.330667 60.330667l271.530667-271.530667a42.453333 42.453333 0 0 0 11.818666-22.613333l0.597334-5.034667v-5.034667a42.496 42.496 0 0 0-12.416-27.648L464.64 218.026667a42.666667 42.666667 0 0 0-60.330667 60.330666z" />
-                    </StackPanel>
-                </local:MyCard>
-            </StackPanel>
-            <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False" Margin="40,50">
-                <local:MyLoading Text="正在获取光影包列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>

+ 0 - 122
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml.vb

@@ -1,122 +0,0 @@
-Public Class PageDownloadShader
-
-    Public Const PageSize = 40
-
-    '加载器信息
-    Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject Shader", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
-    Public Shared Storage As New CompProjectStorage
-    Public Shared Page As Integer = 0
-    Private Sub PageDownloadShader_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
-        If McVersionHighest = -1 Then 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.Shader, Storage, (Page + 1) * PageSize)
-        If FrmDownloadShader IsNot Nothing Then
-            With Request
-                .SearchText = FrmDownloadShader.TextSearchName.Text
-                .GameVersion = If(FrmDownloadShader.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
-                    If(FrmDownloadShader.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadShader.TextSearchVersion.Text.Contains("w"), FrmDownloadShader.TextSearchVersion.Text, Nothing))
-                .Tag = FrmDownloadShader.ComboSearchTag.SelectedItem.Tag
-                .Source = CType(Val(FrmDownloadShader.ComboSearchSource.SelectedItem.Tag), CompSourceType)
-            End With
-        End If
-        Return Request
-    End Function
-
-    '结果 UI 化
-    Private Sub Load_OnFinish()
-        Try
-            Log($"[Comp] 开始可视化光影包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
-            '列表项
-            PanProjects.Children.Clear()
-            For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
-                PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, False))
-            Next
-            '页码
-            CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
-                                      Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
-                                      Visibility.Visible, Visibility.Collapsed)
-            LabPage.Text = Page + 1
-            BtnPageFirst.IsEnabled = Page > 1
-            BtnPageFirst.Opacity = If(BtnPageFirst.IsEnabled, 1, 0.2)
-            BtnPageLeft.IsEnabled = Page > 0
-            BtnPageLeft.Opacity = If(BtnPageLeft.IsEnabled, 1, 0.2)
-            BtnPageRight.IsEnabled = Storage.Results.Count > PageSize * (Page + 1) OrElse
-                                     Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
-            BtnPageRight.Opacity = If(BtnPageRight.IsEnabled, 1, 0.2)
-            '错误信息
-            If Storage.ErrorMessage Is Nothing Then
-                HintError.Visibility = Visibility.Collapsed
-            Else
-                HintError.Visibility = Visibility.Visible
-                HintError.Text = Storage.ErrorMessage
-            End If
-            '强制返回顶部
-            PanBack.ScrollToTop()
-        Catch ex As Exception
-            Log(ex, "可视化光影包列表出错", LogLevel.Feedback)
-        End Try
-    End Sub
-
-    '自动重试
-    Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
-        Select Case Loader.State
-            Case LoadState.Failed
-                Dim ErrorMessage As String = ""
-                If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
-                If ErrorMessage.Contains("不是有效的 JSON 文件") Then
-                    Log("[Download] 下载的光影包列表 JSON 文件损坏,已自动重试", LogLevel.Debug)
-                    PageLoaderRestart()
-                End If
-        End Select
-    End Sub
-
-    '切换页码
-
-    Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
-        ChangePage(0)
-    End Sub
-    Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
-        ChangePage(Page - 1)
-    End Sub
-    Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
-        ChangePage(Page + 1)
-    End Sub
-    Private Sub ChangePage(NewPage As Integer)
-        CardPages.IsEnabled = False
-        Page = NewPage
-        FrmMain.BackToTop()
-        Log($"[Download] 光影包切换到第 {Page + 1} 页")
-        RunInThread(Sub()
-                        Thread.Sleep(100) '等待向上滚的动画结束
-                        RunInUi(Sub() CardPages.IsEnabled = True)
-                        Loader.Start()
-                    End Sub)
-    End Sub
-
-#Region "搜索"
-
-    '搜索按钮
-    Private Sub StartNewSearch() Handles BtnSearchRun.Click
-        Page = 0
-        If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
-        Loader.Start()
-    End Sub
-    Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
-        If e.Key = Key.Enter Then StartNewSearch()
-    End Sub
-
-    '重置按钮
-    Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click
-        TextSearchName.Text = ""
-        TextSearchVersion.Text = "全部 (也可自行输入)"
-        TextSearchVersion.SelectedIndex = 0
-        ComboSearchSource.SelectedIndex = 0
-        ComboSearchTag.SelectedIndex = 0
-        Loader.LastFinishedTime = 0 '要求强制重新开始
-    End Sub
-
-#End Region
-
-End Class

+ 3 - 0
Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb

@@ -125,6 +125,9 @@
                 ElseIf ex.Message.Contains("expired_token") Then
                     Finished(New Exception("$登录用时太长啦,重新试试吧!"))
                     Return
+                ElseIf ex.Message.Contains("service abuse") Then
+                    Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。"))
+                    Return
                 ElseIf ex.Message.Contains("AADSTS70000") Then '可能不能判 “invalid_grant”,见 #269
                     Finished(New RestartException)
                     Return

+ 30 - 33
Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb

@@ -22,10 +22,10 @@
     End Sub
 #End If
 
-#Region "自定义主页"
+#Region "主页"
 
     ''' <summary>
-    ''' 刷新自定义主页。
+    ''' 刷新主页。
     ''' </summary>
     Private Sub Refresh() Handles Me.Loaded
         RunInNewThread(
@@ -35,9 +35,9 @@
                     RefreshReal()
                 End SyncLock
             Catch ex As Exception
-                Log(ex, "加载 PCL 主页自定义信息失败", LogLevel.Msgbox)
+                Log(ex, "加载 PCL 主页自定义信息失败", If(ModeDebug, LogLevel.Msgbox, LogLevel.Hint))
             End Try
-        End Sub, $"刷新自定义主页 #{GetUuid()}")
+        End Sub, $"刷新主页 #{GetUuid()}")
     End Sub
     Private Sub RefreshReal()
         Dim Content As String = ""
@@ -133,8 +133,8 @@ Download:
     End Sub
     Private RefreshLock As New Object
 
-    '联网获取自定义主页文件
-    Private OnlineLoader As New LoaderTask(Of String, Integer)("自定义主页获取", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
+    '联网获取主页文件
+    Private OnlineLoader As New LoaderTask(Of String, Integer)("下载主页", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
     Private Sub OnlineLoaderSub(Task As LoaderTask(Of String, Integer))
         Dim Address As String = Task.Input '#3721 中连续触发两次导致内容变化
         Try
@@ -153,22 +153,22 @@ Download:
             Dim NeedDownload As Boolean = True
             Try
                 Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
-                If Version.Length > 1000 Then Throw New Exception($"获取的自定义主页版本过长({Version.Length} 字符)")
+                If Version.Length > 1000 Then Throw New Exception($"获取的主页版本过长({Version.Length} 字符)")
                 Dim CurrentVersion As String = Setup.Get("CacheSavedPageVersion")
                 If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
-                    Log($"[Page] 当前缓存的自定义主页已为最新,当前版本:{Version},检查源:{VersionAddress}")
+                    Log($"[Page] 当前缓存的主页已为最新,当前版本:{Version},检查源:{VersionAddress}")
                     NeedDownload = False
                 Else
-                    Log($"[Page] 需要下载联网自定义主页,当前版本:{Version},检查源:{VersionAddress}")
+                    Log($"[Page] 需要下载联网主页,当前版本:{Version},检查源:{VersionAddress}")
                 End If
             Catch exx As Exception
-                Log(exx, $"联网获取自定义主页版本失败", LogLevel.Developer)
-                Log($"[Page] 无法检查联网自定义主页版本,将直接下载,检查源:{VersionAddress}")
+                Log(exx, $"联网获取主页版本失败", LogLevel.Developer)
+                Log($"[Page] 无法检查联网主页版本,将直接下载,检查源:{VersionAddress}")
             End Try
             '实际下载
             If NeedDownload Then
                 Dim FileContent As String = NetGetCodeByRequestRetry(Address)
-                Log($"[Page] 已联网下载自定义主页,内容长度:{FileContent.Length},来源:{Address}")
+                Log($"[Page] 已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
                 Setup.Set("CacheSavedPageUrl", Address)
                 Setup.Set("CacheSavedPageVersion", Version)
                 WriteFile(PathTemp & "Cache\Custom.xaml", FileContent)
@@ -176,20 +176,16 @@ Download:
             '要求刷新
             Refresh()
         Catch ex As Exception
-            If Setup.Get("CacheSavedPageVersion") = "" Then
-                Log(ex, $"联网下载自定义主页失败({Address})", LogLevel.Msgbox)
-            Else
-                Log(ex, $"联网下载自定义主页失败({Address})")
-            End If
+            Log(ex, $"下载主页失败({Address})", If(ModeDebug, LogLevel.Msgbox, LogLevel.Hint))
         End Try
     End Sub
 
     ''' <summary>
-    ''' 立即强制刷新自定义主页。
+    ''' 立即强制刷新主页。
     ''' 必须在 UI 线程调用。
     ''' </summary>
     Public Sub ForceRefresh() Implements IRefreshable.Refresh
-        Log("[Page] 要求强制刷新自定义主页")
+        Log("[Page] 要求强制刷新主页")
         ClearCache()
         '实际的刷新
         If FrmMain.PageCurrent.Page = FormMain.PageType.Launch Then
@@ -201,18 +197,18 @@ Download:
     End Sub
 
     ''' <summary>
-    ''' 清空自定义主页缓存信息。
+    ''' 清空主页缓存信息。
     ''' </summary>
     Private Sub ClearCache()
         LoadedContentHash = -1
         OnlineLoader.Input = ""
         Setup.Set("CacheSavedPageUrl", "")
         Setup.Set("CacheSavedPageVersion", "")
-        Log("[Page] 已清空自定义主页缓存")
+        Log("[Page] 已清空主页缓存")
     End Sub
 
     ''' <summary>
-    ''' 从文本内容中加载自定义主页。
+    ''' 从文本内容中加载主页。
     ''' 必须在 UI 线程调用。
     ''' </summary>
     Private Sub LoadContent(Content As String)
@@ -224,27 +220,28 @@ Download:
             '实际加载内容
             PanCustom.Children.Clear()
             If String.IsNullOrWhiteSpace(Content) Then
-                Log($"[Page] 实例化:清空自定义主页 UI,来源为空")
+                Log($"[Page] 实例化:清空主页 UI,来源为空")
                 Return
             End If
             Try
+                '修改时应同时修改 PageOtherHelpDetail.Init
                 Content = HelpArgumentReplace(Content)
                 If Content.Contains("xmlns") Then Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
                 Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
-                Log($"[Page] 实例化:加载自定义主页 UI 开始,最终内容长度:{Content.Count}")
+                Log($"[Page] 实例化:加载主页 UI 开始,最终内容长度:{Content.Count}")
                 PanCustom.Children.Add(GetObjectFromXML(Content))
-            Catch ex As UnauthorizedAccessException
-                Log(ex, "加载失败的自定义主页内容:" & vbCrLf & Content)
-                If MyMsgBox(ex.Message, "加载自定义主页失败", "重试", "取消") = 1 Then
-                    GoTo Refresh '防止 SyncLock 死锁
-                End If
             Catch ex As Exception
-                Log(ex, "加载失败的自定义主页内容:" & vbCrLf & Content)
-                If MyMsgBox($"自定义主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{GetExceptionSummary(ex)}", "加载自定义主页失败", "重试", "取消") = 1 Then
-                    GoTo Refresh '防止 SyncLock 死锁
+                If ModeDebug Then
+                    Log(ex, "加载失败的主页内容:" & vbCrLf & Content)
+                    If MyMsgBox(If(TypeOf ex Is UnauthorizedAccessException, ex.Message, $"主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{GetExceptionSummary(ex)}"),
+                                "加载主页界面失败", "重试", "取消") = 1 Then
+                        GoTo Refresh '防止 SyncLock 死锁
+                    End If
+                Else
+                    Log(ex, "加载主页界面失败", LogLevel.Hint)
                 End If
             End Try
-            Log($"[Page] 实例化:加载自定义主页 UI 完成")
+            Log($"[Page] 实例化:加载主页 UI 完成")
         End SyncLock
         Return
 Refresh:

+ 1 - 1
Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb

@@ -45,7 +45,7 @@ Public Class PageLoginLegacy
         If LoginData.UserName.Trim = "" Then Return "玩家名不能为空!"
         If LoginData.UserName.Contains("""") Then Return "玩家名不能包含英文引号!"
         If McVersionCurrent IsNot Nothing AndAlso
-           ((McVersionCurrent.Version.McCodeMain = 20 AndAlso McVersionCurrent.Version.McCodeSub >= 3) OrElse McVersionCurrent.Version.McCodeMain > 20) AndAlso
+           (McVersionCurrent.Version.IsStandardVersion AndAlso McVersionCurrent.Version.McVersion >= New Version(1, 20, 3)) AndAlso
            LoginData.UserName.Trim.Length > 16 Then
             Return "自 1.20.3 起,玩家名至多只能包含 16 个字符!"
         End If

+ 6 - 1
Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml

@@ -118,11 +118,13 @@
                         <TextBlock Width="120" Margin="0,1.5" Text="Tong" />
                         <TextBlock Width="120" Margin="0,1.5" Text="15158206433" />
                         <TextBlock Width="120" Margin="0,1.5" Text="一抹蓝色和粉色" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="UcFB" />
                         <TextBlock Width="120" Margin="0,1.5" Text="哦" />
                         <TextBlock Width="120" Margin="0,1.5" Text="T2222" />
                         <TextBlock Width="120" Margin="0,1.5" Text="Toudesu" />
                         <TextBlock Width="120" Margin="0,1.5" Text="苏利" />
                         <TextBlock Width="120" Margin="0,1.5" Text="DW." />
+                        <TextBlock Width="120" Margin="0,1.5" Text="抛抛不是泡泡" />
                         <TextBlock Width="120" Margin="0,1.5" Text="无名小卒" />
                         <TextBlock Width="120" Margin="0,1.5" Text="lgl" />
                         <TextBlock Width="120" Margin="0,1.5" Text="mk" />
@@ -187,6 +189,7 @@
                         <TextBlock Width="120" Margin="0,1.5" Text="橘子233" />
                         <TextBlock Width="120" Margin="0,1.5" Text="wyhll" />
                         <TextBlock Width="120" Margin="0,1.5" Text="SiMen_774" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Klcos" />
                         <TextBlock Width="120" Margin="0,1.5" Text="pepsi" />
                         <TextBlock Width="120" Margin="0,1.5" Text="FORGE" />
                         <TextBlock Width="120" Margin="0,1.5" Text="李青团" />
@@ -197,6 +200,7 @@
                         <TextBlock Width="120" Margin="0,1.5" Text="赤诚之心" />
                         <TextBlock Width="120" Margin="0,1.5" Text="藏白" />
                         <TextBlock Width="120" Margin="0,1.5" Text="某零子" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="WJMAS" />
                         <TextBlock Width="120" Margin="0,1.5" Text="骨头" />
                         <TextBlock Width="120" Margin="0,1.5" Text="Starying" />
                         <TextBlock Width="120" Margin="0,1.5" Text="钱小穷" />
@@ -269,6 +273,7 @@
                         <TextBlock Width="120" Margin="0,1.5" Text="梦境之中" />
                         <TextBlock Width="120" Margin="0,1.5" Text="青枝望月" />
                         <TextBlock Width="120" Margin="0,1.5" Text="m1nato__" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="dingwenbo" />
                         <TextBlock Width="120" Margin="0,1.5" Text="法老" />
                         <TextBlock Width="120" Margin="0,1.5" Text="和十奕" />
                         <TextBlock Width="120" Margin="0,1.5" Text="give" />
@@ -281,7 +286,7 @@
                         <TextBlock Width="120" Margin="0,1.5" Text="灵喻" />
                         <TextBlock Width="120" Margin="0,1.5" Text="_暮霭" />
                         <TextBlock Width="120" Margin="0,1.5" Text="TylerDeng" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="咸鱼汤鱼咸" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="我不在线" />
                         <TextBlock Width="120" Margin="0,1.5" Text="Hugh" />
                         <TextBlock Width="120" Margin="0,1.5" Text="Qiyue" />
                         <TextBlock Width="120" Margin="0,1.5" Text="CHAKKS" />

+ 9 - 6
Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml.vb

@@ -14,17 +14,20 @@
     ''' 根据特定帮助项初始化页面 UI,返回是否成功加载。
     ''' </summary>
     Public Function Init(Entry As HelpEntry) As Boolean
-        Dim FileContent = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & If(Entry.XamlContent, "") & "</StackPanel>"
+        Dim Content = If(Entry.XamlContent, "")
+        If Content = "" Then Throw New Exception("帮助 xaml 文件为空")
         Try
-            If String.IsNullOrEmpty(Entry.XamlContent) Then Throw New Exception("帮助 xaml 文件为空")
+            '修改时应同时修改 PageLaunchRight.LoadContent
+            Content = HelpArgumentReplace(Content)
+            If Content.Contains("xmlns") Then Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
+            Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
             Me.Entry = Entry
             PanCustom.Children.Clear()
-            FileContent = HelpArgumentReplace(FileContent)
-            PanCustom.Children.Add(GetObjectFromXML(FileContent))
+            PanCustom.Children.Add(GetObjectFromXML(Content))
             Return True
         Catch ex As Exception
-            Log("[System] 自定义信息内容:" & vbCrLf & FileContent)
-            Log(ex, "加载帮助 xaml 文件失败", LogLevel.Msgbox)
+            Log("[System] 自定义信息内容:" & vbCrLf & Content)
+            Log(ex, "加载帮助 XAML 文件失败", LogLevel.Msgbox)
             Return False
         End Try
     End Function

+ 2 - 1
Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb

@@ -118,7 +118,8 @@
         {"ToolHelpChinese", New SetupEntry(True, Source:=SetupSource.Registry)},
         {"ToolDownloadThread", New SetupEntry(63, Source:=SetupSource.Registry)},
         {"ToolDownloadSpeed", New SetupEntry(42, Source:=SetupSource.Registry)},
-        {"ToolDownloadVersion", New SetupEntry(0, Source:=SetupSource.Registry)},
+        {"ToolDownloadSource", New SetupEntry(1, Source:=SetupSource.Registry)},
+        {"ToolDownloadVersion", New SetupEntry(1, Source:=SetupSource.Registry)},
         {"ToolDownloadTranslate", New SetupEntry(0, Source:=SetupSource.Registry)},
         {"ToolDownloadTranslateV2", New SetupEntry(1, Source:=SetupSource.Registry)},
         {"ToolDownloadIgnoreQuilt", New SetupEntry(True, Source:=SetupSource.Registry)},

+ 1 - 1
Plain Craft Launcher 2/Pages/PageSetup/PageSetupLeft.xaml.vb

@@ -150,7 +150,7 @@
                     ItemLaunch.Checked = True
                 End If
             Case FormMain.PageSubType.SetupUI
-                If MyMsgBox("是否要初始化个性化页的所有设置?该操作不可撤销。" & vbCrLf & "(背景图片与音乐、自定义主页等外部文件不会被删除)", "初始化确认",, "取消", IsWarn:=True) = 1 Then
+                If MyMsgBox("是否要初始化个性化页的所有设置?该操作不可撤销。" & vbCrLf & "(背景图片与音乐、主页等外部文件不会被删除)", "初始化确认",, "取消", IsWarn:=True) = 1 Then
                     If IsNothing(FrmSetupUI) Then FrmSetupUI = New PageSetupUI
                     FrmSetupUI.Reset()
                     ItemUI.Checked = True

+ 29 - 21
Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml

@@ -15,6 +15,8 @@
                             <ColumnDefinition />
                         </Grid.ColumnDefinitions>
                         <Grid.RowDefinitions>
+                            <RowDefinition Height="28" />
+                            <RowDefinition Height="7" />
                             <RowDefinition Height="28" />
                             <RowDefinition Height="7" />
                             <RowDefinition Height="27" />
@@ -22,28 +24,34 @@
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                         </Grid.RowDefinitions>
-                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表源" Margin="0,0,25,0" />
-                        <local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1">
-                            <local:MyComboBoxItem Content="尽量使用镜像源(加载快,但版本可能不是最新的)" IsSelected="True" />
-                            <local:MyComboBoxItem Content="仅在官方源加载缓慢时改用镜像源" />
-                            <local:MyComboBoxItem Content="尽量使用官方源(保证版本最新,但加载缓慢)" />
+                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="文件下载源" Margin="0,0,25,0" />
+                        <local:MyComboBox x:Name="ComboDownloadSource" Grid.ColumnSpan="2" Tag="ToolDownloadSource" Grid.Column="1">
+                            <local:MyComboBoxItem Content="尽量使用镜像源" />
+                            <local:MyComboBoxItem Content="优先使用官方源,在加载缓慢时换用镜像源" IsSelected="True" />
+                            <local:MyComboBoxItem Content="尽量使用官方源" />
                         </local:MyComboBox>
-                        <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="最大线程数" Margin="0,0,25,0" />
-                        <local:MySlider x:Name="SliderDownloadThread" Grid.Row="2" Tag="ToolDownloadThread" MaxValue="255" Value="63" Grid.Column="1"
+                        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表源" Margin="0,0,25,0" Grid.Row="2" />
+                        <local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1" Grid.Row="2">
+                            <local:MyComboBoxItem Content="尽量使用镜像源(可能缺少刚刚更新的版本)" />
+                            <local:MyComboBoxItem Content="优先使用官方源,在加载缓慢时换用镜像源" IsSelected="True" />
+                            <local:MyComboBoxItem Content="尽量使用官方源" />
+                        </local:MyComboBox>
+                        <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="最大线程数" Margin="0,0,25,0" />
+                        <local:MySlider x:Name="SliderDownloadThread" Grid.Row="4" Tag="ToolDownloadThread" MaxValue="255" Value="63" Grid.Column="1"
                                         ToolTip="线程数越多,限速的文件下载越快,但过高的线程数会造成下载时非常严重的卡顿。&#xa;一般而言,64 线程已可以保证足够的下载速度。" />
-                        <TextBlock VerticalAlignment="Center" Grid.Row="3" HorizontalAlignment="Left" Text="速度限制" Margin="0,0,25,0" />
-                        <local:MySlider x:Name="SliderDownloadSpeed" Grid.Row="3" Tag="ToolDownloadSpeed" MaxValue="42" Value="42" Grid.Column="1"
+                        <TextBlock VerticalAlignment="Center" Grid.Row="5" HorizontalAlignment="Left" Text="速度限制" Margin="0,0,25,0" />
+                        <local:MySlider x:Name="SliderDownloadSpeed" Grid.Row="5" Tag="ToolDownloadSpeed" MaxValue="42" Value="42" Grid.Column="1"
                                         ToolTip="设置下载的速度上限,以避免在下载时导致其他需要联网的程序卡死" />
-                        <TextBlock VerticalAlignment="Top" Grid.Row="4" HorizontalAlignment="Left" Text="目标文件夹" Margin="0,5,25,5" />
-                        <TextBlock Margin="0,5" HorizontalAlignment="Left" Text="请在 启动 → 版本选择 → 文件夹列表 中更改下载目标文件夹。&#xa;在某个文件夹或游戏版本上右键,即可选择打开对应文件夹。" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" Opacity="0.5" />
-                        <local:MyCheckBox Margin="0,2,0,4" Text="在正版登录时验证 SSL 证书" Grid.Row="5" Height="22" Grid.ColumnSpan="2"
+                        <TextBlock VerticalAlignment="Top" Grid.Row="6" HorizontalAlignment="Left" Text="目标文件夹" Margin="0,5,25,5" />
+                        <TextBlock Margin="0,5" HorizontalAlignment="Left" Text="请在 启动 → 版本选择 → 文件夹列表 中更改下载目标文件夹。&#xa;在某个文件夹或游戏版本上右键,即可选择打开对应文件夹。" Grid.Row="6" Grid.Column="1" VerticalAlignment="Center" Opacity="0.5" />
+                        <local:MyCheckBox Margin="0,2,0,4" Text="在正版登录时验证 SSL 证书" Grid.Row="7" Height="22" Grid.ColumnSpan="2"
                                           x:Name="CheckDownloadCert" Tag="ToolDownloadCert" 
                                           ToolTip="开启验证可以提高安全性,但可能会导致正版登录失败(见 #3018)。&#xa;若登录因为 “未能为 SSL/TLS 安全通道建立信任关系” 失败,请考虑关闭此选项。" />
                     </Grid>
                 </StackPanel>
             </local:MyCard>
-            <local:MyCard Margin="0,0,0,15" Title="Mod 与整合包">
-                <StackPanel Margin="25,37,25,15">
+            <local:MyCard Margin="0,0,0,15" Title="社区资源">
+                <StackPanel Margin="25,37,25,12">
                     <Grid>
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="Auto" SharedSizeGroup="Name" />
@@ -64,12 +72,12 @@
                         </local:MyComboBox>-->
                         <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="文件名格式" Margin="0,0,25,0" />
                         <local:MyComboBox Grid.Row="0" x:Name="ComboDownloadTranslateV2" Grid.ColumnSpan="2" Tag="ToolDownloadTranslateV2" Grid.Column="1" 
-                                          ToolTip="下载 Mod 时,Mod 的默认文件名格式">
-                            <local:MyComboBoxItem Content="【遗物】relics-0.1.14" />
-                            <local:MyComboBoxItem Content="[遗物] relics-0.1.14" IsSelected="True" />
-                            <local:MyComboBoxItem Content="遗物-relics-0.1.14" />
-                            <local:MyComboBoxItem Content="relics-0.1.14-遗物" />
-                            <local:MyComboBoxItem Content="relics-0.1.14" />
+                                          ToolTip="下载社区资源时,其默认文件名的格式">
+                            <local:MyComboBoxItem Content="【机械动力】create-1.21.1-6.0.4" />
+                            <local:MyComboBoxItem Content="[机械动力] create-1.21.1-6.0.4" IsSelected="True" />
+                            <local:MyComboBoxItem Content="机械动力-create-1.21.1-6.0.4" />
+                            <local:MyComboBoxItem Content="create-1.21.1-6.0.4-机械动力" />
+                            <local:MyComboBoxItem Content="create-1.21.1-6.0.4" />
                         </local:MyComboBox>
                         <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="Mod 管理样式" Margin="0,0,25,0" />
                         <local:MyComboBox Grid.Row="2" x:Name="ComboModLocalNameStyle" Grid.ColumnSpan="2" Tag="ToolModLocalNameStyle" Grid.Column="1" 
@@ -77,7 +85,7 @@
                             <local:MyComboBoxItem Content="标题显示译名,详情显示文件名" IsSelected="True" />
                             <local:MyComboBoxItem Content="标题显示文件名,详情显示译名" />
                         </local:MyComboBox>
-                        <local:MyCheckBox Margin="0,2,0,4" Text="显示 Mod 加载器时忽略 Quilt" Grid.Row="4" Height="22" Grid.ColumnSpan="2"
+                        <local:MyCheckBox Margin="0,2,0,4" Text="在显示 Mod 加载器时忽略 Quilt" Grid.Row="4" Height="22" Grid.ColumnSpan="2"
                                           x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt" />
                     </Grid>
                 </StackPanel>

+ 3 - 1
Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb

@@ -29,6 +29,7 @@
         '下载
         SliderDownloadThread.Value = Setup.Get("ToolDownloadThread")
         SliderDownloadSpeed.Value = Setup.Get("ToolDownloadSpeed")
+        ComboDownloadSource.SelectedIndex = Setup.Get("ToolDownloadSource")
         ComboDownloadVersion.SelectedIndex = Setup.Get("ToolDownloadVersion")
         CheckDownloadCert.Checked = Setup.Get("ToolDownloadCert")
 
@@ -63,6 +64,7 @@
         Try
             Setup.Reset("ToolDownloadThread")
             Setup.Reset("ToolDownloadSpeed")
+            Setup.Reset("ToolDownloadSource")
             Setup.Reset("ToolDownloadVersion")
             Setup.Reset("ToolDownloadTranslateV2")
             Setup.Reset("ToolDownloadIgnoreQuilt")
@@ -96,7 +98,7 @@
     Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change
         If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Value)
     End Sub
-    Private Shared Sub ComboChange(sender As MyComboBox, e As Object) Handles ComboDownloadVersion.SelectionChanged, ComboModLocalNameStyle.SelectionChanged, ComboDownloadTranslateV2.SelectionChanged, ComboSystemUpdate.SelectionChanged, ComboSystemActivity.SelectionChanged
+    Private Shared Sub ComboChange(sender As MyComboBox, e As Object) Handles ComboDownloadVersion.SelectionChanged, ComboModLocalNameStyle.SelectionChanged, ComboDownloadTranslateV2.SelectionChanged, ComboSystemUpdate.SelectionChanged, ComboSystemActivity.SelectionChanged, ComboDownloadSource.SelectionChanged
         If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.SelectedIndex)
     End Sub
     Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextSystemCache.ValidatedTextChanged

+ 1 - 1
Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb

@@ -422,7 +422,7 @@ Refresh:
                  "3. 点击 刷新主页 按钮,查看主页现在长啥样了。" & vbCrLf &
                  vbCrLf &
                  "你可以在生成教学文件后直接刷新主页,对照着进行修改,更有助于理解。" & vbCrLf &
-                 "直接将自定义主页文件拖进 PCL 窗口也可以快捷加载。", "主页自定义教程")
+                 "直接将主页文件拖进 PCL 窗口也可以快捷加载。", "主页自定义教程")
     End Sub
 
     '主题

+ 0 - 0
Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml → Plain Craft Launcher 2/Pages/PageVersion/MyLocalModItem.xaml


+ 0 - 0
Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb → Plain Craft Launcher 2/Pages/PageVersion/MyLocalModItem.xaml.vb


+ 5 - 4
Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb

@@ -380,7 +380,7 @@ Install:
     ''' 下载 Mod。
     ''' </summary>
     Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click
-        PageDownloadMod.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器
+        PageComp.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器
         FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)
     End Sub
 
@@ -432,7 +432,7 @@ Install:
 #Region "筛选"
 
     Private _Filter As FilterType = FilterType.All
-    Private Property Filter As FilterType
+    Public Property Filter As FilterType
         Get
             Return _Filter
         End Get
@@ -454,7 +454,7 @@ Install:
             RefreshUI()
         End Set
     End Property
-    Private Enum FilterType As Integer
+    Public Enum FilterType As Integer
         All = 0
         Enabled = 1
         Disabled = 2
@@ -808,7 +808,8 @@ Install:
                     .Additional = {ModEntry.Comp, New List(Of String), PageVersionLeft.Version.Version.McName,
                         If(PageVersionLeft.Version.Version.HasForge, CompModLoaderType.Forge,
                         If(PageVersionLeft.Version.Version.HasNeoForge, CompModLoaderType.NeoForge,
-                        If(PageVersionLeft.Version.Version.HasFabric, CompModLoaderType.Fabric, CompModLoaderType.Any)))}})
+                        If(PageVersionLeft.Version.Version.HasFabric, CompModLoaderType.Fabric, CompModLoaderType.Any))),
+                        CompType.Mod}})
             Else
                 '获取信息
                 Dim ContentLines As New List(Of String)

+ 41 - 29
Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj

@@ -195,11 +195,14 @@
     <Compile Include="Controls\MyIconTextButton.xaml.vb">
       <DependentUpon>MyIconTextButton.xaml</DependentUpon>
     </Compile>
+
+
+
     <Compile Include="Modules\Minecraft\ModComp.vb" />
     <Compile Include="Modules\Minecraft\ModJava.vb" />
     <Compile Include="Modules\Minecraft\ModMod.vb" />
     <Compile Include="Modules\Minecraft\ModModpack.vb" />
-    <Compile Include="Modules\Minecraft\MyLocalModItem.xaml.vb">
+    <Compile Include="Pages\PageVersion\MyLocalModItem.xaml.vb">
       <DependentUpon>MyLocalModItem.xaml</DependentUpon>
     </Compile>
     <Compile Include="Modules\ModEvent.vb" />
@@ -235,17 +238,26 @@
     <Compile Include="Modules\Reference\CookieWebClient.vb">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="Pages\PageDownload\ModDownloadLib.vb" />
-    <Compile Include="Modules\Minecraft\MyCompItem.xaml.vb">
-      <DependentUpon>MyCompItem.xaml</DependentUpon>
+    <Compile Include="Pages\PageDownload\Comp\PageComp.xaml.vb">
+      <DependentUpon>PageComp.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadDataPack.xaml.vb">
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadDataPack.xaml.vb">
       <DependentUpon>PageDownloadDataPack.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadResourcePack.xaml.vb">
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadPack.xaml.vb">
+      <DependentUpon>PageDownloadPack.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadShader.xaml.vb">
+      <DependentUpon>PageDownloadShader.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadResourcePack.xaml.vb">
       <DependentUpon>PageDownloadResourcePack.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadMod.xaml.vb">
+    <Compile Include="Pages\PageDownload\ModDownloadLib.vb" />
+    <Compile Include="Pages\PageDownload\Comp\MyCompItem.xaml.vb">
+      <DependentUpon>MyCompItem.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadMod.xaml.vb">
       <DependentUpon>PageDownloadMod.xaml</DependentUpon>
     </Compile>
     <Compile Include="Pages\PageDownload\PageDownloadFabric.xaml.vb">
@@ -254,18 +266,12 @@
     <Compile Include="Pages\PageDownload\PageDownloadInstall.xaml.vb">
       <DependentUpon>PageDownloadInstall.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadCompDetail.xaml.vb">
+    <Compile Include="Pages\PageDownload\Comp\PageDownloadCompDetail.xaml.vb">
       <DependentUpon>PageDownloadCompDetail.xaml</DependentUpon>
     </Compile>
     <Compile Include="Pages\PageDownload\PageDownloadNeoForge.xaml.vb">
       <DependentUpon>PageDownloadNeoForge.xaml</DependentUpon>
     </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadPack.xaml.vb">
-      <DependentUpon>PageDownloadPack.xaml</DependentUpon>
-    </Compile>
-    <Compile Include="Pages\PageDownload\PageDownloadShader.xaml.vb">
-      <DependentUpon>PageDownloadShader.xaml</DependentUpon>
-    </Compile>
     <Compile Include="Pages\PageLaunch\MyMsgLogin.xaml.vb">
       <DependentUpon>MyMsgLogin.xaml</DependentUpon>
     </Compile>
@@ -495,23 +501,36 @@
       <DependentUpon>FormMain.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
-    <Page Include="Modules\Minecraft\MyLocalModItem.xaml">
+
+    <Page Include="Pages\PageVersion\MyLocalModItem.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Pages\PageDownload\Comp\MyCompItem.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
-    <Page Include="Modules\Minecraft\MyCompItem.xaml">
+    <Page Include="Pages\PageDownload\Comp\PageComp.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="Pages\PageDownload\Comp\PageDownloadDataPack.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
-    <Page Include="Pages\PageDownload\PageDownloadDataPack.xaml">
+    <Page Include="Pages\PageDownload\Comp\PageDownloadPack.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
-    <Page Include="Pages\PageDownload\PageDownloadResourcePack.xaml">
+    <Page Include="Pages\PageDownload\Comp\PageDownloadShader.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
-    <Page Include="Pages\PageDownload\PageDownloadMod.xaml">
+    <Page Include="Pages\PageDownload\Comp\PageDownloadResourcePack.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="Pages\PageDownload\Comp\PageDownloadMod.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
@@ -523,7 +542,7 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
-    <Page Include="Pages\PageDownload\PageDownloadCompDetail.xaml">
+    <Page Include="Pages\PageDownload\Comp\PageDownloadCompDetail.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
@@ -531,14 +550,6 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
-    <Page Include="Pages\PageDownload\PageDownloadPack.xaml">
-      <SubType>Designer</SubType>
-      <Generator>MSBuild:Compile</Generator>
-    </Page>
-    <Page Include="Pages\PageDownload\PageDownloadShader.xaml">
-      <Generator>MSBuild:Compile</Generator>
-      <SubType>Designer</SubType>
-    </Page>
     <Page Include="Pages\PageLaunch\MyMsgLogin.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
@@ -946,6 +957,7 @@
     <Resource Include="Images\Heads\MCBBS.png" />
   </ItemGroup>
   <ItemGroup>
+
   </ItemGroup>
   <ItemGroup>
     <Resource Include="Images\Blocks\NeoForge.png" />
@@ -958,4 +970,4 @@
     <None Include="Resources\libwebp64.dll" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
-</Project>
+</Project>

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

@@ -299,11 +299,11 @@
     </StackPanel>
 </local:MyCard>
 
-<local:MyCard Title="进阶:联网自定义主页" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
+<local:MyCard Title="进阶:联网主页" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
     <StackPanel Margin="25,40,23,15">
         <local:MyHint Margin="0,0,0,10" Text="以下特性于 PCL 2.6.5 (299) 加入,不适用于更早版本的 PCL。" IsWarn="False" />
         <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
-                    Text="如果你在尝试制作联网更新的自定义主页,可以查看下方的 GitHub 讨论页。&#xa;你可以根据其中的介绍为主页添加版本号检查以节省流量,也可以通过检查 Referer 和 User Agent 来确定对方的 PCL 版本。" />
+                    Text="如果你在尝试制作联网更新的主页,可以查看下方的 GitHub 讨论页。&#xa;你可以根据其中的介绍为主页添加版本号检查以节省流量,也可以通过检查 Referer 和 User Agent 来确定对方的 PCL 版本。" />
         <local:MyButton Margin="0,0,0,8" Height="35" HorizontalAlignment="Center" Padding="20,0,20,0"
                     Text="打开 GitHub 讨论页" EventType="打开网页" EventData="https://github.com/Hex-Dragon/PCL2/discussions/2528" />
     </StackPanel>