龙腾猫跃 %!s(int64=2) %!d(string=hai) anos
pai
achega
fb4386952d
Modificáronse 43 ficheiros con 549 adicións e 645 borrados
  1. 12 0
      Plain Craft Launcher 2/Controls/MyPageRight.vb
  2. 22 67
      Plain Craft Launcher 2/FormMain.xaml.vb
  3. 5 5
      Plain Craft Launcher 2/Modules/Base/ModBase.vb
  4. 1 0
      Plain Craft Launcher 2/Modules/Base/ModNet.vb
  5. 50 43
      Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
  6. 4 4
      Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
  7. 17 56
      Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
  8. 40 43
      Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
  9. 2 1
      Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
  10. 27 20
      Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb
  11. 27 20
      Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb
  12. 3 3
      Plain Craft Launcher 2/My Project/AssemblyInfo.vb
  13. 34 35
      Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
  14. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml
  15. 4 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml.vb
  16. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml
  17. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadFabric.xaml
  18. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadForge.xaml
  19. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml
  20. 2 2
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
  21. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLiteLoader.xaml
  22. 6 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml
  23. 5 1
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb
  24. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadOptiFine.xaml
  25. 5 5
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml
  26. 90 86
      Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb
  27. 5 6
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml
  28. 1 0
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
  29. 1 1
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml
  30. 0 5
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb
  31. 5 5
      Plain Craft Launcher 2/Pages/PageLink/PageLinkFeedback.xaml
  32. 6 6
      Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml
  33. 5 5
      Plain Craft Launcher 2/Pages/PageOther/PageOtherHelp.xaml
  34. 6 6
      Plain Craft Launcher 2/Pages/PageOther/PageOtherHelpDetail.xaml
  35. 4 4
      Plain Craft Launcher 2/Pages/PageSelectRight.xaml
  36. 6 6
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
  37. 5 5
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml
  38. 8 8
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
  39. 5 5
      Plain Craft Launcher 2/Pages/PageSpeedRight.xaml
  40. 8 11
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml
  41. 81 124
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
  42. 6 6
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml
  43. 6 6
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml

+ 12 - 0
Plain Craft Launcher 2/Controls/MyPageRight.vb

@@ -2,6 +2,18 @@
     Inherits AdornerDecorator
     Public PageUuid As Integer = GetUuid()
 
+    '“返回顶部” 按钮检测的滚动区域
+    Public Property PanScroll As MyScrollViewer
+        Get
+            Return GetValue(PanScrollProperty)
+        End Get
+        Set(value As MyScrollViewer)
+            SetValue(PanScrollProperty, value)
+        End Set
+    End Property
+    Private Shared ReadOnly PanScrollProperty =
+        DependencyProperty.Register("PanScroll", GetType(MyScrollViewer), GetType(MyPageRight), New PropertyMetadata(Nothing))
+
     '当前状态
     Public Enum PageStates
         Empty '默认状态,页面全空

+ 22 - 67
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -1,5 +1,4 @@
 Imports System.ComponentModel
-Imports System.Runtime.InteropServices
 
 Public Class FormMain
 
@@ -11,6 +10,11 @@ Public Class FormMain
         Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
         '统计更新日志条目
 #If BETA Then
+        If LastVersion < 313 Then 'Release 2.6.13
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
+            FeatureCount += 6
+            BugCount += 10
+        End If
         If LastVersion < 311 Then 'Release 2.6.12
             FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面将显示 Mod 的中文名、图标、标签等信息"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "从 Mod 管理页面查看 Mod 信息时会跳转到其下载详情页面"))
@@ -119,30 +123,17 @@ Public Class FormMain
             FeatureCount += 4
             BugCount += 4
         End If
-        If LastVersion < 255 Then 'Release 2.2.14
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复无法启动 Minecraft 1.19-Pre1 的 Bug"))
-            FeatureCount += 5
-            BugCount += 3
-        End If
-        If LastVersion < 253 Then 'Release 2.2.13
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持在安装菜单中直接安装 OptiFabric"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持调整 Mod 文件名中中文译名的位置"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "崩溃分析优化,支持分析更多 Forge 相关的崩溃"))
-            FeatureCount += 13
-            BugCount += 27
-        End If
-        If LastVersion < 250 Then 'Release 2.2.11
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持将 Mod 文件拖入窗口进行安装"))
-            If LastVersion = 246 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复无法使用中文搜索 Mod 的 Bug"))
-            FeatureCount += 8
-            BugCount += 33
-        End If
 #Else
         '5:          FEAT+
         '4:     IMP+ FEAT*
         '3:BUG+ IMP* FEAT-
         '2:BUG* IMP-
         '1:BUG-
+        If LastVersion < 312 Then 'Snapshot 2.6.13
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
+            FeatureCount += 6
+            BugCount += 10
+        End If
         If LastVersion < 310 Then 'Snapshot 2.6.12
             If LastVersion = 309 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod 管理页面添加启用、禁用单个 Mod 的快捷按钮"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装 Forge 1.18.3+ 的 Bug"))
@@ -322,28 +313,6 @@ Public Class FormMain
             FeatureCount += 4
             BugCount += 4
         End If
-        If LastVersion < 256 Then 'Snapshot 2.2.14
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复无法启动 Minecraft 1.19-Pre1 的 Bug"))
-            FeatureCount += 5
-            BugCount += 3
-        End If
-        If LastVersion < 254 Then 'Snapshot 2.2.13
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持调整 Mod 文件名中中文译名的位置"))
-            FeatureCount += 3
-            BugCount += 17
-        End If
-        If LastVersion < 252 Then 'Snapshot 2.2.12
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持在安装菜单中直接安装 OptiFabric"))
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "崩溃分析优化,支持分析更多 Forge 相关的崩溃"))
-            If LastVersion = 251 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复同时安装 Forge 和 OptiFine 时 OptiFine 无效的 Bug"))
-            FeatureCount += 10
-            BugCount += 10
-        End If
-        If LastVersion < 251 Then 'Snapshot 2.2.11
-            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持将 Mod 文件拖入窗口进行安装"))
-            FeatureCount += 2
-            BugCount += 16
-        End If
 #End If
         '整理更新日志文本
         Dim ContentList As New List(Of String)
@@ -402,6 +371,13 @@ Public Class FormMain
         '加载 UI
         InitializeComponent()
         Opacity = 0
+        ''开启管理员权限下的文件拖拽,但下列代码也没用(#2531)
+        'If IsAdmin() Then
+        '    Log("[Start] PCL 正以管理员权限运行")
+        '    ChangeWindowMessageFilter(&H233, 1)
+        '    ChangeWindowMessageFilter(&H4A, 1)
+        '    ChangeWindowMessageFilter(&H49, 1)
+        'End If
         '切换到首页
         If Not IsNothing(FrmLaunchLeft.Parent) Then FrmLaunchLeft.SetValue(ContentPresenter.ContentProperty, Nothing)
         If Not IsNothing(FrmLaunchRight.Parent) Then FrmLaunchRight.SetValue(ContentPresenter.ContentProperty, Nothing)
@@ -510,25 +486,11 @@ Public Class FormMain
                            Catch ex As Exception
                                Log(ex, "清理自动更新文件失败")
                            End Try
-                           '开启管理员权限下的文件拖拽,但下列代码也没用(#2531)
-                           If IsAdmin() Then
-                               Log("[System] PCL 正以管理员权限运行")
-                               Dim changeInfo As New ChangeFilter
-                               changeInfo.cbSize = Marshal.SizeOf(changeInfo)
-                               ChangeWindowMessageFilterEx(Handle, &H233, 1, changeInfo)
-                               ChangeWindowMessageFilterEx(Handle, &H4A, 1, changeInfo)
-                               ChangeWindowMessageFilterEx(Handle, &H49, 1, changeInfo)
-                           End If
                        End Sub, "Start Loader", ThreadPriority.Lowest)
 
         Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
     End Sub
-    <StructLayout(LayoutKind.Sequential)>
-    Public Structure ChangeFilter
-        Public cbSize As UInteger
-        Public ExtStatus As UInteger
-    End Structure
-    Private Declare Function ChangeWindowMessageFilterEx Lib "user32.dll" (hwnd As IntPtr, message As UInteger, action As UInteger, changeInfo As ChangeFilter) As Boolean
+    'Private Declare Function ChangeWindowMessageFilter Lib "user32.dll" (message As Integer, action As Integer) As Boolean
     '根据打开次数触发的事件
     Private Sub RunCountSub()
         Setup.Set("SystemCount", Setup.Get("SystemCount") + 1)
@@ -1618,17 +1580,10 @@ Install:
         Return RealScroll IsNot Nothing AndAlso RealScroll.Visibility = Visibility.Visible AndAlso RealScroll.VerticalOffset > Height + If(BtnExtraBack.Show, 0, 700)
     End Function
     Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
-        If PanMainRight.Child Is Nothing Then Return Nothing
-        Dim RightChild = CType(PanMainRight.Child, AdornerDecorator).Child
-        If RightChild Is Nothing Then
-            Return Nothing
-        ElseIf TypeOf RightChild Is MyScrollViewer Then
-            Return RightChild
-        ElseIf TypeOf RightChild Is Grid AndAlso TypeOf CType(RightChild, Grid).Children(0) Is MyScrollViewer Then
-            Return CType(RightChild, Grid).Children(0)
-        Else
-            Return Nothing
-        End If
+        If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
+        Dim Scroll = CType(PanMainRight.Child, MyPageRight).PanScroll
+        If Scroll Is Nothing OrElse Scroll.Visibility <> Visibility.Visible Then Return Nothing
+        Return Scroll
     End Function
 
 #End Region

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

@@ -11,12 +11,12 @@ Public Module ModBase
 #Region "声明"
 
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.6.12" '不含分支前缀的显示用版本名
-    Public Const VersionStandardCode As String = "2.6.12." & VersionBranchCode '标准格式的四段式版本号
+    Public Const VersionBaseName As String = "2.6.13" '不含分支前缀的显示用版本名
+    Public Const VersionStandardCode As String = "2.6.13." & VersionBranchCode '标准格式的四段式版本号
 #If BETA Then
-    Public Const VersionCode As Integer = 308 'Release
+    Public Const VersionCode As Integer = 313 'Release
 #Else
-    Public Const VersionCode As Integer = 310 'Snapshot
+    Public Const VersionCode As Integer = 312 'Snapshot
 #End If
     '自动生成的版本信息
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -1819,7 +1819,7 @@ NextElement:
     ''' 获取格式类似于“11:08:52.037”的当前时间的字符串。
     ''' </summary>
     Public Function GetTimeNow() As String
-        Return Date.Now.ToString("HH:mm:ss.fff")
+        Return Date.Now.ToString("HH':'mm':'ss'.'fff")
     End Function
     ''' <summary>
     ''' 获取系统运行时间(毫秒),保证为正 Long 且大于 1,但可能突变减小。

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

@@ -273,6 +273,7 @@ RequestFinished:
                 Client.Headers(HttpRequestHeader.Referer) = "http://" & VersionCode & ".pcl2.server/"
                 Client.DownloadFile(Url, LocalFile)
             Catch ex As Exception
+                File.Delete(LocalFile)
                 Throw New WebException("直接下载文件失败(" & Url & ")。", ex)
             End Try
         End Using

+ 50 - 43
Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb

@@ -20,6 +20,7 @@
         Forge = 1
         Fabric = 4
         Quilt = 5
+        NeoForge = 6
     End Enum
     <Flags> Public Enum CompSourceType
         CurseForge = 1
@@ -258,14 +259,7 @@
                     Website = Data("links")("websiteUrl").ToString.TrimEnd("/")
                     LastUpdate = Data("dateReleased") '#1194
                     DownloadCount = Data("downloadCount")
-                    'Logo
-                    If Data("logo").Count > 0 Then
-                        If GetPixelSize(1) > 1.25 Then
-                            LogoUrl = Data("logo")("thumbnailUrl").ToString '使用 256x256 图标
-                        Else
-                            LogoUrl = Data("logo")("thumbnailUrl").ToString.Replace("/256/256/", "/64/64/") '使用 64x64 图标
-                        End If
-                    End If
+                    If Data("logo").Count > 0 Then LogoUrl = Data("logo")("thumbnailUrl")
                     'FileIndexes / GameVersions
                     Dim Files = If(CType(Data("latestFilesIndexes"), JArray), New JArray).
                                 Where(Function(i) i("gameVersion").ToString.Contains("1."))
@@ -371,6 +365,7 @@
                             Case "forge" : ModLoaders.Add(CompModLoaderType.Forge)
                             Case "fabric" : ModLoaders.Add(CompModLoaderType.Fabric)
                             Case "quilt" : ModLoaders.Add(CompModLoaderType.Quilt)
+                            Case "neoforge" : ModLoaders.Add(CompModLoaderType.NeoForge)
                         'Mod
                             Case "worldgen" : Tags.Add("世界元素")
                             Case "technology" : Tags.Add("科技")
@@ -442,21 +437,34 @@
                 GameVersionDescription = "未知"
             Else
                 Dim SpaVersions As New List(Of String)
-                For i = 0 To GameVersions.Count - 1
+                Dim IsOld As Boolean = False
+                For i = 0 To GameVersions.Count - 1 '版本号一定为降序
+                    '获取当前连续的版本号段
                     Dim StartVersion As Integer = GameVersions(i), EndVersion As Integer = GameVersions(i)
-                    For ii = i + 1 To GameVersions.Count - 1
-                        If GameVersions(ii) = EndVersion - 1 Then
-                            EndVersion = GameVersions(ii)
-                            i = ii
-                        Else
+                    If StartVersion < 10 Then '如果支持新版本,则不显示 1.9-
+                        If SpaVersions.Any() AndAlso Not IsOld Then
                             Exit For
+                        Else
+                            IsOld = True
                         End If
+                    End If
+                    For ii = i + 1 To GameVersions.Count - 1
+                        If GameVersions(ii) <> EndVersion - 1 Then Exit For
+                        EndVersion = GameVersions(ii)
+                        i = ii
                     Next
+                    '将版本号段转为描述文本
                     If StartVersion = EndVersion Then
                         SpaVersions.Add("1." & StartVersion)
                     ElseIf McVersionHighest > -1 AndAlso StartVersion >= McVersionHighest Then
-                        SpaVersions.Add("1." & EndVersion & "+")
-                    ElseIf EndVersion <= 7 Then
+                        If EndVersion <= 10 Then
+                            SpaVersions.Clear()
+                            SpaVersions.Add("全版本")
+                            Exit For
+                        Else
+                            SpaVersions.Add("1." & EndVersion & "+")
+                        End If
+                    ElseIf EndVersion <= 10 Then
                         SpaVersions.Add("1." & StartVersion & "-")
                         Exit For
                     ElseIf StartVersion - EndVersion = 1 Then
@@ -467,6 +475,28 @@
                 Next
                 GameVersionDescription = SpaVersions.Join(", ")
             End If
+            '获取 Mod 加载器描述
+            Dim ModLoaderDescriptionFull As String, ModLoaderDescriptionPart As String
+            Select Case ModLoaders.Count
+                Case 0
+                    ModLoaderDescriptionFull = "未知"
+                    ModLoaderDescriptionPart = ""
+                Case 1
+                    ModLoaderDescriptionFull = "仅 " & ModLoaders.Single.ToString
+                    ModLoaderDescriptionPart = ModLoaders.Single.ToString
+                Case 2, 3
+                    If Setup.Get("ToolDownloadIgnoreQuilt") AndAlso
+                       ModLoaders.Contains(CompModLoaderType.Forge) AndAlso ModLoaders.Contains(CompModLoaderType.Fabric) Then
+                        ModLoaderDescriptionFull = "任意"
+                        ModLoaderDescriptionPart = ""
+                    Else
+                        ModLoaderDescriptionFull = ModLoaders.Join(" / ")
+                        ModLoaderDescriptionPart = ModLoaders.Join(" / ")
+                    End If
+                Case Else
+                    ModLoaderDescriptionFull = "任意"
+                    ModLoaderDescriptionPart = ""
+            End Select
             '实例化 UI
             Dim NewItem As New MyCompItem With {.Tag = Me, .Logo = GetControlLogo()}
             Dim Title = GetControlTitle(True)
@@ -488,37 +518,13 @@
                 NewItem.ColumnVersion3.Width = New GridLength(0)
             ElseIf ShowMcVersionDesc AndAlso ShowMcVersionDesc Then
                 '全部显示
-                Dim ModLoaderDesc As String = If(ModLoaders.Count > 0 AndAlso ModLoaders.Count < 3 AndAlso
-                        Not (Type = CompType.Mod AndAlso Setup.Get("ToolDownloadIgnoreQuilt") AndAlso ModLoaders.Contains(CompModLoaderType.Forge) AndAlso ModLoaders.Contains(CompModLoaderType.Fabric)),
-                        ModLoaders.Join(" / ") & " ", "")
-                Dim VersionDesc As String = If(GameVersionDescription = "1.7+", "全版本", GameVersionDescription)
-                NewItem.LabVersion.Text = ModLoaderDesc & VersionDesc
+                NewItem.LabVersion.Text = If(ModLoaderDescriptionPart = "", "", ModLoaderDescriptionPart & " ") & GameVersionDescription
             ElseIf ShowMcVersionDesc Then
                 '仅显示版本
-                NewItem.LabVersion.Text = If(GameVersionDescription = "1.7+", "全版本", GameVersionDescription)
+                NewItem.LabVersion.Text = GameVersionDescription
             Else
                 '仅显示 Mod 加载器
-                Select Case ModLoaders.Count
-                    Case 0
-                        NewItem.LabVersion.Text = "未知"
-                    Case 1
-                        NewItem.LabVersion.Text = If(Type = CompType.Mod, "仅 ", "") & ModLoaders(0).ToString
-                    Case 2
-                        If Type = CompType.Mod Then
-                            NewItem.LabVersion.Text =
-                                If(Not (Setup.Get("ToolDownloadIgnoreQuilt") AndAlso
-                                ModLoaders.Contains(CompModLoaderType.Forge) AndAlso ModLoaders.Contains(CompModLoaderType.Fabric)),
-                                ModLoaders.Join(" / "), "任意")
-                        Else
-                            NewItem.LabVersion.Text = ModLoaders.Join(" / ")
-                        End If
-                    Case 3
-                        If Type = CompType.Mod Then
-                            NewItem.LabVersion.Text = "任意"
-                        Else
-                            NewItem.LabVersion.Text = ModLoaders.Join(" / ")
-                        End If
-                End Select
+                NewItem.LabVersion.Text = ModLoaderDescriptionFull
             End If
             NewItem.LabSource.Text = If(FromCurseForge, "CurseForge", "Modrinth")
             If LastUpdate IsNot Nothing Then
@@ -1255,6 +1261,7 @@ Retry:
                 If RawVersions.Contains("forge") Then ModLoaders.Add(CompModLoaderType.Forge)
                 If RawVersions.Contains("fabric") Then ModLoaders.Add(CompModLoaderType.Fabric)
                 If RawVersions.Contains("quilt") Then ModLoaders.Add(CompModLoaderType.Quilt)
+                If RawVersions.Contains("neoforge") Then ModLoaders.Add(CompModLoaderType.NeoForge)
 #End Region
             Else
 #Region "Modrinth"

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

@@ -625,7 +625,7 @@
                     Dim ReleaseDate As New Date(ReleaseTimeSplit(0), ReleaseTimeSplit(1), ReleaseTimeSplit(2), '年月日
                                                 ReleaseTimeSplit(3), ReleaseTimeSplit(4), ReleaseTimeSplit(5), '时分秒
                                                 0, DateTimeKind.Utc) '以 UTC 时间作为标准
-                    Dim ReleaseTime As String = ReleaseDate.ToLocalTime.ToString("yyyy/MM/dd HH:mm") '时区与格式转换
+                    Dim ReleaseTime As String = ReleaseDate.ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm") '时区与格式转换
                     '分类与 MD5 获取
                     Dim MD5 As String, Category As String
                     If VersionCode.Contains("classifier-installer""") Then
@@ -706,7 +706,7 @@
                 '基础信息获取
                 Dim Entry = New DlForgeVersionEntry With {.Hash = Hash, .Category = Category, .Version = Name, .Branch = Branch, .Inherit = Inherit, .IsRecommended = Recommended = Name}
                 Dim TimeSplit = Token("modified").ToString.Split("-"c, "T"c, ":"c, "."c, " "c, "/"c)
-                Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy/MM/dd HH:mm")
+                Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm")
                 '添加项
                 Versions.Add(Entry)
             Next
@@ -812,7 +812,7 @@
                              .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
                              .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
                              .MD5 = RealEntry("md5"),
-                             .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
+                             .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
                              .JsonToken = RealEntry
                          })
             Next
@@ -841,7 +841,7 @@
                              .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
                              .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
                              .MD5 = RealEntry("md5"),
-                             .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
+                             .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
                              .JsonToken = RealEntry
                          })
             Next

+ 17 - 56
Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb

@@ -435,13 +435,7 @@ NextInner:
             Case McLoginType.Ms
                 Loader = McLoginMsLoader
             Case McLoginType.Legacy
-                If PageLinkHiper.HiperState = LoadState.Finished Then
-                    '使用 HiPer 内网验证
-                    Loader = McLoginHiperLoader
-                Else
-                    '默认的离线验证
-                    Loader = McLoginLegacyLoader
-                End If
+                Loader = McLoginLegacyLoader
             Case McLoginType.Nide
                 Loader = McLoginNideLoader
             Case McLoginType.Auth
@@ -462,7 +456,6 @@ NextInner:
     Public McLoginLegacyLoader As New LoaderTask(Of McLoginLegacy, McLoginResult)("Loader Login Legacy", AddressOf McLoginLegacyStart)
     Public McLoginNideLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Nide", AddressOf McLoginServerStart) With {.ReloadTimeout = 60000}
     Public McLoginAuthLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Auth", AddressOf McLoginServerStart) With {.ReloadTimeout = 60000}
-    Public McLoginHiperLoader As New LoaderTask(Of McLoginLegacy, McLoginResult)("Loader Login HiPer", AddressOf McLoginHiperStart) With {.ReloadTimeout = 60000}
 
     '主加载函数,返回所有需要的登录信息
     Private Sub McLoginMsStart(Data As LoaderTask(Of McLoginMs, McLoginResult))
@@ -615,45 +608,6 @@ LoginFinish:
         Names.Insert(0, Input.UserName)
         Setup.Set("LoginLegacyName", Join(Names.ToArray, "¨"))
     End Sub
-    Private Sub McLoginHiperStart(Data As LoaderTask(Of McLoginLegacy, McLoginResult))
-        Dim Input As McLoginLegacy = Data.Input
-        McLaunchLog("登录方式:联机离线(" & Input.UserName & ")")
-        Data.Progress = 0.05
-        '尝试登录
-        Try
-            McLaunchLog("登录开始(Login, HiPer)")
-            Dim LoginJson As JObject = GetJson(NetRequestRetry(
-                   Url:="http://hiperauth.tech/api/yggdrasil-hiper/authserver/authenticate",
-                   Method:="POST",
-                   Data:="{""agent"": {""name"": ""Minecraft"",""version"": 1},""username"":""" & Data.Input.UserName & """,""password"":""" &
-                         McLoginLegacyUuidWithCustomSkin(Input.UserName, Input.SkinType, Input.SkinName) & """,""requestUser"":true}",
-                   ContentType:="application/json; charset=utf-8"))
-            '将登录结果输出
-            Data.Output.AccessToken = LoginJson("accessToken").ToString
-            Data.Output.ClientToken = LoginJson("clientToken").ToString
-            Data.Output.Name = LoginJson("selectedProfile")("name").ToString
-            Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString
-            Data.Output.Type = "Auth"
-            McLaunchLog("登录成功(Login, HiPer)")
-            '保存启动记录
-            Dim Names As New List(Of String)
-            If Not Setup.Get("LoginLegacyName") = "" Then Names.AddRange(Setup.Get("LoginLegacyName").ToString.Split("¨"))
-            Names.Remove(Input.UserName)
-            Names.Insert(0, Input.UserName)
-            Setup.Set("LoginLegacyName", Join(Names.ToArray, "¨"))
-        Catch ex As Exception
-            Dim AllMessage As String = GetExceptionSummary(ex)
-            Log(ex, "登录失败原始错误信息", LogLevel.Normal)
-            Dim ThrowEx As Exception = ex
-            If AllMessage.Contains("403") Then
-                ThrowEx = New Exception("登录尝试过于频繁,导致被系统暂时屏蔽。请不要操作,等待 10 分钟后再试。", ex)
-            ElseIf AllMessage.Contains("超时") OrElse AllMessage.Contains("imeout") OrElse AllMessage.Contains("网络请求失败") Then
-                ThrowEx = New Exception("$登录失败:连接登录服务器超时。" & vbCrLf & "请检查 HiPer 联机模块的连接状况是否良好,或选择其他登录方式!")
-            End If
-            McLaunchLog("登录失败:" & GetExceptionSummary(ThrowEx))
-            Throw ThrowEx
-        End Try
-    End Sub
 
     'Server 登录:三种验证方式的请求
     Private Sub McLoginRequestValidate(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult))
@@ -664,11 +618,13 @@ LoginFinish:
         Dim Uuid As String = Setup.Get("Cache" & Data.Input.Token & "Uuid")
         Dim Name As String = Setup.Get("Cache" & Data.Input.Token & "Name")
         '发送登录请求
+        Dim RequestData As New JObject(
+            New JProperty("accessToken", AccessToken), New JProperty("clientToken", ClientToken), New JProperty("requestUser", True))
         NetRequestRetry(
-               Url:=Data.Input.BaseUrl & "/validate",
-               Method:="POST",
-               Data:="{""accessToken"":""" & AccessToken & """,""clientToken"":""" & ClientToken & """,""requestUser"":true}",
-               ContentType:="application/json; charset=utf-8") '没有返回值的
+            Url:=Data.Input.BaseUrl & "/validate",
+            Method:="POST",
+            Data:=RequestData.ToString(0),
+            ContentType:="application/json; charset=utf-8") '没有返回值的
         '将登录结果输出
         Data.Output.AccessToken = AccessToken
         Data.Output.ClientToken = ClientToken
@@ -712,11 +668,16 @@ LoginFinish:
         Try
             Dim NeedRefresh As Boolean = False
             McLaunchLog("登录开始(Login, " & Data.Input.Token & ")")
+            Dim RequestData As New JObject(
+                New JProperty("agent", New JObject(New JProperty("name", "Minecraft"), New JProperty("version", 1))),
+                New JProperty("username", Data.Input.UserName),
+                New JProperty("password", Data.Input.Password),
+                New JProperty("requestUser", True))
             Dim LoginJson As JObject = GetJson(NetRequestRetry(
-                   Url:=Data.Input.BaseUrl & "/authenticate",
-                   Method:="POST",
-                   Data:="{""agent"": {""name"": ""Minecraft"",""version"": 1},""username"":""" & Data.Input.UserName & """,""password"":""" & Data.Input.Password & """,""requestUser"":true}",
-                   ContentType:="application/json; charset=utf-8"))
+                Url:=Data.Input.BaseUrl & "/authenticate",
+                Method:="POST",
+                Data:=RequestData.ToString(0),
+                ContentType:="application/json; charset=utf-8"))
             '检查登录结果
             If LoginJson("availableProfiles").Count = 0 Then
                 If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色,无法更换!", HintType.Critical)
@@ -964,7 +925,7 @@ SystemBrowser:
     Private Function MsLoginStep5(Tokens As String()) As String
         McLaunchLog("开始微软登录步骤 5")
 
-        Dim Request As String = "{""identityToken"": ""XBL3.0 x=" & Tokens(1) & ";" & Tokens(0) & """}"
+        Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
         Dim Result As String
         Try
             Result = NetRequestMulty("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json", 2)

+ 40 - 43
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -128,7 +128,7 @@
             ""name"": ""PCL"",
             ""lastVersionId"": ""latest-release"",
             ""type"": ""latest-release"",
-            ""lastUsed"": """ & Date.Now.ToString("yyyy-MM-dd") & "T" & Date.Now.ToString("HH:mm:ss") & ".0000Z""
+            ""lastUsed"": """ & Date.Now.ToString("yyyy'-'MM'-'dd") & "T" & Date.Now.ToString("HH':'mm':'ss") & ".0000Z""
         }
     },
     ""selectedProfile"": ""PCL"",
@@ -779,7 +779,7 @@ ExitDataLoad:
                     WriteIni(Path & "PCL\Setup.ini", "Logo", Logo)
                 End If
                 If State <> McVersionState.Error Then
-                    WriteIni(Path & "PCL\Setup.ini", "ReleaseTime", ReleaseTime.ToString("yyyy-MM-dd HH:mm:ss"))
+                    WriteIni(Path & "PCL\Setup.ini", "ReleaseTime", ReleaseTime.ToString("yyyy'-'MM'-'dd HH':'mm"))
                     WriteIni(Path & "PCL\Setup.ini", "VersionFabric", Version.FabricVersion)
                     WriteIni(Path & "PCL\Setup.ini", "VersionOptiFine", Version.OptiFineVersion)
                     WriteIni(Path & "PCL\Setup.ini", "VersionLiteLoader", Version.HasLiteLoader)
@@ -1613,13 +1613,13 @@ NextVersion:
         End Property
         Private _Url As String
         ''' <summary>
-        ''' 原 Json 中 Name 项除去最后一部分版本号的较前部分。可能为 Nothing。
+        ''' 原 Json 中 Name 项除去版本号部分的较前部分。可能为 Nothing。
         ''' </summary>
         Public ReadOnly Property Name As String
             Get
                 If OriginalName Is Nothing Then Return Nothing
                 Dim Splited As New List(Of String)(OriginalName.Split(":"))
-                Splited.RemoveAt(Splited.Count - 1)
+                Splited.RemoveAt(2) 'Java 的此格式下版本号固定为第三段,第四段可能包含架构、分包等其他信息
                 Return Join(Splited, ":")
             End Get
         End Property
@@ -1678,7 +1678,7 @@ NextVersion:
             End If
             If Not IsNothing(Rule("features")) Then '标签
                 IsRightRule = IsRightRule AndAlso IsNothing(Rule("features")("is_demo_user")) '反选是否为 Demo 用户
-                If CType(Rule("features"), JObject).Children.Any(Function(j As JProperty) j.Name.StartsWith("is_quick_play")) Then
+                If CType(Rule("features"), JObject).Children.Any(Function(j As JProperty) j.Name.Contains("quick_play")) Then
                     IsRightRule = False '不开 Quick Play,让玩家自己加去
                 End If
             End If
@@ -1788,8 +1788,7 @@ NextVersion:
             End If
 
             '根据是否本地化处理(Natives)
-            If Library("natives") Is Nothing Then
-                '没有 Natives
+            If Library("natives") Is Nothing Then '没有 Natives
                 Dim LocalPath As String
                 If IsJumpLoader Then
                     LocalPath = McLibGet(Library("name"), CustomMcFolder:=If(JumpLoaderFolder, CustomMcFolder))
@@ -1813,29 +1812,26 @@ NextVersion:
                     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})
                 End Try
-            Else
-                '有 Natives
-                If Library("natives")("windows") IsNot Nothing Then
-                    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,
-                                                           McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")),
-                                                           CustomMcFolder & "libraries\" & Library("downloads")("classifiers")("natives-windows")("path").ToString.Replace("/", "\")),
-                                                       .Size = Val(Library("downloads")("classifiers")("natives-windows")("size").ToString),
-                                                       .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})
-                        End If
-                    Catch ex As Exception
-                        Log(ex, "处理实际支持库列表失败(有 Natives," & If(Library("name"), "Nothing").ToString & ")")
+            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,
+                                 McLibGet(Library("name"), CustomMcFolder:=CustomMcFolder).Replace(".jar", "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")),
+                                 CustomMcFolder & "libraries\" & Library("downloads")("classifiers")("natives-windows")("path").ToString.Replace("/", "\")),
+                             .Size = Val(Library("downloads")("classifiers")("natives-windows")("size").ToString),
+                             .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})
-                    End Try
-                End If
+                    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})
+                End Try
             End If
 
         Next
@@ -1845,17 +1841,19 @@ NextVersion:
         For i = 0 To BasicArray.Count - 1
             Dim Key As String = BasicArray(i).Name & BasicArray(i).IsNatives.ToString & BasicArray(i).IsJumpLoader.ToString
             If ResultArray.ContainsKey(Key) Then
-                If BasicArray(i).Version <> ResultArray(Key).Version AndAlso
-                    (KeepSameNameDifferentVersionResult OrElse BasicArray(i).Version.Contains("natives-windows")) Then
-                    'Contains("natives-windows") 源于 1.19-Pre1 开始,lwjgl-3.3.1-natives-windows 与 lwjgl-3.3.1-natives-windows-x86 重复
+                If BasicArray(i).Version <> ResultArray(Key).Version AndAlso KeepSameNameDifferentVersionResult Then
+                    Log($"[Minecraft] 发现疑似重复的支持库:{BasicArray(i)} 与 {ResultArray(Key)}")
                     ResultArray.Add(Key & GetUuid(), BasicArray(i))
-                ElseIf VersionSortBoolean(BasicArray(i).Version, ResultArray(Key).Version) Then
-                    ResultArray(Key) = BasicArray(i)
+                Else
+                    Log($"[Minecraft] 发现重复的支持库:{BasicArray(i)} 与 {ResultArray(Key)},已忽略其中之一")
+                    If VersionSortBoolean(BasicArray(i).Version, ResultArray(Key).Version) Then
+                        ResultArray(Key) = BasicArray(i)
+                    End If
                 End If
             Else
                 ResultArray.Add(Key, BasicArray(i))
             End If
-        Next i
+        Next
         Return ResultArray.Values.ToList
     End Function
 
@@ -2043,14 +2041,13 @@ NextVersion:
             '    Return GetJson("{""id"": """ & AssetsName & """}")
             'Else
             Log("[Minecraft] 无法获取资源文件索引下载地址,使用默认的 legacy 下载地址")
-            Return GetJson("
-                    {
-                        ""id"": ""legacy"",
-                        ""sha1"": ""c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729"",
-                        ""size"": 134284,
-                        ""url"": ""https://launchermeta.mojang.com/mc-staging/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json"",
-                        ""totalSize"": 111220701
-                    }")
+            Return GetJson("{
+                ""id"": ""legacy"",
+                ""sha1"": ""c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729"",
+                ""size"": 134284,
+                ""url"": ""https://launchermeta.mojang.com/mc-staging/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json"",
+                ""totalSize"": 111220701
+            }")
             'End If
         Else
             Throw New Exception("该版本不存在资源文件索引信息")

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

@@ -943,7 +943,7 @@ VersionFindFail:
                     Dim Project As New CompProject(ProjectJson)
                     For Each Entry In ModrinthMapping(Project.Id)
                         If Entry.Comp IsNot Nothing AndAlso Not Entry.IsCompFromModrinth Then
-                            Project.LogoUrl = Entry.Comp.LogoUrl 'Modrinth 部分 Logo 加载不出来
+                            Project.LogoUrl = Entry.Comp.LogoUrl 'Modrinth 的部分 Logo 加载不出来
                         End If
                         Entry.IsCompFromModrinth = True
                         Entry.Comp = Project
@@ -992,6 +992,7 @@ VersionFindFail:
                 Dim CurseForgeProject = CType(GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods", "POST",
                                     $"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("data")
                 For Each ProjectJson In CurseForgeProject
+                    If Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
                     Dim Project As New CompProject(ProjectJson)
                     For Each Entry In CurseForgeMapping(Project.Id) '倒查防止 CurseForge 返回的内容有漏
                         If Entry.Comp IsNot Nothing AndAlso Entry.IsCompFromModrinth Then

+ 27 - 20
Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb

@@ -46,34 +46,41 @@
         End Set
     End Property
     '后台加载 Logo
-    Private Sub LogoLoader(Address As String)
-        Dim Retry As Boolean = False
+    Private Sub LogoLoader(LocalFileAddress As String)
+        Dim Retried As Boolean = False
         Dim DownloadEnd As String = GetUuid()
 RetryStart:
         Try
-            NetDownload(_Logo, Address & DownloadEnd)
+            'CurseForge 图片使用缩略图
+            Dim Url As String = _Logo
+            If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
+                Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
+            End If
+            '下载图片
+            NetDownload(Url, LocalFileAddress & DownloadEnd)
             Dim LoadError As Exception = Nothing
-            RunInUiWait(Sub()
-                            Try
-                                '在地址更换时取消加载
-                                If Not Address = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png" Then Exit Sub
-                                '在完成正常加载后才保存缓存图片
-                                PathLogo.Source = New MyBitmap(Address & DownloadEnd)
-                            Catch ex As Exception
-                                Log(ex, "读取资源工程图标失败(" & Address & ")")
-                                File.Delete(Address & DownloadEnd)
-                                LoadError = ex
-                            End Try
-                        End Sub)
+            RunInUiWait(
+            Sub()
+                Try
+                    '在地址更换时取消加载
+                    If LocalFileAddress <> $"{PathTemp}CompLogo\{GetHash(_Logo)}.png" Then Exit Sub
+                    '在完成正常加载后才保存缓存图片
+                    PathLogo.Source = New MyBitmap(LocalFileAddress & DownloadEnd)
+                Catch ex As Exception
+                    Log(ex, $"读取资源工程图标失败({LocalFileAddress})")
+                    File.Delete(LocalFileAddress & DownloadEnd)
+                    LoadError = ex
+                End Try
+            End Sub)
             If LoadError IsNot Nothing Then Throw LoadError
-            If File.Exists(Address) Then
-                File.Delete(Address & DownloadEnd)
+            If File.Exists(LocalFileAddress) Then
+                File.Delete(LocalFileAddress & DownloadEnd)
             Else
-                FileIO.FileSystem.MoveFile(Address & DownloadEnd, Address)
+                FileIO.FileSystem.MoveFile(LocalFileAddress & DownloadEnd, LocalFileAddress)
             End If
         Catch ex As Exception
-            If Not Retry Then
-                Retry = True
+            If Not Retried Then
+                Retried = True
                 GoTo RetryStart
             Else
                 Log(ex, $"下载资源工程图标失败({_Logo})")

+ 27 - 20
Plain Craft Launcher 2/Modules/Minecraft/MyLocalModItem.xaml.vb

@@ -48,34 +48,41 @@ Public Class MyLocalModItem
         End Set
     End Property
     '后台加载 Logo
-    Private Sub LogoLoader(Address As String)
-        Dim Retry As Boolean = False
+    Private Sub LogoLoader(LocalFileAddress As String)
+        Dim Retried As Boolean = False
         Dim DownloadEnd As String = GetUuid()
 RetryStart:
         Try
-            NetDownload(_Logo, Address & DownloadEnd)
+            'CurseForge 图片使用缩略图
+            Dim Url As String = _Logo
+            If Url.Contains("/256/256/") AndAlso GetPixelSize(1) <= 1.25 AndAlso Not Retried Then
+                Url = Url.Replace("/256/256/", "/64/64/") '#3075:部分 Mod 不存在 64x64 图标,所以重试时不再缩小
+            End If
+            '下载图片
+            NetDownload(Url, LocalFileAddress & DownloadEnd)
             Dim LoadError As Exception = Nothing
-            RunInUiWait(Sub()
-                            Try
-                                '在地址更换时取消加载
-                                If Not Address = PathTemp & "CompLogo\" & GetHash(_Logo) & ".png" Then Exit Sub
-                                '在完成正常加载后才保存缓存图片
-                                PathLogo.Source = New MyBitmap(Address & DownloadEnd)
-                            Catch ex As Exception
-                                Log(ex, "读取本地 Mod 图标失败(" & Address & ")")
-                                File.Delete(Address & DownloadEnd)
-                                LoadError = ex
-                            End Try
-                        End Sub)
+            RunInUiWait(
+            Sub()
+                Try
+                    '在地址更换时取消加载
+                    If LocalFileAddress <> $"{PathTemp}CompLogo\{GetHash(_Logo)}.png" Then Exit Sub
+                    '在完成正常加载后才保存缓存图片
+                    PathLogo.Source = New MyBitmap(LocalFileAddress & DownloadEnd)
+                Catch ex As Exception
+                    Log(ex, "读取本地 Mod 图标失败(" & LocalFileAddress & ")")
+                    File.Delete(LocalFileAddress & DownloadEnd)
+                    LoadError = ex
+                End Try
+            End Sub)
             If LoadError IsNot Nothing Then Throw LoadError
-            If File.Exists(Address) Then
-                File.Delete(Address & DownloadEnd)
+            If File.Exists(LocalFileAddress) Then
+                File.Delete(LocalFileAddress & DownloadEnd)
             Else
-                FileIO.FileSystem.MoveFile(Address & DownloadEnd, Address)
+                FileIO.FileSystem.MoveFile(LocalFileAddress & DownloadEnd, LocalFileAddress)
             End If
         Catch ex As Exception
-            If Not Retry Then
-                Retry = True
+            If Not Retried Then
+                Retried = True
                 GoTo RetryStart
             Else
                 Log(ex, $"下载本地 Mod 图标失败({_Logo})")

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

@@ -12,7 +12,7 @@ Imports System.Runtime.InteropServices
 <Assembly: AssemblyDescription("Minecraft 启动器 (制作:龙腾猫跃)")>
 <Assembly: AssemblyCompany("")>
 <Assembly: AssemblyProduct("Plain Craft Launcher 2")>
-<Assembly: AssemblyCopyright("Copyright © 龙腾猫跃 2016-2023. All Rights Reserved.")>
+<Assembly: AssemblyCopyright("Copyright © 龙腾猫跃 2016-2024. All Rights Reserved.")>
 <Assembly: AssemblyConfiguration("PCL2 Config Mark")>
 <Assembly: ComVisible(False)>
 
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
 ' 方法是按如下所示使用“*”
 
-<Assembly: AssemblyVersion("2.6.12.0")>
-<Assembly: AssemblyFileVersion("2.6.12.0")>
+<Assembly: AssemblyVersion("2.6.13.0")>
+<Assembly: AssemblyFileVersion("2.6.13.0")>
 <Assembly: NeutralResourcesLanguage("")>

+ 34 - 35
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -192,7 +192,7 @@ Public Module ModDownloadLib
         '建立控件
         Dim NewItem As New MyListItem With {.Logo = Logo, .SnapsToDevicePixels = True, .Title = Entry("id").ToString, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry}
         If Entry("lore") Is Nothing Then
-            NewItem.Info = Entry("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
+            NewItem.Info = Entry("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
         Else
             NewItem.Info = Entry("lore").ToString
         End If
@@ -632,8 +632,8 @@ Retry:
                     Dim Json As String = "{
     ""id"": """ & Id & """,
     ""inheritsFrom"": """ & DownloadInfo.Inherit & """,
-    ""time"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy-MM-dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
-    ""releaseTime"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy-MM-dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
+    ""time"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy'-'MM'-'dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
+    ""releaseTime"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy'-'MM'-'dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
     ""type"": ""release"",
     ""libraries"": [
         {""name"": ""optifine:OptiFine:" & DownloadInfo.NameFile.Replace("OptiFine_", "").Replace(".jar", "").Replace("preview_", "") & """},
@@ -900,42 +900,41 @@ Retry:
 
         '启动依赖版本的下载
         If ClientDownloadLoader Is Nothing Then
-            Loaders.Add(New LoaderTask(Of String, String)(
-                    "启动 LiteLoader 依赖版本下载",
-                    Sub()
-                        If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
-                        ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
-                    End Sub) With {.ProgressWeight = 0.2, .Show = False, .Block = False})
+            Loaders.Add(New LoaderTask(Of String, String)("启动 LiteLoader 依赖版本下载",
+            Sub()
+                If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
+                ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
+            End Sub) With {.ProgressWeight = 0.2, .Show = False, .Block = False})
         End If
         '安装
         Loaders.Add(New LoaderTask(Of String, String)("安装 LiteLoader",
-                                                            Sub(Task As LoaderTask(Of String, String))
-                                                                Try
-                                                                    '新建版本文件夹
-                                                                    Directory.CreateDirectory(VersionFolder)
-                                                                    '构造版本 Json
-                                                                    Dim VersionJson As New JObject
-                                                                    VersionJson.Add("id", VersionName)
-                                                                    VersionJson.Add("time", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
-                                                                    VersionJson.Add("releaseTime", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
-                                                                    VersionJson.Add("type", "release")
-                                                                    VersionJson.Add("arguments", GetJson("{""game"":[""--tweakClass"",""" & DownloadInfo.JsonToken("tweakClass").ToString & """]}"))
-                                                                    VersionJson.Add("libraries", DownloadInfo.JsonToken("libraries"))
-                                                                    CType(VersionJson("libraries"), JContainer).Add(GetJson("{""name"": ""com.mumfrey:liteloader:" & DownloadInfo.JsonToken("version").ToString & """,""url"": ""https://dl.liteloader.com/versions/""}"))
-                                                                    VersionJson.Add("mainClass", "net.minecraft.launchwrapper.Launch")
-                                                                    VersionJson.Add("minimumLauncherVersion", 18)
-                                                                    VersionJson.Add("inheritsFrom", DownloadInfo.Inherit)
-                                                                    VersionJson.Add("jar", DownloadInfo.Inherit)
-                                                                    '输出 Json 文件
-                                                                    WriteFile(VersionFolder & VersionName & ".json", VersionJson.ToString)
-                                                                Catch ex As Exception
-                                                                    Throw New Exception("安装新 LiteLoader 版本失败", ex)
-                                                                End Try
-                                                            End Sub) With {.ProgressWeight = 1})
+        Sub(Task As LoaderTask(Of String, String))
+            Try
+                '新建版本文件夹
+                Directory.CreateDirectory(VersionFolder)
+                '构造版本 Json
+                Dim VersionJson As New JObject
+                VersionJson.Add("id", VersionName)
+                VersionJson.Add("time", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
+                VersionJson.Add("releaseTime", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
+                VersionJson.Add("type", "release")
+                VersionJson.Add("arguments", GetJson("{""game"":[""--tweakClass"",""" & DownloadInfo.JsonToken("tweakClass").ToString & """]}"))
+                VersionJson.Add("libraries", DownloadInfo.JsonToken("libraries"))
+                CType(VersionJson("libraries"), JContainer).Add(GetJson("{""name"": ""com.mumfrey:liteloader:" & DownloadInfo.JsonToken("version").ToString & """,""url"": ""https://dl.liteloader.com/versions/""}"))
+                VersionJson.Add("mainClass", "net.minecraft.launchwrapper.Launch")
+                VersionJson.Add("minimumLauncherVersion", 18)
+                VersionJson.Add("inheritsFrom", DownloadInfo.Inherit)
+                VersionJson.Add("jar", DownloadInfo.Inherit)
+                '输出 Json 文件
+                WriteFile(VersionFolder & VersionName & ".json", VersionJson.ToString)
+            Catch ex As Exception
+                Throw New Exception("安装新 LiteLoader 版本失败", ex)
+            End Try
+        End Sub) With {.ProgressWeight = 1})
         '下载支持库
         If FixLibrary Then
             Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 LiteLoader 支持库文件",
-                                                                Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
+                Sub(Task) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
             Loaders.Add(New LoaderDownload("下载 LiteLoader 支持库文件", New List(Of NetFile)) With {.ProgressWeight = 6})
         End If
 
@@ -1797,7 +1796,7 @@ Retry:
         '建立控件
         Dim NewItem As New MyListItem With {
             .Title = Entry.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry,
-            .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy/MM/dd HH:mm"),
+            .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy'/'MM'/'dd HH':'mm"),
             .Logo = PathImage & "Blocks/Fabric.png"
         }
         AddHandler NewItem.Click, OnClick
@@ -1808,7 +1807,7 @@ Retry:
         '建立控件
         Dim NewItem As New MyListItem With {
             .Title = Entry.DisplayName.ToLower.Replace("optifabric-", "").Replace(".jar", "").Trim.TrimStart("v"), .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry,
-            .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy/MM/dd HH:mm"),
+            .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy'/'MM'/'dd HH':'mm"),
             .Logo = PathImage & "Blocks/OptiFabric.png"
         }
         AddHandler NewItem.Click, OnClick

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

@@ -1,9 +1,9 @@
 <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="PageDownloadClient"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadClient"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
             <StackPanel Orientation="Vertical" Margin="5">

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

@@ -55,9 +55,8 @@
             Next
             '排序
             For i = 0 To Dict.Keys.Count - 1
-                Dict(Dict.Keys(i)) = Sort(Dict.Values(i), Function(Left As JObject, Right As JObject) As Boolean
-                                                              Return Left("releaseTime").Value(Of Date) > Right("releaseTime").Value(Of Date)
-                                                          End Function)
+                Dict(Dict.Keys(i)) = Sort(Dict.Values(i),
+                                          Function(a, b) a("releaseTime").Value(Of Date) > b("releaseTime").Value(Of Date))
             Next
             '清空当前
             PanMain.Children.Clear()
@@ -65,11 +64,11 @@
             Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 0, 0, 15), .SwapType = 2}
             Dim TopestVersions As New List(Of JObject)
             Dim Release As JObject = Dict("正式版")(0).DeepClone()
-            Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
+            Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
             TopestVersions.Add(Release)
             If Dict("正式版")(0)("releaseTime").Value(Of Date) < Dict("预览版")(0)("releaseTime").Value(Of Date) Then
                 Dim Snapshot As JObject = Dict("预览版")(0).DeepClone()
-                Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
+                Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
                 TopestVersions.Add(Snapshot)
             End If
             Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = TopestVersions}

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

@@ -1,9 +1,9 @@
 <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="PageDownloadCompDetail"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadCompDetail"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <Grid Margin="25,25,25,10">
             <Grid.RowDefinitions>

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

@@ -1,9 +1,9 @@
 <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="PageDownloadFabric"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadFabric"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Orientation="Vertical" Margin="5">
             <local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="Fabric 简介" x:Name="CardTip">

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

@@ -1,9 +1,9 @@
 <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="PageDownloadForge"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadForge"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Orientation="Vertical" Margin="5">
             <local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="Forge 简介" x:Name="CardTip">

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

@@ -1,9 +1,9 @@
 <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="PageDownloadInstall"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadInstall"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
             <Grid Margin="25,10,25,25">

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

@@ -549,11 +549,11 @@
             Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15), .SwapType = 2}
             Dim TopestVersions As New List(Of JObject)
             Dim Release As JObject = Dict("正式版")(0).DeepClone()
-            Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
+            Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
             TopestVersions.Add(Release)
             If Dict("正式版")(0)("releaseTime").Value(Of Date) < Dict("预览版")(0)("releaseTime").Value(Of Date) Then
                 Dim Snapshot As JObject = Dict("预览版")(0).DeepClone()
-                Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
+                Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
                 TopestVersions.Add(Snapshot)
             End If
             Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = TopestVersions}

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

@@ -1,9 +1,9 @@
 <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="PageDownloadLiteLoader"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadLiteLoader"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Orientation="Vertical" Margin="5">
             <local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="LiteLoader 简介" x:Name="CardTip">

+ 6 - 5
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml

@@ -1,9 +1,9 @@
 <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"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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">
@@ -57,6 +57,7 @@
                             <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">

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

@@ -120,7 +120,11 @@
     End Sub
 
     '版本选择
-    Private Sub TextSearchVersion_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSearchVersion.TextChanged
+    '#3067:当下拉菜单展开时,程序会被 WPF 挂起,因而无法更新 Grid 布局,所以必须延迟到下拉菜单收起后才能更新
+    Private Sub TextSearchVersion_TextChanged() Handles TextSearchVersion.TextChanged
+        If Not TextSearchVersion.IsDropDownOpen Then UpdateSearchLoaderVisibility()
+    End Sub
+    Private Sub UpdateSearchLoaderVisibility() Handles TextSearchVersion.DropDownClosed
         If TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w") Then
             ComboSearchLoader.Visibility = Visibility.Visible
             Grid.SetColumnSpan(TextSearchVersion, 1)

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

@@ -1,9 +1,9 @@
 <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="PageDownloadOptiFine"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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="PageDownloadOptiFine"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Orientation="Vertical" Margin="5">
             <local:MyCard Margin="20,20,20,0" Grid.IsSharedSizeScope="True" Title="OptiFine 简介" x:Name="CardTip">

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

@@ -1,9 +1,9 @@
 <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"
-             d:DesignWidth="778.571" d:DesignHeight="90.857">
+    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">

+ 90 - 86
Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb

@@ -145,26 +145,27 @@
             '由于 Abort 不是实时的,暂时不会释放文件,会导致删除报错,故只能取消执行
             Hint("有正在获取中的皮肤,请稍后再试!", HintType.Info)
         Else
-            RunInThread(Sub()
-                            Try
-                                Hint("正在刷新头像……")
-                                '清空缓存
-                                Log("[Skin] 正在清空皮肤缓存")
-                                If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
-                                If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
-                                IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
-                                IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
-                                IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
-                                IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
-                                '刷新控件
-                                For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
-                                    SkinLoader.WaitForExit(IsForceRestart:=True)
-                                Next
-                                Hint("已刷新头像!", HintType.Finish)
-                            Catch ex As Exception
-                                Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
-                            End Try
-                        End Sub)
+            RunInThread(
+            Sub()
+                Try
+                    Hint("正在刷新头像……")
+                    '清空缓存
+                    Log("[Skin] 正在清空皮肤缓存")
+                    If Directory.Exists(PathTemp & "Cache\Skin") Then DeleteDirectory(PathTemp & "Cache\Skin")
+                    If Directory.Exists(PathTemp & "Cache\Uuid") Then DeleteDirectory(PathTemp & "Cache\Uuid")
+                    IniClearCache(PathTemp & "Cache\Skin\IndexMs.ini")
+                    IniClearCache(PathTemp & "Cache\Skin\IndexNide.ini")
+                    IniClearCache(PathTemp & "Cache\Skin\IndexAuth.ini")
+                    IniClearCache(PathTemp & "Cache\Uuid\Mojang.ini")
+                    '刷新控件
+                    For Each SkinLoader In If(sender IsNot Nothing, {sender}, {PageLaunchLeft.SkinLegacy, PageLaunchLeft.SkinMs})
+                        SkinLoader.WaitForExit(IsForceRestart:=True)
+                    Next
+                    Hint("已刷新头像!", HintType.Finish)
+                Catch ex As Exception
+                    Log(ex, "刷新皮肤缓存失败", LogLevel.Msgbox)
+                End Try
+            End Sub)
         End If
     End Sub
     ''' <summary>
@@ -172,21 +173,22 @@
     ''' </summary>
     ''' <param name="SkinAddress">新的正版皮肤完整地址。</param>
     Public Shared Sub ReloadCache(SkinAddress As String)
-        RunInThread(Sub()
-                        Try
-                            '更新缓存
-                            WriteIni(PathTemp & "Cache\Skin\IndexMs.ini", Setup.Get("CacheMsUuid"), SkinAddress)
-                            Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("CacheMsUuid"), SkinAddress))
-                            '刷新控件
-                            For Each SkinLoader In {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}
-                                SkinLoader.WaitForExit(IsForceRestart:=True)
-                            Next
-                            '完成提示
-                            Hint("更改皮肤成功!", HintType.Finish)
-                        Catch ex As Exception
-                            Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
-                        End Try
-                    End Sub)
+        RunInThread(
+        Sub()
+            Try
+                '更新缓存
+                WriteIni(PathTemp & "Cache\Skin\IndexMs.ini", Setup.Get("CacheMsUuid"), SkinAddress)
+                Log(String.Format("[Skin] 已写入皮肤地址缓存 {0} -> {1}", Setup.Get("CacheMsUuid"), SkinAddress))
+                '刷新控件
+                For Each SkinLoader In {PageLaunchLeft.SkinMs, PageLaunchLeft.SkinLegacy}
+                    SkinLoader.WaitForExit(IsForceRestart:=True)
+                Next
+                '完成提示
+                Hint("更改皮肤成功!", HintType.Finish)
+            Catch ex As Exception
+                Log(ex, "更改正版皮肤后刷新皮肤失败", LogLevel.Feedback)
+            End Try
+        End Sub)
     End Sub
 
     '披风
@@ -216,58 +218,60 @@
         Hint("正在获取披风列表,请稍候……")
         IsChanging = True
         '开始实际获取
-        RunInNewThread(Sub()
-                           Try
+        RunInNewThread(
+        Sub()
+            Try
 Retry:
-                               '获取登录信息
-                               If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
-                               If McLoginMsLoader.State <> LoadState.Finished Then
-                                   Hint("登录失败,无法更改披风!", HintType.Critical)
-                                   Exit Sub
-                               End If
-                               Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
-                               Dim Uuid As String = McLoginMsLoader.Output.Uuid
-                               Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
-                               '获取玩家的所有披风
-                               Dim SelId As Integer? = Nothing
-                               RunInUiWait(Sub()
-                                               Try
-                                                   Dim CapeNames As New Dictionary(Of String, String) From {
-                                                       {"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
-                                                       {"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
-                                                       {"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
-                                                       {"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"},
-                                                       {"Cherry Blossom", "樱花披风"}
-                                                   }
-                                                   Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
-                                                   For Each Cape In SkinData("capes")
-                                                       Dim CapeName As String = Cape("alias").ToString
-                                                       If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
-                                                       SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
-                                                   Next
-                                                   SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
-                                               Catch ex As Exception
-                                                   Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
-                                               End Try
-                                           End Sub)
-                               If SelId Is Nothing Then Exit Sub
-                               '发送请求
-                               Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
-                                                                       If(SelId = 0, "DELETE", "PUT"),
-                                                                       If(SelId = 0, "", "{""capeId"": """ & SkinData("capes")(SelId - 1)("id").ToString & """}"),
-                                                                       "application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
-                               If Result.Contains("""errorMessage""") Then
-                                   Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
-                                   Exit Sub
-                               Else
-                                   Hint("更改披风成功!", HintType.Finish)
-                               End If
-                           Catch ex As Exception
-                               Log(ex, "更改披风失败", LogLevel.Hint)
-                           Finally
-                               IsChanging = False
-                           End Try
-                       End Sub, "Cape Change")
+                '获取登录信息
+                If McLoginMsLoader.State <> LoadState.Finished Then McLoginMsLoader.WaitForExit(PageLoginMsSkin.GetLoginData())
+                If McLoginMsLoader.State <> LoadState.Finished Then
+                    Hint("登录失败,无法更改披风!", HintType.Critical)
+                    Exit Sub
+                End If
+                Dim AccessToken As String = McLoginMsLoader.Output.AccessToken
+                Dim Uuid As String = McLoginMsLoader.Output.Uuid
+                Dim SkinData As JObject = GetJson(McLoginMsLoader.Output.ProfileJson)
+                '获取玩家的所有披风
+                Dim SelId As Integer? = Nothing
+                RunInUiWait(
+                Sub()
+                    Try
+                        Dim CapeNames As New Dictionary(Of String, String) From {
+                            {"Migrator", "迁移者披风"}, {"MapMaker", "Realms 地图制作者披风"}, {"Moderator", "Mojira 管理员披风"},
+                            {"Translator-Chinese", "Crowdin 中文翻译者披风"}, {"Translator", "Crowdin 翻译者披风"}, {"Cobalt", "Cobalt 披风"},
+                            {"Vanilla", "原版披风"}, {"Minecon2011", "Minecon 2011 参与者披风"}, {"Minecon2012", "Minecon 2012 参与者披风"},
+                            {"Minecon2013", "Minecon 2013 参与者披风"}, {"Minecon2015", "Minecon 2015 参与者披风"}, {"Minecon2016", "Minecon 2016 参与者披风"},
+                            {"Cherry Blossom", "樱花披风"}
+                        }
+                        Dim SelectionControl As New List(Of IMyRadio) From {New MyRadioBox With {.Text = "无披风"}}
+                        For Each Cape In SkinData("capes")
+                            Dim CapeName As String = Cape("alias").ToString
+                            If CapeNames.ContainsKey(CapeName) Then CapeName = CapeNames(CapeName)
+                            SelectionControl.Add(New MyRadioBox With {.Text = CapeName})
+                        Next
+                        SelId = MyMsgBoxSelect(SelectionControl, "选择披风", "确定", "取消")
+                    Catch ex As Exception
+                        Log(ex, "获取玩家皮肤列表失败", LogLevel.Feedback)
+                    End Try
+                End Sub)
+                If SelId Is Nothing Then Exit Sub
+                '发送请求
+                Dim Result As String = NetRequestRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
+                    If(SelId = 0, "DELETE", "PUT"),
+                    If(SelId = 0, "", New JObject(New JProperty("capeId", SkinData("capes")(SelId - 1)("id"))).ToString(0)),
+                    "application/json", Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
+                If Result.Contains("""errorMessage""") Then
+                    Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
+                    Exit Sub
+                Else
+                    Hint("更改披风成功!", HintType.Finish)
+                End If
+            Catch ex As Exception
+                Log(ex, "更改披风失败", LogLevel.Hint)
+            Finally
+                IsChanging = False
+            End Try
+        End Sub, "Cape Change")
     End Sub
 
 End Class

+ 5 - 6
Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml

@@ -1,10 +1,9 @@
 <local:MyPageRight x:Class="PageLaunchRight"
-             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">
+    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"> <!-- 不知道为啥只有这个文件不能在 XAML 设置 PanScroll -->
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
             <StackPanel x:Name="PanCustom">

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

@@ -2,6 +2,7 @@
 
     Private Sub Init() Handles Me.Loaded
         PanBack.ScrollToHome()
+        PanScroll = PanBack '不知道为啥不能在 XAML 设置
         PanLog.Visibility = If(ModeDebug, Visibility.Visible, Visibility.Collapsed)
         '快照版提示
 #If BETA Then

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

@@ -29,7 +29,7 @@
             </Grid.ColumnDefinitions>
             <local:MyTextButton Text="»  购买正版" EventType="打开网页" EventData="https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj"
                             Grid.Column="0" />
-            <local:MyTextButton Text="»  老账号迁移" x:Name="BtnMigration"
+            <local:MyTextButton Text="»  前往官网" EventType="打开网页" EventData="https://www.minecraft.net/zh-hans"
                             Grid.Column="2" />
         </Grid>
     </Grid>

+ 0 - 5
Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMs.xaml.vb

@@ -101,10 +101,5 @@
                            End Try
                        End Sub, "Ms Login")
     End Sub
-    '账号迁移入口
-    Private Sub BtnMigration_Click(sender As Object, e As EventArgs) Handles BtnMigration.Click
-        MyMsgBox($"请在接下来要打开的页面登录你的 Mojang 账号。{vbCrLf}在迁移时,你可能需要设置档案信息,请注意让年龄大于 18 岁,否则可能会导致无法登录!", "迁移提示", "继续", ForceWait:=True)
-        OpenWebsite("https://www.minecraft.net/zh-hans/login")
-    End Sub
 
 End Class

+ 5 - 5
Plain Craft Launcher 2/Pages/PageLink/PageLinkFeedback.xaml

@@ -1,9 +1,9 @@
 <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="PageLinkFeedback"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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="PageLinkFeedback"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
             <local:MyCard Title="关于" Margin="0,0,0,15">

+ 6 - 6
Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml

@@ -1,10 +1,10 @@
 <local:MyPageRight
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:System="clr-namespace:System;assembly=mscorlib" 
-             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="PageLinkIoi"
-             d:DesignWidth="800" d:DesignHeight="800">
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:System="clr-namespace:System;assembly=mscorlib" 
+    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="PageLinkIoi"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
             <StackPanel Orientation="Vertical" Margin="25,10">

+ 5 - 5
Plain Craft Launcher 2/Pages/PageOther/PageOtherHelp.xaml

@@ -1,9 +1,9 @@
 <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="PageOtherHelp"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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="PageOtherHelp"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <local:MyScrollViewer Visibility="Collapsed" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
             <StackPanel x:Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">

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

@@ -1,10 +1,10 @@
 <local:MyPageRight x:Class="PageOtherHelpDetail"
-             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">
+    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"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanCustom" Margin="25,25,25,10">
             <StackPanel.Resources>

+ 4 - 4
Plain Craft Launcher 2/Pages/PageSelectRight.xaml

@@ -1,9 +1,9 @@
 <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="PageSelectRight"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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="PageSelectRight"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <Grid x:Name="PanAllBack">
             <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">

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

@@ -1,10 +1,10 @@
 <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" 
-             xmlns:System="clr-namespace:System;assembly=mscorlib" 
-             mc:Ignorable="d" x:Class="PageSetupLaunch"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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" 
+    xmlns:System="clr-namespace:System;assembly=mscorlib" 
+    mc:Ignorable="d" x:Class="PageSetupLaunch"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,10">
             <local:MyCard x:Name="CardSkin" Margin="0,15" Title="离线皮肤">

+ 5 - 5
Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml

@@ -1,9 +1,9 @@
 <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="PageSetupLink"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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="PageSetupLink"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,10">
             <local:MyCard Margin="0,15" Title="HiPer">

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

@@ -1,10 +1,10 @@
 <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" 
-             xmlns:sys="clr-namespace:System;assembly=mscorlib"
-             mc:Ignorable="d" x:Class="PageSetupSystem"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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" 
+    xmlns:sys="clr-namespace:System;assembly=mscorlib"
+    mc:Ignorable="d" x:Class="PageSetupSystem"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,10">
 
@@ -52,9 +52,9 @@
                         <local:MyCheckBox Margin="0,5,0,4" Text="安装整合包时保留原始压缩包" Grid.Row="7" Height="22" Grid.ColumnSpan="2"
                                           x:Name="CheckDownloadKeepModpack" Tag="ToolDownloadKeepModpack" 
                                           ToolTip="安装整合包时,在版本文件夹下保留用于安装的原始整合包压缩包文件" />
-                        <local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器需求(忽略 Quilt)" Grid.Row="8" Height="22" Grid.ColumnSpan="2"
+                        <local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器显示" Grid.Row="8" Height="22" Grid.ColumnSpan="2"
                                           x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt" 
-                                          ToolTip="当 Mod 支持 Forge、Fabric 但不支持 Quilt 时,不显示它的 Mod 加载器需求。&#xa;例如,关闭时 JEI 会显示需要 Forge / Fabric 1.8+,开启后 JEI 仅显示需要 1.8+。" />
+                                          ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。&#xa;例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" />
                     </Grid>
                 </StackPanel>
             </local:MyCard>

+ 5 - 5
Plain Craft Launcher 2/Pages/PageSpeedRight.xaml

@@ -1,9 +1,9 @@
 <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="PageSpeedRight"
-             d:DesignWidth="800" d:DesignHeight="800">
+     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="PageSpeedRight"
+     PanScroll="{Binding ElementName=PanBack}">
      <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,25,25,10" Grid.IsSharedSizeScope="True">
             <!--<local:MyCard Title="UI 的示例下载任务">

+ 8 - 11
Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml

@@ -1,9 +1,9 @@
 <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="PageVersionMod"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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="PageVersionMod"
+    PanScroll="{Binding ElementName=PanBack}">
     <Grid>
         <Grid x:Name="PanAllBack">
             <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
@@ -23,12 +23,9 @@
                             <local:MyButton x:Name="BtnManageCheck" Grid.Column="3" MinWidth="140" Text="检查 Mod" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ToolTip="快速检查 Mod 的版本要求、重复、前置缺失等问题。&#xa;结果仅供参考,由于部分 Mod 标注的信息有误,可能并不准确。" Visibility="Collapsed" />
                         </Grid>
                     </local:MyCard>
-                    <Grid>
-                        <local:MyCard x:Name="PanSearch" Visibility="Collapsed" VerticalAlignment="Top" Opacity="0" Margin="0,0,0,15" Title=" " MinHeight="40">
-                            <StackPanel Margin="20,40,18,18" Name="PanSearchList" VerticalAlignment="Top" />
-                        </local:MyCard>
-                        <StackPanel Name="PanList" />
-                    </Grid>
+                    <local:MyCard x:Name="PanListBack" VerticalAlignment="Top" Opacity="0" Margin="0,0,0,15" Title=" " MinHeight="40">
+                        <StackPanel Margin="20,38,18,18" Name="PanList" VerticalAlignment="Top" />
+                    </local:MyCard>
                 </StackPanel>
             </local:MyScrollViewer>
             <local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40" x:Name="PanEmpty">

+ 81 - 124
Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb

@@ -34,7 +34,7 @@
     End Sub
 
     Private Sub LoaderInit() Handles Me.Initialized
-        PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf Load_Finish, AutoRun:=False)
+        PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf LoadUIFromLoaderOutput, AutoRun:=False)
     End Sub
     Private Sub Load_Click(sender As Object, e As MouseButtonEventArgs) Handles Load.Click
         If McModLoader.State = LoadState.Failed Then
@@ -46,55 +46,36 @@
 
 #Region "UI 化"
 
-    Private PanItems As StackPanel
     ''' <summary>
-    ''' 将 Mod 列表加载为 UI。
+    ''' 已加载的 Mod UI 缓存,不确保按显示顺序排列。Key 为 Mod 的 RawFileName。
     ''' </summary>
-    Private Sub Load_Finish(Loader As LoaderTask(Of String, List(Of McMod)))
-        Dim List As List(Of McMod) = Loader.Output
+    Public ModItems As New Dictionary(Of String, MyLocalModItem)
+    ''' <summary>
+    ''' 将加载器结果的 Mod 列表加载为 UI。
+    ''' </summary>
+    Private Sub LoadUIFromLoaderOutput()
+        Dim Mods As List(Of McMod) = McModLoader.Output
         Try
-            PanList.Children.Clear()
-
             '判断应该显示哪一个页面
-            If List.Count = 0 Then
+            If Mods.Any() Then
+                PanBack.Visibility = Visibility.Visible
+                PanEmpty.Visibility = Visibility.Collapsed
+            Else
                 PanEmpty.Visibility = Visibility.Visible
                 PanBack.Visibility = Visibility.Collapsed
                 Exit Sub
-            Else
-                PanBack.Visibility = Visibility.Visible
-                PanEmpty.Visibility = Visibility.Collapsed
             End If
-
             SearchBox.Text = ""
-            '建立 StackPanel
-            PanItems = New StackPanel With {.Margin = New Thickness(20, 38, 18, If(List.Count > 0, 20, 0)), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
-            For Each ModEntity As McMod In List
-                PanItems.Children.Add(McModListItem(ModEntity))
+            '输出结果
+            ModItems.Clear()
+            For Each ModEntity As McMod In Mods
+                ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
             Next
-            '建立 MyCard
-            Dim NewCard As New MyCard With {.Title = McModGetTitle(List), .Margin = New Thickness(0, 0, 0, 15)}
-            NewCard.Children.Add(PanItems)
-            PanList.Children.Add(NewCard)
-
+            RefreshResult(Mods)
         Catch ex As Exception
             Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
         End Try
     End Sub
-    ''' <summary>
-    ''' 获取 Card 的标题。
-    ''' </summary>
-    Private Function McModGetTitle(List As List(Of McMod)) As String
-        Dim Counter = {0, 0, 0}
-        For Each ModEntity As McMod In List
-            Counter(ModEntity.State) += 1
-        Next
-        If List.Count = 0 Then Return "未找到任何 Mod"
-        Dim TypeList As New List(Of String)
-        If Counter(McMod.McModState.Fine) > 0 Then TypeList.Add("启用 " & Counter(McMod.McModState.Fine))
-        If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
-        If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
-        Return "Mod 列表(" & Join(TypeList, ",") & ")"
-    End Function
     Private Function McModListItem(Entry As McMod) As MyLocalModItem
         AniControlEnabled += 1
         Dim NewItem As New MyLocalModItem With {.SnapsToDevicePixels = True, .Entry = Entry,
@@ -109,26 +90,26 @@
         AddHandler sender.Changed, AddressOf CheckChanged
         AddHandler sender.Click, Sub(ss As MyLocalModItem, ee As EventArgs) ss.Checked = Not ss.Checked
         '图标按钮
-        Dim BtnOpen As New MyIconButton With {.LogoScale = 1.15, .Logo = Logo.IconButtonOpen, .Tag = sender}
+        Dim BtnOpen As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonOpen, .Tag = sender}
         BtnOpen.ToolTip = "打开文件位置"
         ToolTipService.SetPlacement(BtnOpen, Primitives.PlacementMode.Center)
         ToolTipService.SetVerticalOffset(BtnOpen, 30)
         ToolTipService.SetHorizontalOffset(BtnOpen, 2)
         AddHandler BtnOpen.Click, AddressOf Open_Click
-        Dim BtnCont As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .Tag = sender}
+        Dim BtnCont As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonInfo, .Tag = sender}
         BtnCont.ToolTip = "详情"
         ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
         ToolTipService.SetVerticalOffset(BtnCont, 30)
         ToolTipService.SetHorizontalOffset(BtnCont, 2)
         AddHandler BtnCont.Click, AddressOf Info_Click
         AddHandler sender.MouseRightButtonDown, AddressOf Info_Click
-        Dim BtnDelete As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete, .Tag = sender}
+        Dim BtnDelete As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonDelete, .Tag = sender}
         BtnDelete.ToolTip = "删除"
         ToolTipService.SetPlacement(BtnDelete, Primitives.PlacementMode.Center)
         ToolTipService.SetVerticalOffset(BtnDelete, 30)
         ToolTipService.SetHorizontalOffset(BtnDelete, 2)
         AddHandler BtnDelete.Click, AddressOf Delete_Click
-        Dim BtnED As New MyIconButton With {.LogoScale = 1.1, .Logo = If(sender.Entry.State = McMod.McModState.Fine,
+        Dim BtnED As New MyIconButton With {.LogoScale = 1, .Logo = If(sender.Entry.State = McMod.McModState.Fine,
             "M508 990.4c-261.6 0-474.4-212-474.4-474.4S246.4 41.6 508 41.6s474.4 212 474.4 474.4S769.6 990.4 508 990.4zM508 136.8c-209.6 0-379.2 169.6-379.2 379.2 0 209.6 169.6 379.2 379.2 379.2s379.2-169.6 379.2-379.2C887.2 306.4 717.6 136.8 508 136.8zM697.6 563.2 318.4 563.2c-26.4 0-47.2-21.6-47.2-47.2 0-26.4 21.6-47.2 47.2-47.2l379.2 0c26.4 0 47.2 21.6 47.2 47.2C744.8 542.4 724 563.2 697.6 563.2z",
             "M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 921.6a409.6 409.6 0 1 1 409.6-409.6 409.6 409.6 0 0 1-409.6 409.6z M716.8 339.968l-256 253.44L328.192 460.8A51.2 51.2 0 0 0 256 532.992l168.448 168.96a51.2 51.2 0 0 0 72.704 0l289.28-289.792A51.2 51.2 0 0 0 716.8 339.968z"),
             .Tag = sender}
@@ -144,6 +125,39 @@
         End If
     End Sub
 
+    ''' <summary>
+    ''' 刷新结果显示。
+    ''' </summary>
+    Private Sub RefreshResult(Mods As List(Of McMod))
+        PanList.Children.Clear()
+        For Each TargetMod In Mods
+            PanList.Children.Add(ModItems(TargetMod.RawFileName))
+        Next
+        RefreshTitle()
+    End Sub
+    ''' <summary>
+    ''' 刷新卡片标题。
+    ''' </summary>
+    Private Sub RefreshTitle()
+        Dim Mods = PanList.Children.Cast(Of MyLocalModItem).Select(Function(i) i.Entry).ToList
+        Dim Counter = {0, 0, 0}
+        For Each ModEntity As McMod In Mods
+            Counter(ModEntity.State) += 1
+        Next
+        Dim TypeList As New List(Of String)
+        If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
+        If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
+        If Counter(McMod.McModState.Fine) > 0 Then TypeList.Insert(0, If(TypeList.Any, "启用 ", "") & Counter(McMod.McModState.Fine))
+        If Not IsSearching Then
+            PanListBack.Title = "Mod 列表 (" & Join(TypeList, ",") & ")"
+        ElseIf TypeList.Any() Then
+            PanListBack.Title = "搜索结果 (" & Join(TypeList, ",") & ")"
+        Else
+            PanListBack.Title = "无搜索结果"
+        End If
+        PanList.Visibility = If(Mods.Any(), Visibility.Visible, Visibility.Collapsed)
+    End Sub
+
 #End Region
 
 #Region "管理"
@@ -182,7 +196,7 @@
     ''' 全选。
     ''' </summary>
     Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
-        If SelectedMods.Count < If(IsSearching, PanSearchList.Children.Count, McModLoader.Output.Count) Then
+        If SelectedMods.Count < PanList.Children.Count Then
             ChangeAllSelected(True)
         Else
             ChangeAllSelected(False)
@@ -289,28 +303,10 @@
     Private Sub ChangeAllSelected(Value As Boolean)
         AniControlEnabled += 1
         SelectedMods.Clear()
-        If IsSearching Then
-            '搜索中
-            For Each Item As MyLocalModItem In PanSearchList.Children
-                Item.Checked = Value
-                If Value Then SelectedMods.Add(Item.Entry.RawFileName)
-            Next
-            If Not Value Then '只取消选择
-                If PanItems IsNot Nothing Then
-                    For Each Item As MyLocalModItem In PanItems.Children
-                        Item.Checked = Value
-                    Next
-                End If
-            End If
-        Else
-            '非搜索中
-            If PanItems IsNot Nothing Then
-                For Each Item As MyLocalModItem In PanItems.Children
-                    Item.Checked = Value
-                    If Value Then SelectedMods.Add(Item.Entry.RawFileName)
-                Next
-            End If
-        End If
+        For Each Item As MyLocalModItem In PanList.Children
+            Item.Checked = Value
+            If Value Then SelectedMods.Add(Item.Entry.RawFileName)
+        Next
         AniControlEnabled -= 1
         '更新下边栏 UI
         RefreshBottomBar()
@@ -331,13 +327,13 @@
 
     '启用 / 禁用
     Private Sub BtnSelectED_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
-        EDMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList(),
+        EDMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)),
                Not sender.Equals(BtnSelectDisable))
         ChangeAllSelected(False)
     End Sub
-    Private Sub EDMods(ModList As List(Of McMod), IsEnable As Boolean)
+    Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
         Dim IsSuccessful As Boolean = True
-        For Each ModEntity In ModList
+        For Each ModEntity In ModList.ToList
             Dim NewPath As String = Nothing
             If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
                 '禁用
@@ -361,24 +357,21 @@
                 Log(ex, $"重命名 Mod 失败({If(ModEntity.Path, "null")})")
                 IsSuccessful = False
             End Try
-            '更改 Loader 和 UI 中的列表
+            '更改 Loader 中的列表
             Dim NewModEntity As New McMod(NewPath)
             NewModEntity.Comp = ModEntity.Comp
             Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
             McModLoader.Output.RemoveAt(IndexOfLoader)
             McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
-            Dim List = If(IsSearching, PanSearchList, PanItems).Children
-            Dim IndexOfUi As Integer = List.IndexOf(List.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry Is ModEntity))
+            '更改 UI 中的列表
+            Dim NewItem As MyLocalModItem = McModListItem(NewModEntity)
+            ModItems(ModEntity.RawFileName) = NewItem
+            Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry Is ModEntity))
             If IndexOfUi = -1 Then Continue For '因为未知原因 Mod 的状态已经切换完了
-            List.RemoveAt(IndexOfUi)
-            List.Insert(IndexOfUi, McModListItem(NewModEntity))
+            PanList.Children.RemoveAt(IndexOfUi)
+            PanList.Children.Insert(IndexOfUi, NewItem)
         Next
-        If Not IsSearching Then
-            '改变禁用数量的显示
-            CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
-            '更新加载器状态
-            LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
-        End If
+        RefreshTitle() '改变数量显示
         If Not IsSuccessful Then
             Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
             RefreshList(True)
@@ -416,21 +409,16 @@
                 SelectedMods.Remove(ModEntity.RawFileName)
                 '更改 Loader 和 UI 中的列表
                 McModLoader.Output.Remove(ModEntity)
-                Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
-                Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyLocalModItem).First(Function(i) i.Entry Is ModEntity))
-                Parent.Children.RemoveAt(IndexOfUi)
+                ModItems.Remove(ModEntity.RawFileName)
+                Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).First(Function(i) i.Entry Is ModEntity))
+                PanList.Children.RemoveAt(IndexOfUi)
             Next
-            If Not IsSearching Then
-                '改变禁用数量的显示
-                CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
-                '更新加载器状态
-                LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
-            End If
+            RefreshTitle()
             If Not IsSuccessful Then
                 Hint("由于文件被占用,Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
                 RefreshList(True)
-            ElseIf If(IsSearching, PanSearchList, PanItems).Children.Count = 0 Then
-                RefreshList(True)
+            ElseIf PanList.Children.Count = 0 Then
+                RefreshList(True) '删除了全部文件
             Else
                 RefreshBottomBar()
             End If
@@ -553,7 +541,7 @@
     '启用 / 禁用
     Public Sub ED_Click(sender As MyIconButton, e As EventArgs)
         Dim ListItem As MyLocalModItem = sender.Tag
-        EDMods(New List(Of McMod) From {ListItem.Entry}, ListItem.Entry.State = McMod.McModState.Disabled)
+        EDMods({ListItem.Entry}, ListItem.Entry.State = McMod.McModState.Disabled)
     End Sub
 
 #End Region
@@ -588,43 +576,12 @@
                 End If
                 QueryList.Add(New SearchEntry(Of McMod) With {.Item = Entry, .SearchSource = SearchSource})
             Next
-            '进行搜索,构造列表
+            '进行搜索
             Dim SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35)
-            PanSearchList.Children.Clear()
-            If SearchResult.Count = 0 Then
-                PanSearch.Title = "无搜索结果"
-                PanSearchList.Visibility = Visibility.Collapsed
-            Else
-                PanSearch.Title = "搜索结果"
-                For Each Result In SearchResult
-                    Dim Item = McModListItem(Result.Item)
-                    If ModeDebug Then Item.Description = If(Result.AbsoluteRight, "完全匹配,", "") & "相似度:" & Math.Round(Result.Similarity, 3) & "," & Item.Description
-                    PanSearchList.Children.Add(Item)
-                Next
-                PanSearchList.Visibility = Visibility.Visible
-            End If
-            '显示
-            AniStart({
-                     AaOpacity(PanList, -PanList.Opacity, 100),
-                     AaCode(Sub()
-                                PanList.Visibility = Visibility.Collapsed
-                                PanSearch.Visibility = Visibility.Visible
-                                PanSearch.TriggerForceResize()
-                            End Sub,, True),
-                     AaOpacity(PanSearch, 1 - PanSearch.Opacity, 200, 60)
-                }, "FrmVersionMod Search Switch", True)
+            RefreshResult(SearchResult.Select(Function(r) r.Item).ToList)
         Else
-            '隐藏
-            LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.RunOnUpdated)
-            AniStart({
-                     AaOpacity(PanSearch, -PanSearch.Opacity, 100),
-                     AaCode(Sub()
-                                PanSearch.Height = 0
-                                PanSearch.Visibility = Visibility.Collapsed
-                                PanList.Visibility = Visibility.Visible
-                            End Sub,, True),
-                     AaOpacity(PanList, 1 - PanList.Opacity, 150, 30)
-                }, "FrmVersionMod Search Switch", True)
+            '退出搜索状态
+            RefreshResult(McModLoader.Output)
         End If
     End Sub
 

+ 6 - 6
Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml

@@ -1,10 +1,10 @@
 <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" 
-             xmlns:System="clr-namespace:System;assembly=mscorlib" 
-             mc:Ignorable="d" x:Class="PageVersionOverall"
-             d:DesignWidth="800" d:DesignHeight="800" Grid.IsSharedSizeScope="True">
+    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" 
+    xmlns:System="clr-namespace:System;assembly=mscorlib" 
+    mc:Ignorable="d" x:Class="PageVersionOverall"
+    PanScroll="{Binding ElementName=PanBack}" Grid.IsSharedSizeScope="True">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,10">
             <local:MyCard Margin="0,15" Title="">

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

@@ -1,10 +1,10 @@
 <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" 
-             xmlns:System="clr-namespace:System;assembly=mscorlib" 
-             mc:Ignorable="d" x:Class="PageVersionSetup"
-             d:DesignWidth="800" d:DesignHeight="800">
+    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" 
+    xmlns:System="clr-namespace:System;assembly=mscorlib" 
+    mc:Ignorable="d" x:Class="PageVersionSetup"
+    PanScroll="{Binding ElementName=PanBack}">
     <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
         <StackPanel x:Name="PanMain" Margin="25,10">
             <local:MyHint Text="本页面的设置均只对当前版本生效,这对整合包制作应该会有所帮助。&#xa;你也可以在设置页面中修改对全部版本生效的全局设置。" IsWarn="False" Margin="0,15" />