龙腾猫跃 vor 1 Jahr
Ursprung
Commit
add4153067
52 geänderte Dateien mit 1650 neuen und 1954 gelöschten Zeilen
  1. 5 1
      Plain Craft Launcher 2/App.config
  2. 20 44
      Plain Craft Launcher 2/Application.xaml.vb
  3. 33 35
      Plain Craft Launcher 2/FormMain.xaml.vb
  4. 29 13
      Plain Craft Launcher 2/Modules/Base/ModBase.vb
  5. 1 1
      Plain Craft Launcher 2/Modules/Base/ModLoader.vb
  6. 411 776
      Plain Craft Launcher 2/Modules/Base/ModNet.vb
  7. 32 20
      Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
  8. 2 2
      Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
  9. 22 59
      Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
  10. 10 8
      Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
  11. 75 56
      Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
  12. 108 86
      Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
  13. 13 14
      Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
  14. 1 1
      Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
  15. 1 4
      Plain Craft Launcher 2/Modules/ModSecret.vb
  16. 0 59
      Plain Craft Launcher 2/Modules/Reference/CookieWebClient.vb
  17. 22 23
      Plain Craft Launcher 2/Modules/ThirdParty/DragHelper.vb
  18. 2 2
      Plain Craft Launcher 2/My Project/AssemblyInfo.vb
  19. 53 13
      Plain Craft Launcher 2/My Project/Resources.Designer.vb
  20. 19 7
      Plain Craft Launcher 2/My Project/Resources.resx
  21. 1 1
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml
  22. 0 1
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageComp.xaml.vb
  23. 10 15
      Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb
  24. 30 27
      Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
  25. 1 2
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml
  26. 10 9
      Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
  27. 11 9
      Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb
  28. 6 5
      Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb
  29. 5 3
      Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
  30. 1 1
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml
  31. 1 0
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginLegacy.xaml.vb
  32. 4 2
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml
  33. 1 1
      Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb
  34. 491 468
      Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
  35. 1 1
      Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb
  36. 3 3
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
  37. 1 1
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
  38. 6 14
      Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb
  39. 4 4
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml.vb
  40. 11 5
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
  41. 25 33
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb
  42. 2 2
      Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml
  43. 45 6
      Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
  44. BIN
      Plain Craft Launcher 2/Resources/CacheCow.Client.FileStore.dll
  45. BIN
      Plain Craft Launcher 2/Resources/CacheCow.Client.dll
  46. BIN
      Plain Craft Launcher 2/Resources/CacheCow.Common.dll
  47. 4 0
      Plain Craft Launcher 2/Resources/Custom.xaml
  48. BIN
      Plain Craft Launcher 2/Resources/Help.zip
  49. 117 117
      Plain Craft Launcher 2/Resources/ModData.txt
  50. BIN
      Plain Craft Launcher 2/Resources/Newtonsoft.Json.dll
  51. BIN
      Plain Craft Launcher 2/Resources/System.Net.Http.Formatting.dll
  52. BIN
      Plain Craft Launcher 2/Resources/java-wrapper.jar

+ 5 - 1
Plain Craft Launcher 2/App.config

@@ -30,6 +30,10 @@
                   <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
                   <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
                   <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
                   <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
             </dependentAssembly>
             </dependentAssembly>
+			<dependentAssembly>
+				<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
+				<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="13.0.0.0"/>
+			</dependentAssembly>
         </assemblyBinding>
         </assemblyBinding>
     </runtime>
     </runtime>
-</configuration>
+</configuration>

+ 20 - 44
Plain Craft Launcher 2/Application.xaml.vb

@@ -181,53 +181,29 @@ WaitRetry:
     End Sub
     End Sub
 
 
     '动态 DLL 调用
     '动态 DLL 调用
-    Private Shared AssemblyNAudio As Assembly
-    Private Shared AssemblyJson As Assembly
-    Private Shared AssemblyDialog As Assembly
-    Private Shared AssemblyImazenWebp As Assembly
-    Private Shared ReadOnly AssemblyNAudioLock As New Object
-    Private Shared ReadOnly AssemblyJsonLock As New Object
-    Private Shared ReadOnly AssemblyDialogLock As New Object
-    Private Shared ReadOnly AssemblyImazenWebpLock As New Object
     Private Declare Function SetDllDirectory Lib "kernel32" Alias "SetDllDirectoryA" (lpPathName As String) As Boolean
     Private Declare Function SetDllDirectory Lib "kernel32" Alias "SetDllDirectoryA" (lpPathName As String) As Boolean
-    Public Shared Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
-        If args.Name.StartsWithF("NAudio") Then
-            SyncLock AssemblyNAudioLock
-                If AssemblyNAudio Is Nothing Then
-                    Log("[Start] 加载 DLL:NAudio")
-                    AssemblyNAudio = Assembly.Load(GetResources("NAudio"))
-                End If
-                Return AssemblyNAudio
-            End SyncLock
-        ElseIf args.Name.StartsWithF("Newtonsoft.Json") Then
-            SyncLock AssemblyJsonLock
-                If AssemblyJson Is Nothing Then
-                    Log("[Start] 加载 DLL:Json")
-                    AssemblyJson = Assembly.Load(GetResources("Json"))
-                End If
-                Return AssemblyJson
-            End SyncLock
-        ElseIf args.Name.StartsWithF("Ookii.Dialogs.Wpf") Then
-            SyncLock AssemblyDialogLock
-                If AssemblyDialog Is Nothing Then
-                    Log("[Start] 加载 DLL:Dialogs")
-                    AssemblyDialog = Assembly.Load(GetResources("Dialogs"))
-                End If
-                Return AssemblyDialog
-            End SyncLock
-        ElseIf args.Name.StartsWithF("Imazen.WebP") Then
-            SyncLock AssemblyImazenWebpLock
-                If AssemblyImazenWebp Is Nothing Then
-                    Log("[Start] 加载 DLL:Imazen.WebP")
-                    AssemblyImazenWebp = Assembly.Load(GetResources("Imazen_WebP"))
-                    SetDllDirectory(PathPure.TrimEnd("\"))
+    Public Shared Function AssemblyResolve(sender As Object, Args As ResolveEventArgs) As Assembly
+        '缓存
+        Static Prefixes As String() = {"NAudio", "Newtonsoft.Json", "Ookii.Dialogs.Wpf", "Imazen.WebP", "CacheCow.Common", "CacheCow.Client.FileStore", "CacheCow.Client", "System.Net.Http.Formatting"}
+        Static Locks As New Dictionary(Of String, Object)(StringComparer.Ordinal)
+        Static LoadedAssembly As New Dictionary(Of String, Assembly)(StringComparer.Ordinal)
+        '查找对应的 DLL
+        Dim Prefix As String = Prefixes.FirstOrDefault(Function(p) Args.Name.StartsWithF(p))
+        If Prefix Is Nothing Then Return Nothing
+        '加载 DLL
+        If Not Locks.ContainsKey(Prefix) Then Locks(Prefix) = New Object()
+        SyncLock Locks(Prefix)
+            If Not LoadedAssembly.ContainsKey(Prefix) Then
+                Log($"[Start] 加载 DLL:{Prefix}")
+                LoadedAssembly(Prefix) = Assembly.Load(GetResources(Prefix))
+                'WebP 特判
+                If Prefix = "Imazen.WebP" Then
+                    SetDllDirectory(PathPure.TrimEnd("\"c))
                     WriteFile(PathPure & "libwebp.dll", GetResources("libwebp64"))
                     WriteFile(PathPure & "libwebp.dll", GetResources("libwebp64"))
                 End If
                 End If
-                Return AssemblyImazenWebp
-            End SyncLock
-        Else
-            Return Nothing
-        End If
+            End If
+            Return LoadedAssembly(Prefix)
+        End SyncLock
     End Function
     End Function
 
 
     '切换窗口
     '切换窗口

+ 33 - 35
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -83,6 +83,13 @@ Public Class FormMain
         '3:BUG+ IMP* FEAT-
         '3:BUG+ IMP* FEAT-
         '2:BUG* IMP-
         '2:BUG* IMP-
         '1:BUG-
         '1:BUG-
+        If LastVersion < 363 Then 'Snapshot 2.10.4
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化:下载资源时,会单独记忆每种资源上次下载到的文件夹,以防混淆"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:网络底层框架与下载稳定性"))
+            FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法启动一部分 LabyMod 和 GTNH 客户端"))
+            FeatureCount += 22
+            BugCount += 26
+        End If
         If LastVersion < 362 Then 'Snapshot 2.10.3
         If LastVersion < 362 Then 'Snapshot 2.10.3
             FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包"))
             FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复:无法安装部分使用老版本 PCL 导出的整合包"))
         End If
         End If
@@ -204,8 +211,6 @@ Public Class FormMain
         End Sub, "UpdateLog Output")
         End Sub, "UpdateLog Output")
     End Sub
     End Sub
 
 
-    Private ReadOnly Helper As New DragFileHelper()
-
     '窗口加载
     '窗口加载
     Private IsWindowLoadFinished As Boolean = False
     Private IsWindowLoadFinished As Boolean = False
     Public Sub New()
     Public Sub New()
@@ -228,7 +233,7 @@ Public Class FormMain
             If Not Setup.IsUnset("LaunchArgumentIndie") Then
             If Not Setup.IsUnset("LaunchArgumentIndie") Then
                 Log("[Start] 从老 PCL 迁移版本隔离")
                 Log("[Start] 从老 PCL 迁移版本隔离")
                 Setup.Set("LaunchArgumentIndieV2", Setup.Get("LaunchArgumentIndie"))
                 Setup.Set("LaunchArgumentIndieV2", Setup.Get("LaunchArgumentIndie"))
-            ElseIf Not Setup.IsUnset("WindowHeight") Then
+            ElseIf Not Setup.IsUnset("LaunchVersionSelect") Then
                 Log("[Start] 从老 PCL 升级,但此前未调整版本隔离,使用老的版本隔离默认值")
                 Log("[Start] 从老 PCL 升级,但此前未调整版本隔离,使用老的版本隔离默认值")
                 Setup.Set("LaunchArgumentIndieV2", Setup.GetDefault("LaunchArgumentIndie"))
                 Setup.Set("LaunchArgumentIndieV2", Setup.GetDefault("LaunchArgumentIndie"))
             Else
             Else
@@ -244,36 +249,24 @@ Public Class FormMain
         [AddHandler](DragDrop.DragOverEvent, New DragEventHandler(AddressOf HandleDrag), handledEventsToo:=True)
         [AddHandler](DragDrop.DragOverEvent, New DragEventHandler(AddressOf HandleDrag), handledEventsToo:=True)
         '加载 UI
         '加载 UI
         InitializeComponent()
         InitializeComponent()
-
-
-
         Opacity = 0
         Opacity = 0
-        '旧代码
-        ''开启管理员权限下的文件拖拽,但下列代码也没用(#2531)
-        'If IsAdmin() Then
-        '    Log("[Start] PCL 正以管理员权限运行")
-        '    ChangeWindowMessageFilter(&H233, 1)
-        '    ChangeWindowMessageFilter(&H4A, 1)
-        '    ChangeWindowMessageFilter(&H49, 1)
-        'End If
-
         '开启管理员权限下的文件拖拽
         '开启管理员权限下的文件拖拽
         If IsAdmin() Then
         If IsAdmin() Then
-            AddHandler Me.SourceInitialized,
-            Sub(sender, e)
-                Dim wpfHelper As New WindowInteropHelper(Me)
-                Helper.HwndIntPtrSource = HwndSource.FromHwnd(wpfHelper.Handle)
+            Static Helper As New DragHelper
+            AddHandler SourceInitialized,
+            Sub()
+                Dim WpfHelper As New WindowInteropHelper(Me)
+                Helper.HwndIntPtrSource = HwndSource.FromHwnd(WpfHelper.Handle)
                 Helper.AddHook()
                 Helper.AddHook()
             End Sub
             End Sub
-            AddHandler Me.Closing,
-            Sub(sender, e)
-                Helper.RemoveDragHook()
-            End Sub
-            AddHandler Helper.DragDrop,
-            Sub(sender, e)
-                Me.FileDrag(Helper.DropFilePaths)
-            End Sub
+            AddHandler Closing, Sub() Helper.RemoveDragHook()
+            AddHandler Helper.DragDrop, Sub() FileDrag(Helper.DropFilePaths)
         End If
         End If
+        'If IsAdmin() Then '下列代码没用(#2531)
+        '    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(FrmLaunchLeft.Parent) Then FrmLaunchLeft.SetValue(ContentPresenter.ContentProperty, Nothing)
         If Not IsNothing(FrmLaunchRight.Parent) Then FrmLaunchRight.SetValue(ContentPresenter.ContentProperty, Nothing)
         If Not IsNothing(FrmLaunchRight.Parent) Then FrmLaunchRight.SetValue(ContentPresenter.ContentProperty, Nothing)
@@ -390,7 +383,7 @@ Public Class FormMain
             Catch ex As Exception
             Catch ex As Exception
                 Log(ex, "清理自动更新文件失败")
                 Log(ex, "清理自动更新文件失败")
             End Try
             End Try
-        End Sub, "Start Loader", ThreadPriority.Lowest)
+        End Sub, "初始化", ThreadPriority.Lowest)
 
 
         Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
         Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
     End Sub
     End Sub
@@ -616,11 +609,12 @@ Public Class FormMain
     '按键事件
     '按键事件
     Private Sub FormMain_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
     Private Sub FormMain_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
         If e.IsRepeat Then Return
         If e.IsRepeat Then Return
-        '调用弹窗:回车选择第一个,Esc 选择最后一个
-        If PanMsg.Children.Count > 0 Then
+        '修复按下 Alt 后误认为弹出系统菜单导致的冻结
+        If e.SystemKey = Key.LeftAlt OrElse e.SystemKey = Key.RightAlt Then e.Handled = True
+        '在有弹窗时:回车选择第一个,Esc 选择最后一个
+        If PanMsg.Children.Any Then
             If e.Key = Key.Enter Then
             If e.Key = Key.Enter Then
                 CType(PanMsg.Children(0), Object).Btn1_Click()
                 CType(PanMsg.Children(0), Object).Btn1_Click()
-                Return
             ElseIf e.Key = Key.Escape Then
             ElseIf e.Key = Key.Escape Then
                 Dim Msg As Object = PanMsg.Children(0)
                 Dim Msg As Object = PanMsg.Children(0)
                 If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
                 If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
@@ -630,9 +624,14 @@ Public Class FormMain
                 Else
                 Else
                     Msg.Btn1_Click()
                     Msg.Btn1_Click()
                 End If
                 End If
-                Return
             End If
             End If
+            Return
         End If
         End If
+
+        '==========================
+        ' 在没有弹窗时:继续检查……
+        '==========================
+
         '按 ESC 返回上一级
         '按 ESC 返回上一级
         If e.Key = Key.Escape Then TriggerPageBack()
         If e.Key = Key.Escape Then TriggerPageBack()
         '更改隐藏版本可见性
         '更改隐藏版本可见性
@@ -666,8 +665,6 @@ Public Class FormMain
                 FrmLaunchLeft.LaunchButtonClick()
                 FrmLaunchLeft.LaunchButtonClick()
             End If
             End If
         End If
         End If
-        '修复按下 Alt 后误认为弹出系统菜单导致的冻结
-        If e.SystemKey = Key.LeftAlt OrElse e.SystemKey = Key.RightAlt Then e.Handled = True
     End Sub
     End Sub
     Private Sub FormMain_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseDown
     Private Sub FormMain_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseDown
         '鼠标侧键返回上一级
         '鼠标侧键返回上一级
@@ -732,6 +729,7 @@ Public Class FormMain
     End Sub
     End Sub
     Private Sub FrmMain_Drop(sender As Object, e As DragEventArgs) Handles Me.Drop
     Private Sub FrmMain_Drop(sender As Object, e As DragEventArgs) Handles Me.Drop
         Try
         Try
+            ShowWindowToTop()
             If e.Data.GetDataPresent(DataFormats.Text) Then
             If e.Data.GetDataPresent(DataFormats.Text) Then
                 '获取文本
                 '获取文本
                 Try
                 Try
@@ -1505,4 +1503,4 @@ Public Class FormMain
         lastMouseArg = e
         lastMouseArg = e
     End Sub
     End Sub
 
 
-End Class
+End Class

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

@@ -13,13 +13,13 @@ Public Module ModBase
 #Region "声明"
 #Region "声明"
 
 
     '下列版本信息由更新器自动修改
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.10.3" '不含分支前缀的显示用版本名
-    Public Const VersionStandardCode As String = "2.10.3." & VersionBranchCode '标准格式的四段式版本号
+    Public Const VersionBaseName As String = "2.10.4" '不含分支前缀的显示用版本名
+    Public Const VersionStandardCode As String = "2.10.4." & VersionBranchCode '标准格式的四段式版本号
     Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
     Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
 #If BETA Then
 #If BETA Then
     Public Const VersionCode As Integer = 361 'Release
     Public Const VersionCode As Integer = 361 'Release
 #Else
 #Else
-    Public Const VersionCode As Integer = 362 'Snapshot
+    Public Const VersionCode As Integer = 363 'Snapshot
 #End If
 #End If
     '自动生成的版本信息
     '自动生成的版本信息
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
     Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -1551,7 +1551,7 @@ RetryDir:
     ''' <summary>
     ''' <summary>
     ''' 获取 JSON 对象。
     ''' 获取 JSON 对象。
     ''' </summary>
     ''' </summary>
-    Public Function GetJson(Data As String)
+    Public Function GetJson(Data As String) As JToken
         Try
         Try
             Return JsonConvert.DeserializeObject(Data, New JsonSerializerSettings With {.DateTimeZoneHandling = DateTimeZoneHandling.Local})
             Return JsonConvert.DeserializeObject(Data, New JsonSerializerSettings With {.DateTimeZoneHandling = DateTimeZoneHandling.Local})
         Catch ex As Exception
         Catch ex As Exception
@@ -2131,6 +2131,19 @@ RetryDir:
         End Function
         End Function
     End Class
     End Class
 
 
+    ''' <summary>
+    ''' 将二维数组转换为字典。
+    ''' </summary>
+    <Extension> Public Function ToDictionary(Of T)(arr As T(,)) As Dictionary(Of T, T)
+        Dim result As New Dictionary(Of T, T)
+        If arr.Length = 0 Then Return result
+        If arr.GetLength(1) <> 2 Then Throw New ArgumentException("数组必须为两列,第一列为 Key,第二列为 Value。")
+        For i As Integer = 0 To arr.GetLength(0) - 1
+            result(arr(i, 0)) = arr(i, 1)
+        Next
+        Return result
+    End Function
+
     ''' <summary>
     ''' <summary>
     ''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。
     ''' 可用于临时存放文件的,不含任何特殊字符的文件夹路径,以“\”结尾。
     ''' </summary>
     ''' </summary>
@@ -2179,10 +2192,10 @@ RetryDir:
     End Function
     End Function
 
 
     ''' <summary>
     ''' <summary>
-    ''' 判断当前系统语言是否为 zh-CN。
+    ''' 判断当前系统语言是否为中文。
     ''' </summary>
     ''' </summary>
     Public Function IsSystemLanguageChinese() As Boolean
     Public Function IsSystemLanguageChinese() As Boolean
-        Return CultureInfo.CurrentCulture.Name = "zh-CN" OrElse CultureInfo.CurrentUICulture.Name = "zh-CN"
+        Return CultureInfo.CurrentCulture.Name.StartsWithF("zh-") OrElse CultureInfo.CurrentUICulture.Name.StartsWithF("zh-")
     End Function
     End Function
 
 
     Private Uuid As Integer = 1
     Private Uuid As Integer = 1
@@ -2225,6 +2238,9 @@ NextElement:
         Next i
         Next i
         Return ResultArray
         Return ResultArray
     End Function
     End Function
+    <Extension> Public Function Any(Arr As UIElementCollection) As Boolean
+        Return Arr?.Count > 0
+    End Function
 
 
     ''' <summary>
     ''' <summary>
     ''' 获取格式类似于“11:08:52.037”的当前时间的字符串。
     ''' 获取格式类似于“11:08:52.037”的当前时间的字符串。
@@ -2462,7 +2478,7 @@ NextElement:
     ''' </summary>
     ''' </summary>
     Public Sub RunInThread(Action As Action)
     Public Sub RunInThread(Action As Action)
         If RunInUi() Then
         If RunInUi() Then
-            RunInNewThread(Action, "Runtime Invoke " & GetUuid() & "#")
+            RunInNewThread(Action, "Invoke " & GetUuid())
         Else
         Else
             Action()
             Action()
         End If
         End If
@@ -2760,11 +2776,11 @@ Retry:
             Using Reader As New XamlXmlReader(Stream)
             Using Reader As New XamlXmlReader(Stream)
                 While Reader.Read()
                 While Reader.Read()
                     For Each BlackListType In {GetType(WebBrowser), GetType(Frame), GetType(MediaElement), GetType(ObjectDataProvider), GetType(XamlReader), GetType(Window), GetType(XmlDataProvider)}
                     For Each BlackListType In {GetType(WebBrowser), GetType(Frame), GetType(MediaElement), GetType(ObjectDataProvider), GetType(XamlReader), GetType(Window), GetType(XmlDataProvider)}
-                        If Reader.Type IsNot Nothing AndAlso BlackListType.IsAssignableFrom(Reader.Type.UnderlyingType) Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListType.Name} 类型。")
-                        If Reader.Value IsNot Nothing AndAlso Reader.Value = BlackListType.Name Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListType.Name} 值。")
+                        If Reader.Type IsNot Nothing AndAlso BlackListType.IsAssignableFrom(Reader.Type.UnderlyingType) Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListType.Name} 类型。")
+                        If Reader.Value IsNot Nothing AndAlso Reader.Value = BlackListType.Name Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListType.Name} 值。")
                     Next
                     Next
                     For Each BlackListMember In {"Code", "FactoryMethod", "Static"}
                     For Each BlackListMember In {"Code", "FactoryMethod", "Static"}
-                        If Reader.Member IsNot Nothing AndAlso Reader.Member.Name = BlackListMember Then Throw New UnauthorizedAccessException($"不允许使用 {BlackListMember} 成员。")
+                        If Reader.Member IsNot Nothing AndAlso Reader.Member.Name = BlackListMember Then Throw New UnauthorizedAccessException($"基于安全考虑,不允许使用 {BlackListMember} 成员。")
                     Next
                     Next
                 End While
                 End While
             End Using
             End Using
@@ -2922,7 +2938,7 @@ Retry:
         '处理错误会导致再次调用 Log() 导致无限循环
         '处理错误会导致再次调用 Log() 导致无限循环
 
 
         '输出日志
         '输出日志
-        Dim AppendText As String = "[" & GetTimeNow() & "] " & Text & vbCrLf '减轻同步锁占用
+        Dim AppendText As String = $"[{GetTimeNow()}] <{If(Thread.CurrentThread.Name = "", "主线程", Thread.CurrentThread.Name)}> {Text}{vbCrLf}" '减轻同步锁占用
         If ModeDebug Then
         If ModeDebug Then
             SyncLock LogListLock
             SyncLock LogListLock
                 LogList.Append(AppendText)
                 LogList.Append(AppendText)
@@ -2986,7 +3002,7 @@ Retry:
         Dim ExFull As String = Desc & ":" & GetExceptionDetail(Ex)
         Dim ExFull As String = Desc & ":" & GetExceptionDetail(Ex)
 
 
         '输出日志
         '输出日志
-        Dim AppendText As String = "[" & GetTimeNow() & "] " & Desc & ":" & GetExceptionDetail(Ex, True) & vbCrLf '减轻同步锁占用
+        Dim AppendText As String = $"[{GetTimeNow()}] <{If(Thread.CurrentThread.Name = "", "主线程", Thread.CurrentThread.Name)}> {Desc}:{GetExceptionDetail(Ex, True)}{vbCrLf}" '减轻同步锁占用
         If ModeDebug Then
         If ModeDebug Then
             SyncLock LogListLock
             SyncLock LogListLock
                 LogList.Append(AppendText)
                 LogList.Append(AppendText)
@@ -3048,7 +3064,7 @@ Retry:
         If ForceOpenLog OrElse (ShowMsgbox AndAlso MyMsgBox("若你在汇报一个 Bug,请点击 打开文件夹 按钮,并上传 Log(1~5).txt 中包含错误信息的文件。" & vbCrLf & "游戏崩溃一般与启动器无关,请不要因为游戏崩溃而提交反馈。", "反馈提交提醒", "打开文件夹", "不需要") = 1) Then
         If ForceOpenLog OrElse (ShowMsgbox AndAlso MyMsgBox("若你在汇报一个 Bug,请点击 打开文件夹 按钮,并上传 Log(1~5).txt 中包含错误信息的文件。" & vbCrLf & "游戏崩溃一般与启动器无关,请不要因为游戏崩溃而提交反馈。", "反馈提交提醒", "打开文件夹", "不需要") = 1) Then
             OpenExplorer(Path & "PCL\Log1.txt")
             OpenExplorer(Path & "PCL\Log1.txt")
         End If
         End If
-        OpenWebsite("https://github.com/Hex-Dragon/PCL2/issues/")
+        OpenWebsite("https://github.com/Meloong-Git/PCL/issues/")
     End Sub
     End Sub
     Public Function CanFeedback(ShowHint As Boolean) As Boolean
     Public Function CanFeedback(ShowHint As Boolean) As Boolean
         If False.Equals(PageSetupSystem.IsLauncherNewest) Then
         If False.Equals(PageSetupSystem.IsLauncherNewest) Then

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

@@ -339,7 +339,7 @@
                     [Error] = ex
                     [Error] = ex
                     State = LoadState.Failed
                     State = LoadState.Failed
                 End Try
                 End Try
-            End Sub) With {.Name = Name, .Priority = ThreadPriority}
+            End Sub) With {.Name = "L/" & Name, .Priority = ThreadPriority}
             LastRunningThread.Start() '不能使用 RunInNewThread,否则在函数返回前线程就会运行完,导致误判 IsAborted
             LastRunningThread.Start() '不能使用 RunInNewThread,否则在函数返回前线程就会运行完,导致误判 IsAborted
         End Sub
         End Sub
         Public Overrides Sub Abort()
         Public Overrides Sub Abort()

Datei-Diff unterdrückt, da er zu groß ist
+ 411 - 776
Plain Craft Launcher 2/Modules/Base/ModNet.vb


+ 32 - 20
Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb

@@ -1076,7 +1076,7 @@ Retry:
                     Try
                     Try
                         '获取工程列表
                         '获取工程列表
                         Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
                         Log("[Comp] 开始从 CurseForge 获取工程列表:" & CurseForgeUrl)
-                        Dim RequestResult As JObject = DlModRequest(CurseForgeUrl, IsJson:=True)
+                        Dim RequestResult As JObject = DlModRequest(CurseForgeUrl)
                         Task.Progress += 0.2
                         Task.Progress += 0.2
                         Dim ProjectList As New List(Of CompProject)
                         Dim ProjectList As New List(Of CompProject)
                         For Each JsonEntry As JObject In RequestResult("data")
                         For Each JsonEntry As JObject In RequestResult("data")
@@ -1107,7 +1107,7 @@ Retry:
                 Sub()
                 Sub()
                     Try
                     Try
                         Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
                         Log("[Comp] 开始从 Modrinth 获取工程列表:" & ModrinthUrl)
-                        Dim RequestResult As JObject = DlModRequest(ModrinthUrl, IsJson:=True)
+                        Dim RequestResult As JObject = DlModRequest(ModrinthUrl)
                         Task.Progress += 0.2
                         Task.Progress += 0.2
                         Dim ProjectList As New List(Of CompProject)
                         Dim ProjectList As New List(Of CompProject)
                         For Each JsonEntry As JObject In RequestResult("hits")
                         For Each JsonEntry As JObject In RequestResult("hits")
@@ -1396,7 +1396,7 @@ Retry:
                     'DownloadAddress
                     'DownloadAddress
                     Dim Url = Data("downloadUrl").ToString
                     Dim Url = Data("downloadUrl").ToString
                     If Url = "" Then Url = $"https://media.forgecdn.net/files/{CInt(Id.ToString.Substring(0, 4))}/{CInt(Id.ToString.Substring(4))}/{FileName}"
                     If Url = "" Then Url = $"https://media.forgecdn.net/files/{CInt(Id.ToString.Substring(0, 4))}/{CInt(Id.ToString.Substring(4))}/{FileName}"
-                    Url = Url.Replace(FileName, Net.WebUtility.UrlEncode(FileName)) '对文件名进行编码
+                    Url = Url.Replace(FileName, WebUtility.UrlEncode(FileName)) '对文件名进行编码
                     DownloadUrls = HandleCurseForgeDownloadUrls(Url) '对脑残 CurseForge 的下载地址进行多种修正
                     DownloadUrls = HandleCurseForgeDownloadUrls(Url) '对脑残 CurseForge 的下载地址进行多种修正
                     DownloadUrls.AddRange(DownloadUrls.Select(Function(u) DlSourceModGet(u)).ToList) '添加镜像源,这个写法是为了让镜像源排在后面
                     DownloadUrls.AddRange(DownloadUrls.Select(Function(u) DlSourceModGet(u)).ToList) '添加镜像源,这个写法是为了让镜像源排在后面
                     DownloadUrls = DownloadUrls.Distinct.ToList '最终去重
                     DownloadUrls = DownloadUrls.Distinct.ToList '最终去重
@@ -1492,9 +1492,8 @@ Retry:
             Return {
             Return {
                 Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
                 Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
                 Url.Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
                 Url.Replace("://edge.", "://mediafilez.").Replace("://media.", "://mediafilez."),
-                Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge.", "://media."),
                 Url.Replace("-service.overwolf.wtf", ".forgecdn.net"),
                 Url.Replace("-service.overwolf.wtf", ".forgecdn.net"),
-                Url.Replace("://edge.", "://media."),
+                Url.Replace("://media.", "://edge."),
                 Url
                 Url
             }.Distinct.ToList
             }.Distinct.ToList
         End Function
         End Function
@@ -1591,27 +1590,40 @@ Retry:
         If CompProjectCache.ContainsKey(ProjectId) Then '存在缓存
         If CompProjectCache.ContainsKey(ProjectId) Then '存在缓存
             TargetProject = CompProjectCache(ProjectId)
             TargetProject = CompProjectCache(ProjectId)
         ElseIf FromCurseForge Then 'CurseForge
         ElseIf FromCurseForge Then 'CurseForge
-            TargetProject = New CompProject(DlModRequest("https://api.curseforge.com/v1/mods/" & ProjectId, IsJson:=True)("data"))
+            TargetProject = New CompProject(DlModRequest("https://api.curseforge.com/v1/mods/" & ProjectId)("data"))
         Else 'Modrinth
         Else 'Modrinth
-            TargetProject = New CompProject(DlModRequest("https://api.modrinth.com/v2/project/" & ProjectId, IsJson:=True))
+            TargetProject = New CompProject(DlModRequest("https://api.modrinth.com/v2/project/" & ProjectId))
         End If
         End If
         '获取工程对象的文件列表
         '获取工程对象的文件列表
         If Not CompFilesCache.ContainsKey(ProjectId) Then '有缓存也不能直接返回,这时候前置可能没获取(#5173)
         If Not CompFilesCache.ContainsKey(ProjectId) Then '有缓存也不能直接返回,这时候前置可能没获取(#5173)
             Log("[Comp] 开始获取文件列表:" & ProjectId)
             Log("[Comp] 开始获取文件列表:" & ProjectId)
-            Dim ResultJsonArray As JArray
+            Dim ResultJsonArray As JArray = Nothing
             If FromCurseForge Then
             If FromCurseForge Then
                 'CurseForge
                 'CurseForge
-                'HMCL 一次性请求了 10000 个文件,虽然不知道会不会出问题但先这样吧……(#5522)
-                ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=10000", IsJson:=True)("data")
-                '之前只请求一部分文件的方法备份如下:
-                'If TargetProject.Type = CompType.Mod Then 'Mod 使用每个版本最新的文件
-                '    ResultJsonArray = GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json"))("data")
-                'Else '否则使用全部文件
-                '    ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=999", IsJson:=True)("data")
-                'End If
+                For RetryCount As Integer = 0 To 2
+                    Dim ResultJson As JObject = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=" &
+                        (10000 + RetryCount)) '每次重试多请求一个文件,以避免触发 CDN 缓存
+                    'HMCL 一次性请求了 10000 个文件,虽然不知道会不会出问题但先这样吧……(#5522)
+                    '之前只请求一部分文件的方法备份如下:
+                    'If TargetProject.Type = CompType.Mod Then 'Mod 使用每个版本最新的文件
+                    '    ResultJsonArray = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post, "{""fileIds"": [" & Join(TargetProject.CurseForgeFileIds, ",") & "]}", "application/json")("data")
+                    'Else '否则使用全部文件
+                    '    ResultJsonArray = DlModRequest($"https://api.curseforge.com/v1/mods/{ProjectId}/files?pageSize=999")("data")
+                    'End If
+                    If ResultJson("pagination")("resultCount").ToObject(Of Integer) = ResultJson("pagination")("totalCount").ToObject(Of Integer) Then
+                        ResultJsonArray = ResultJson("data")
+                        Exit For
+                    ElseIf RetryCount < 2 Then
+                        Log($"[Comp] CurseForge 返回的文件列表存在缺失,即将进行第 {RetryCount + 1} 次重试", LogLevel.Debug) '#6224
+                        Log($"[Comp] 返回的原始内容如下:{vbCrLf}{ResultJson}")
+                    Else
+                        Log($"[Comp] CurseForge 返回的文件列表存在缺失,返回的原始内容如下:{vbCrLf}{ResultJson}")
+                        Throw New Exception("CurseForge 返回的文件列表存在缺失")
+                    End If
+                Next
             Else
             Else
                 'Modrinth
                 'Modrinth
-                ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version", IsJson:=True)
+                ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version")
             End If
             End If
             CompFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)).
             CompFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) New CompFile(a, TargetProject.Type)).
                 Where(Function(a) a.Available).ToList.
                 Where(Function(a) a.Available).ToList.
@@ -1625,10 +1637,10 @@ Retry:
             Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置:{Join(UndoneDeps, ",")}")
             Log($"[Comp] {ProjectId} 文件列表中还需要获取信息的前置:{Join(UndoneDeps, ",")}")
             Dim Projects As JArray
             Dim Projects As JArray
             If TargetProject.FromCurseForge Then
             If TargetProject.FromCurseForge Then
-                Projects = GetJson(DlModRequest("https://api.curseforge.com/v1/mods",
-                    "POST", "{""modIds"": [" & Join(UndoneDeps, ",") & "]}", "application/json"))("data")
+                Projects = DlModRequest("https://api.curseforge.com/v1/mods",
+                    HttpMethod.Post, "{""modIds"": [" & Join(UndoneDeps, ",") & "]}", "application/json")("data")
             Else
             Else
-                Projects = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]", IsJson:=True)
+                Projects = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Join(UndoneDeps, """,""")}""]")
             End If
             End If
             For Each Project In Projects
             For Each Project In Projects
                 Dim Unused As New CompProject(Project) '在 New 的时候会添加缓存以便之后读取
                 Dim Unused As New CompProject(Project) '在 New 的时候会添加缓存以便之后读取

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

@@ -1018,9 +1018,9 @@ NextStack:
                     End If
                     End If
                 Case CrashReason.特定实体导致崩溃
                 Case CrashReason.特定实体导致崩溃
                     If Additional.Count = 1 Then
                     If Additional.Count = 1 Then
-                        Results.Add("游戏似乎因为实体 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并生成一个该实体,然后观察游戏的运行情况:\n - 若正常运行,则是该实体导致出错,你或许需要使用一些方式删除此实体。\n - 若仍然出错,问题就可能来自其他原因……\h")
+                        Results.Add("游戏似乎因为实体 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并生成一个该实体,然后观察游戏的运行情况。\h")
                     Else
                     Else
-                        Results.Add("游戏似乎因为世界中的某些实体出现了问题。\n\n你可以创建一个新世界,并生成各种实体,观察游戏的运行情况:\n - 若正常运行,则是某些实体导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h")
+                        Results.Add("游戏似乎因为世界中的某些实体出现了问题。\n\n你可以创建一个新世界,并生成各种实体,然后观察游戏的运行情况。\h")
                     End If
                     End If
                 Case CrashReason.OptiFine与Forge不兼容
                 Case CrashReason.OptiFine与Forge不兼容
                     Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。")
                     Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。")

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

@@ -198,7 +198,7 @@
     Private IsNewClientVersionHinted As Boolean = False
     Private IsNewClientVersionHinted As Boolean = False
     Private Sub DlClientListMojangMain(Loader As LoaderTask(Of String, DlClientListResult))
     Private Sub DlClientListMojangMain(Loader As LoaderTask(Of String, DlClientListResult))
         Dim StartTime As Long = GetTimeTick()
         Dim StartTime As Long = GetTimeTick()
-        Dim Json As JObject = NetGetCodeByRequestRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", IsJson:=True)
+        Dim Json As JObject = GetJson(NetRequestByClientRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json"))
         Try
         Try
             Dim Versions As JArray = Json("versions")
             Dim Versions As JArray = Json("versions")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
@@ -235,7 +235,7 @@
     ''' </summary>
     ''' </summary>
     Public DlClientListBmclapiLoader As New LoaderTask(Of String, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
     Public DlClientListBmclapiLoader As New LoaderTask(Of String, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
     Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of String, DlClientListResult))
     Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of String, DlClientListResult))
-        Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
+        Dim Json As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json"))
         Try
         Try
             Dim Versions As JArray = Json("versions")
             Dim Versions As JArray = Json("versions")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
             If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
@@ -377,7 +377,7 @@
     ''' </summary>
     ''' </summary>
     Public DlOptiFineListOfficialLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Official", AddressOf DlOptiFineListOfficialMain)
     Public DlOptiFineListOfficialLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Official", AddressOf DlOptiFineListOfficialMain)
     Private Sub DlOptiFineListOfficialMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
     Private Sub DlOptiFineListOfficialMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
-        Dim Result As String = NetGetCodeByClient("https://optifine.net/downloads", Encoding.Default)
+        Dim Result As String = NetRequestByClientRetry("https://optifine.net/downloads", Encoding:=Encoding.Default)
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         Try
         Try
             '获取所有版本信息
             '获取所有版本信息
@@ -413,7 +413,7 @@
     ''' </summary>
     ''' </summary>
     Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
     Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
     Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
     Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
-        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
+        Dim Json As JArray = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/optifine/versionList"))
         Try
         Try
             Dim Versions As New List(Of DlOptiFineListEntry)
             Dim Versions As New List(Of DlOptiFineListEntry)
             For Each Token As JObject In Json
             For Each Token As JObject In Json
@@ -483,7 +483,7 @@
     ''' </summary>
     ''' </summary>
     Public DlForgeListOfficialLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Official", AddressOf DlForgeListOfficialMain)
     Public DlForgeListOfficialLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Official", AddressOf DlForgeListOfficialMain)
     Private Sub DlForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
     Private Sub DlForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
-        Dim Result As String = NetGetCodeByRequestRetry("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html", UseBrowserUserAgent:=True)
+        Dim Result As String = NetRequestByClientRetry("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding:=Encoding.Default, Accept:="text/html", UseBrowserUserAgent:=True)
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         '获取所有版本信息
         '获取所有版本信息
         Dim Names As List(Of String) = RegexSearch(Result, "(?<=a href=""index_)[0-9.]+(_pre[0-9]?)?(?=.html)")
         Dim Names As List(Of String) = RegexSearch(Result, "(?<=a href=""index_)[0-9.]+(_pre[0-9]?)?(?=.html)")
@@ -497,7 +497,7 @@
     ''' </summary>
     ''' </summary>
     Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
     Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
     Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
     Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
-        Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
+        Dim Result As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding:=Encoding.Default)
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
         '获取所有版本信息
         '获取所有版本信息
         Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
         Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
@@ -629,7 +629,7 @@
     Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
     Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
         Dim Result As String
         Dim Result As String
         Try
         Try
-            Result = NetGetCodeByLoader("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
+            Result = NetRequestByLoader("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
                                           Loader.Input.Replace("-", "_") & '兼容 Forge 1.7.10-pre4,#4057
                                           Loader.Input.Replace("-", "_") & '兼容 Forge 1.7.10-pre4,#4057
                                           ".html", UseBrowserUserAgent:=True)
                                           ".html", UseBrowserUserAgent:=True)
         Catch ex As Exception
         Catch ex As Exception
@@ -700,9 +700,8 @@
     ''' Forge 版本列表,BMCLAPI。
     ''' Forge 版本列表,BMCLAPI。
     ''' </summary>
     ''' </summary>
     Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
     Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
-        Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" &
-                                                      Loader.Input.Replace("-", "_"), '兼容 Forge 1.7.10-pre4,#4057
-                                                      IsJson:=True)
+        Dim Json As JArray = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" &
+                                                      Loader.Input.Replace("-", "_"))) '兼容 Forge 1.7.10-pre4,#4057
         Dim Versions As New List(Of DlForgeVersionEntry)
         Dim Versions As New List(Of DlForgeVersionEntry)
         Try
         Try
             Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
             Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
@@ -836,8 +835,8 @@
     Public DlNeoForgeListOfficialLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Official", AddressOf DlNeoForgeListOfficialMain)
     Public DlNeoForgeListOfficialLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Official", AddressOf DlNeoForgeListOfficialMain)
     Private Sub DlNeoForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
     Private Sub DlNeoForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
         '获取版本列表 JSON
         '获取版本列表 JSON
-        Dim ResultLatest As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
-        Dim ResultLegacy As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
+        Dim ResultLatest As String = NetRequestByClientRetry("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge")
+        Dim ResultLegacy As String = NetRequestByClientRetry("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge")
         If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
         If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
         '解析
         '解析
         Try
         Try
@@ -854,8 +853,8 @@
     Public DlNeoForgeListBmclapiLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Bmclapi", AddressOf DlNeoForgeListBmclapiMain)
     Public DlNeoForgeListBmclapiLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Bmclapi", AddressOf DlNeoForgeListBmclapiMain)
     Public Sub DlNeoForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
     Public Sub DlNeoForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
         '获取版本列表 JSON
         '获取版本列表 JSON
-        Dim ResultLatest As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
-        Dim ResultLegacy As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
+        Dim ResultLatest As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge")
+        Dim ResultLegacy As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge")
         If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
         If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
         '解析
         '解析
         Try
         Try
@@ -960,7 +959,7 @@
     ''' </summary>
     ''' </summary>
     Public DlLiteLoaderListOfficialLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Official", AddressOf DlLiteLoaderListOfficialMain)
     Public DlLiteLoaderListOfficialLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Official", AddressOf DlLiteLoaderListOfficialMain)
     Private Sub DlLiteLoaderListOfficialMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
     Private Sub DlLiteLoaderListOfficialMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://dl.liteloader.com/versions/versions.json", IsJson:=True)
+        Dim Result As JObject = GetJson(NetRequestByClientRetry("https://dl.liteloader.com/versions/versions.json"))
         Try
         Try
             Dim Json As JObject = Result("versions")
             Dim Json As JObject = Result("versions")
             Dim Versions As New List(Of DlLiteLoaderListEntry)
             Dim Versions As New List(Of DlLiteLoaderListEntry)
@@ -988,7 +987,7 @@
     ''' </summary>
     ''' </summary>
     Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
     Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
     Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
     Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
+        Dim Result As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json"))
         Try
         Try
             Dim Json As JObject = Result("versions")
             Dim Json As JObject = Result("versions")
             Dim Versions As New List(Of DlLiteLoaderListEntry)
             Dim Versions As New List(Of DlLiteLoaderListEntry)
@@ -1059,7 +1058,7 @@
     ''' </summary>
     ''' </summary>
     Public DlFabricListOfficialLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Official", AddressOf DlFabricListOfficialMain)
     Public DlFabricListOfficialLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Official", AddressOf DlFabricListOfficialMain)
     Private Sub DlFabricListOfficialMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
     Private Sub DlFabricListOfficialMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://meta.fabricmc.net/v2/versions", IsJson:=True)
+        Dim Result As JObject = GetJson(NetRequestByClientRetry("https://meta.fabricmc.net/v2/versions"))
         Try
         Try
             Dim Output = New DlFabricListResult With {.IsOfficial = True, .SourceName = "Fabric 官方源", .Value = Result}
             Dim Output = New DlFabricListResult With {.IsOfficial = True, .SourceName = "Fabric 官方源", .Value = Result}
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
@@ -1074,7 +1073,7 @@
     ''' </summary>
     ''' </summary>
     Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
     Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
     Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
     Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
-        Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
+        Dim Result As JObject = GetJson(NetRequestByClientRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions"))
         Try
         Try
             Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
             Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
             If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
@@ -1101,50 +1100,14 @@
 #Region "DlMod | Mod 镜像源请求"
 #Region "DlMod | Mod 镜像源请求"
 
 
     ''' <summary>
     ''' <summary>
-    ''' 对可能涉及 Mod 镜像源的请求进行处理,返回字符串或 JObject。
-    ''' 调用 NetGetCodeByRequest,会进行重试。
-    ''' </summary>
-    Public Function DlModRequest(Url As String, Optional IsJson As Boolean = False) As Object
-        Dim Urls As New List(Of KeyValuePair(Of String, Integer))
-        Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
-        Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
-        'Dim McimUrl As String = DlSourceModGet(Url)
-        'If McimUrl <> Url Then
-        '    Select Case Setup.Get("ToolDownloadMod")
-        '        Case 0
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
-        '        Case 1
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 5))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
-        '        Case Else
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
-        '            Urls.Add(New KeyValuePair(Of String, Integer)(McimUrl, 10))
-        '    End Select
-        'End If
-        Dim Exs As String = ""
-        For Each Source In Urls
-            Try
-                Return NetGetCodeByRequestOnce(Source.Key, Encode:=Encoding.UTF8, Timeout:=Source.Value * 1000, IsJson:=IsJson, UseBrowserUserAgent:=True)
-            Catch ex As Exception
-                Exs += ex.Message + vbCrLf
-            End Try
-        Next
-        Throw New Exception(Exs)
-    End Function
-
-    ''' <summary>
-    ''' 对可能涉及 Mod 镜像源的请求进行处理。
+    ''' 对可能涉及 Mod 镜像源的请求进行处理,返回 JToken。
     ''' 调用 NetRequest,会进行重试。
     ''' 调用 NetRequest,会进行重试。
     ''' </summary>
     ''' </summary>
-    Public Function DlModRequest(Url As String, Method As String, Data As String, ContentType As String) As String
+    Public Function DlModRequest(Url As String, Optional Method As HttpMethod = Nothing,
+                                 Optional Content As String = Nothing, Optional ContentType As String = Nothing) As JToken
         Dim Urls As New List(Of KeyValuePair(Of String, Integer))
         Dim Urls As New List(Of KeyValuePair(Of String, Integer))
         Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
         Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
-        Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
+        Urls.Add(New KeyValuePair(Of String, Integer)(Url, 30))
         'Dim McimUrl As String = DlSourceModGet(Url)
         'Dim McimUrl As String = DlSourceModGet(Url)
         'If McimUrl <> Url Then
         'If McimUrl <> Url Then
         '   Select Case Setup.Get("ToolDownloadMod")
         '   Select Case Setup.Get("ToolDownloadMod")
@@ -1166,7 +1129,7 @@
         Dim Exs As String = ""
         Dim Exs As String = ""
         For Each Source In Urls
         For Each Source In Urls
             Try
             Try
-                Return NetRequestOnce(Source.Key, Method, Data, ContentType, Timeout:=Source.Value * 1000)
+                Return GetJson(NetRequestByClient(Source.Key, Method, Content, ContentType, Timeout:=Source.Value * 1000, Encoding:=Encoding.UTF8))
             Catch ex As Exception
             Catch ex As Exception
                 Exs += ex.Message + vbCrLf
                 Exs += ex.Message + vbCrLf
             End Try
             End Try

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

@@ -117,7 +117,7 @@
                 If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败")
                 If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败")
                 If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
                 If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
                 If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试")
                 If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试")
-                If Output.Contains("a fatal error") Then Throw New ApplicationException("由于操作系统或 JVM 存在问题,无法运行该 Java。")
+                If Output.Contains("a fatal error") Then Throw New ApplicationException("无法运行该 Java,该 Java 或系统存在问题")
                 '获取详细信息
                 '获取详细信息
                 Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
                 Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
                 If VersionString.Split(".").Count > 4 Then VersionString = VersionString.Replace(".0.", ".") '#3493,VersionString = "21.0.2.0.2"
                 If VersionString.Split(".").Count > 4 Then VersionString = VersionString.Replace(".0.", ".") '#3493,VersionString = "21.0.2.0.2"
@@ -460,10 +460,12 @@ NoUserJava:
     ''' 将 Java 按照适用性排序。
     ''' 将 Java 按照适用性排序。
     ''' </summary>
     ''' </summary>
     Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
     Public Function JavaSorter(Left As JavaEntry, Right As JavaEntry) As Boolean
+        Dim PathInfo As New DirectoryInfo(ShortenPath(Path))
+        Dim PathMcInfo As New DirectoryInfo(ShortenPath(PathMcFolder))
         '1. 尽量在当前文件夹或当前 Minecraft 文件夹
         '1. 尽量在当前文件夹或当前 Minecraft 文件夹
         Dim ProgramPathParent As String, MinecraftPathParent As String = ""
         Dim ProgramPathParent As String, MinecraftPathParent As String = ""
-        ProgramPathParent = If(New DirectoryInfo(Path).Parent, New DirectoryInfo(Path)).FullName
-        If PathMcFolder <> "" Then MinecraftPathParent = If(New DirectoryInfo(PathMcFolder).Parent, New DirectoryInfo(PathMcFolder)).FullName
+        ProgramPathParent = If(PathInfo.Parent, PathInfo).FullName
+        If PathMcFolder <> "" Then MinecraftPathParent = If(PathMcInfo.Parent, PathMcInfo).FullName
         If Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Not Right.PathFolder.StartsWithF(ProgramPathParent) Then Return True
         If Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Not Right.PathFolder.StartsWithF(ProgramPathParent) Then Return True
         If Not Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Right.PathFolder.StartsWithF(ProgramPathParent) Then Return False
         If Not Left.PathFolder.StartsWithF(ProgramPathParent) AndAlso Right.PathFolder.StartsWithF(ProgramPathParent) Then Return False
         If PathMcFolder <> "" Then
         If PathMcFolder <> "" Then
@@ -530,7 +532,7 @@ NoUserJava:
                 If Disk.DriveType = DriveType.Network Then Continue For '跳过网络驱动器(#3705)
                 If Disk.DriveType = DriveType.Network Then Continue For '跳过网络驱动器(#3705)
                 JavaSearchFolder(Disk.Name, JavaPreList, False)
                 JavaSearchFolder(Disk.Name, JavaPreList, False)
             Next
             Next
-            '查找 ~/.jdks 文件夹中的 Java
+            '查找 .jdks 文件夹中的 Java
             JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\.jdks\", JavaPreList, False)
             JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) & "\.jdks\", JavaPreList, False)
             '查找 APPDATA 文件夹中的 Java
             '查找 APPDATA 文件夹中的 Java
             JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
             JavaSearchFolder(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\", JavaPreList, False)
@@ -680,8 +682,8 @@ Wait:
             '若该目录有 Java,则加入结果
             '若该目录有 Java,则加入结果
             If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
             If File.Exists(Path & "javaw.exe") Then Results(Path) = Source
             '查找其下的所有文件夹
             '查找其下的所有文件夹
-            '不应使用网易的 Java:https://github.com/HMeloong-Git/PCL/issues/1279#issuecomment-2761489121
-            Dim Keywords = {"java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "dragon",
+            '不应使用网易的 Java:https://github.com/Meloong-Git/PCL/issues/1279#issuecomment-2761489121
+            Dim Keywords = {"java", "jdk", "env", "环境", "run", "软件", "jre", "mc", "dragon", "bin",
                             "soft", "cache", "temp", "corretto", "roaming", "users", "craft", "program", "世界", "net",
                             "soft", "cache", "temp", "corretto", "roaming", "users", "craft", "program", "世界", "net",
                             "游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", "client", "整合",
                             "游戏", "oracle", "game", "file", "data", "jvm", "服务", "server", "客户", "client", "整合",
                             "应用", "运行", "前置", "mojang", "官启", "新建文件夹", "eclipse", "microsoft", "hotspot",
                             "应用", "运行", "前置", "mojang", "官启", "新建文件夹", "eclipse", "microsoft", "hotspot",
@@ -748,7 +750,7 @@ Wait:
     Private LastJavaBaseDir As String = Nothing '用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
     Private LastJavaBaseDir As String = Nothing '用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
     Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
     Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
         Log("[Java] 开始获取 Java 下载信息")
         Log("[Java] 开始获取 Java 下载信息")
-        Dim IndexFileStr As String = NetGetCodeByLoader(DlVersionListOrder(
+        Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
             {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
             {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
             {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
             {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
         ), IsJson:=True)
         ), IsJson:=True)
@@ -760,7 +762,7 @@ Wait:
         Dim Address As String = TargetEntry.Value("manifest")("url")
         Dim Address As String = TargetEntry.Value("manifest")("url")
         Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
         Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
         '获取文件列表
         '获取文件列表
-        Dim ListFileStr As String = NetGetCodeByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
+        Dim ListFileStr As String = NetRequestByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
         LastJavaBaseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
         LastJavaBaseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
         Dim Results As New List(Of NetFile)
         Dim Results As New List(Of NetFile)
         For Each File As JProperty In CType(GetJson(ListFileStr), JObject)("files")
         For Each File As JProperty In CType(GetJson(ListFileStr), JObject)("files")

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

@@ -684,11 +684,10 @@ LoginFinish:
         '发送登录请求
         '发送登录请求
         Dim RequestData As New JObject(
         Dim RequestData As New JObject(
             New JProperty("accessToken", AccessToken), New JProperty("clientToken", ClientToken), New JProperty("requestUser", True))
             New JProperty("accessToken", AccessToken), New JProperty("clientToken", ClientToken), New JProperty("requestUser", True))
-        NetRequestRetry(
-            Url:=Data.Input.BaseUrl & "/validate",
-            Method:="POST",
-            Data:=RequestData.ToString(0),
-            Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
+        NetRequestByClientRetry(
+            Data.Input.BaseUrl & "/validate", HttpMethod.Post,
+            Content:=RequestData.ToString(Newtonsoft.Json.Formatting.None),
+            Headers:={{"Accept-Language", "zh-CN"}},
             ContentType:="application/json; charset=utf-8") '没有返回值的
             ContentType:="application/json; charset=utf-8") '没有返回值的
         '将登录结果输出
         '将登录结果输出
         Data.Output.AccessToken = AccessToken
         Data.Output.AccessToken = AccessToken
@@ -701,10 +700,9 @@ LoginFinish:
     End Sub
     End Sub
     Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean)
     Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean)
         McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")")
         McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")")
-        Dim LoginJson As JObject = GetJson(NetRequestRetry(
-               Url:=Data.Input.BaseUrl & "/refresh",
-               Method:="POST",
-               Data:=New JObject(
+        Dim LoginJson As JObject = GetJson(NetRequestByClientRetry(
+               Data.Input.BaseUrl & "/refresh", HttpMethod.Post,
+               Content:=New JObject(
                    New JProperty("selectedProfile", New JObject(
                    New JProperty("selectedProfile", New JObject(
                        New JProperty("name", Setup.Get($"Cache{Data.Input.Token}Name")),
                        New JProperty("name", Setup.Get($"Cache{Data.Input.Token}Name")),
                        New JProperty("id", Setup.Get($"Cache{Data.Input.Token}Uuid"))
                        New JProperty("id", Setup.Get($"Cache{Data.Input.Token}Uuid"))
@@ -712,7 +710,7 @@ LoginFinish:
                    New JProperty("accessToken", Setup.Get($"Cache{Data.Input.Token}Access")),
                    New JProperty("accessToken", Setup.Get($"Cache{Data.Input.Token}Access")),
                    New JProperty("requestUser", True)
                    New JProperty("requestUser", True)
                ).ToString(Newtonsoft.Json.Formatting.None),
                ).ToString(Newtonsoft.Json.Formatting.None),
-               Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
+               Headers:={{"Accept-Language", "zh-CN"}},
                ContentType:="application/json; charset=utf-8"))
                ContentType:="application/json; charset=utf-8"))
         '将登录结果输出
         '将登录结果输出
         If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!")
         If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!")
@@ -739,11 +737,10 @@ LoginFinish:
                 New JProperty("username", Data.Input.UserName),
                 New JProperty("username", Data.Input.UserName),
                 New JProperty("password", Data.Input.Password),
                 New JProperty("password", Data.Input.Password),
                 New JProperty("requestUser", True))
                 New JProperty("requestUser", True))
-            Dim LoginJson As JObject = GetJson(NetRequestRetry(
-                Url:=Data.Input.BaseUrl & "/authenticate",
-                Method:="POST",
-                Data:=RequestData.ToString(0),
-                Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
+            Dim LoginJson As JObject = GetJson(NetRequestByClientRetry(
+                Data.Input.BaseUrl & "/authenticate", HttpMethod.Post,
+                Content:=RequestData.ToString(Newtonsoft.Json.Formatting.None),
+                Headers:={{"Accept-Language", "zh-CN"}},
                 ContentType:="application/json; charset=utf-8"))
                 ContentType:="application/json; charset=utf-8"))
             '检查登录结果
             '检查登录结果
             If LoginJson("availableProfiles").Count = 0 Then
             If LoginJson("availableProfiles").Count = 0 Then
@@ -858,8 +855,9 @@ LoginFinish:
         '初始请求
         '初始请求
 Retry:
 Retry:
         McLaunchLog("开始微软登录步骤 1/6(原始登录)")
         McLaunchLog("开始微软登录步骤 1/6(原始登录)")
-        Dim PrepareJson As JObject = GetJson(NetRequestRetry("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", "POST",
-            $"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access", "application/x-www-form-urlencoded"))
+        Dim PrepareJson As JObject = GetJson(NetRequestByClientRetry("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", HttpMethod.Post,
+            Content:=$"client_id={OAuthClientId}&tenant=/consumers&scope=XboxLive.signin%20offline_access",
+            Accept:="application/x-www-form-urlencoded"))
         McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
         McLaunchLog("网页登录地址:" & PrepareJson("verification_uri").ToString)
 
 
         '弹窗
         '弹窗
@@ -888,9 +886,11 @@ Retry:
 
 
         Dim Result As String
         Dim Result As String
         Try
         Try
-            Result = NetRequestMultiple("https://login.live.com/oauth20_token.srf", "POST",
-                $"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access",
-                "application/x-www-form-urlencoded", 2)
+            Result = NetRequestByClientMultiple("https://login.live.com/oauth20_token.srf", HttpMethod.Post,
+                Content:=$"client_id={OAuthClientId}&refresh_token={Uri.EscapeDataString(Code)}&grant_type=refresh_token&scope=XboxLive.signin%20offline_access",
+                ContentType:="application/x-www-form-urlencoded",
+                Headers:={{"Accept-Language", "en-US,en;q=0.5"}, {"X-Requested-With", "XMLHttpRequest"}},
+                ThreadCount:=2)
         Catch ex As Exception
         Catch ex As Exception
             If ex.Message.ContainsF("must sign in again", True) OrElse ex.Message.ContainsF("password expired", True) OrElse
             If ex.Message.ContainsF("must sign in again", True) OrElse ex.Message.ContainsF("password expired", True) OrElse
                (ex.Message.Contains("refresh_token") AndAlso ex.Message.Contains("is not valid")) Then '#269
                (ex.Message.Contains("refresh_token") AndAlso ex.Message.Contains("is not valid")) Then '#269
@@ -918,8 +918,8 @@ Retry:
            ""RelyingParty"": ""http://auth.xboxlive.com"",
            ""RelyingParty"": ""http://auth.xboxlive.com"",
            ""TokenType"": ""JWT""
            ""TokenType"": ""JWT""
         }"
         }"
-        Dim Result As String = NetRequestMultiple("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
-
+        Dim Result As String = NetRequestByClientMultiple("https://user.auth.xboxlive.com/user/authenticate", HttpMethod.Post,
+            Content:=Request, ContentType:="application/json")
         Dim ResultJson As JObject = GetJson(Result)
         Dim ResultJson As JObject = GetJson(Result)
         Dim XBLToken As String = ResultJson("Token").ToString
         Dim XBLToken As String = ResultJson("Token").ToString
         Return XBLToken
         Return XBLToken
@@ -940,7 +940,8 @@ Retry:
                                  }"
                                  }"
         Dim Result As String
         Dim Result As String
         Try
         Try
-            Result = NetRequestMultiple("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
+            Result = NetRequestByClientMultiple("https://xsts.auth.xboxlive.com/xsts/authorize", HttpMethod.Post,
+                Content:=Request, ContentType:="application/json")
         Catch ex As Net.WebException
         Catch ex As Net.WebException
             '参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js
             '参考 https://github.com/PrismarineJS/prismarine-auth/blob/master/src/common/Constants.js
             If ex.Message.Contains("2148916227") Then
             If ex.Message.Contains("2148916227") Then
@@ -971,10 +972,15 @@ Retry:
             End If
             End If
         End Try
         End Try
 
 
-        Dim ResultJson As JObject = GetJson(Result)
-        Dim XSTSToken As String = ResultJson("Token").ToString
-        Dim UHS As String = ResultJson("DisplayClaims")("xui")(0)("uhs").ToString
-        Return {XSTSToken, UHS}
+        Try '这个 Try 块仅为了追踪 #6683 而添加
+            Dim ResultJson As JObject = GetJson(Result)
+            Dim XSTSToken As String = ResultJson("Token").ToString
+            Dim UHS As String = ResultJson("DisplayClaims")("xui")(0)("uhs").ToString
+            Return {XSTSToken, UHS}
+        Catch
+            Log("[Launch] 返回内容:" & vbCrLf & Result)
+            Throw
+        End Try
     End Function
     End Function
     '微软登录步骤 4:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
     '微软登录步骤 4:从 {XSTSToken, UHS} 获取 Minecraft AccessToken
     Private Function MsLoginStep4(Tokens As String()) As String
     Private Function MsLoginStep4(Tokens As String()) As String
@@ -983,7 +989,8 @@ Retry:
         Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
         Dim Request As String = New JObject(New JProperty("identityToken", $"XBL3.0 x={Tokens(1)};{Tokens(0)}")).ToString(0)
         Dim Result As String
         Dim Result As String
         Try
         Try
-            Result = NetRequestRetry("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json")
+            Result = NetRequestByClientRetry("https://api.minecraftservices.com/authentication/login_with_xbox", HttpMethod.Post,
+                Content:=Request, ContentType:="application/json")
         Catch ex As Net.WebException
         Catch ex As Net.WebException
             Dim Message As String = GetExceptionSummary(ex)
             Dim Message As String = GetExceptionSummary(ex)
             If Message.Contains("(429)") Then
             If Message.Contains("(429)") Then
@@ -1005,7 +1012,8 @@ Retry:
     Private Sub MsLoginStep5(AccessToken As String)
     Private Sub MsLoginStep5(AccessToken As String)
         McLaunchLog("开始微软登录步骤 5/6")
         McLaunchLog("开始微软登录步骤 5/6")
 
 
-        Dim Result As String = NetRequestMultiple("https://api.minecraftservices.com/entitlements/mcstore", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
+        Dim Result As String = NetRequestByClientMultiple("https://api.minecraftservices.com/entitlements/mcstore",
+            ContentType:="application/json", ThreadCount:=2, Headers:={{"Authorization", "Bearer " & AccessToken}})
         Try
         Try
             Dim ResultJson As JObject = GetJson(Result)
             Dim ResultJson As JObject = GetJson(Result)
             If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any) Then
             If Not (ResultJson.ContainsKey("items") AndAlso ResultJson("items").Any) Then
@@ -1026,7 +1034,8 @@ Retry:
 
 
         Dim Result As String
         Dim Result As String
         Try
         Try
-            Result = NetRequestMultiple("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
+            Result = NetRequestByClientMultiple("https://api.minecraftservices.com/minecraft/profile",
+                ContentType:="application/json", ThreadCount:=2, Headers:={{"Authorization", "Bearer " & AccessToken}})
         Catch ex As Net.WebException
         Catch ex As Net.WebException
             Dim Message As String = GetExceptionSummary(ex)
             Dim Message As String = GetExceptionSummary(ex)
             If Message.Contains("(429)") Then
             If Message.Contains("(429)") Then
@@ -1099,7 +1108,7 @@ Retry:
         If Len(Uuid) = 32 Then Return Uuid
         If Len(Uuid) = 32 Then Return Uuid
         '从官网获取
         '从官网获取
         Try
         Try
-            Dim GotJson As JObject = NetGetCodeByRequestRetry("https://api.mojang.com/users/profiles/minecraft/" & Name, IsJson:=True)
+            Dim GotJson As JObject = GetJson(NetRequestByClientRetry("https://api.mojang.com/users/profiles/minecraft/" & Name))
             If GotJson Is Nothing Then Throw New FileNotFoundException("正版玩家档案不存在(" & Name & ")")
             If GotJson Is Nothing Then Throw New FileNotFoundException("正版玩家档案不存在(" & Name & ")")
             Uuid = If(GotJson("id"), "")
             Uuid = If(GotJson("id"), "")
         Catch ex As Exception
         Catch ex As Exception
@@ -1205,6 +1214,12 @@ Retry:
             End If
             End If
         End If
         End If
 
 
+        'LiteLoader 检测
+        If McVersionCurrent.Version.HasLiteLoader AndAlso McVersionCurrent.Version.IsStandardVersion Then '不管非标准版本
+            '最高 Java 8
+            MaxVer = If(New Version(1, 8, 999, 999) < MaxVer, New Version(1, 8, 999, 999), MaxVer)
+        End If
+
         '统一通行证检测
         '统一通行证检测
         If Setup.Get("LoginType") = McLoginType.Nide Then
         If Setup.Get("LoginType") = McLoginType.Nide Then
             '至少 Java 8u101
             '至少 Java 8u101
@@ -1356,16 +1371,6 @@ Retry:
         If McLaunchJavaSelected.VersionCode >= 18 Then
         If McLaunchJavaSelected.VersionCode >= 18 Then
             If Not Arguments.Contains("-Dfile.encoding=") Then Arguments = "-Dfile.encoding=COMPAT " & Arguments
             If Not Arguments.Contains("-Dfile.encoding=") Then Arguments = "-Dfile.encoding=COMPAT " & Arguments
         End If
         End If
-        '替换参数
-        Dim ReplaceArguments = McLaunchArgumentsReplace(McVersionCurrent, Loader)
-        If String.IsNullOrWhiteSpace(ReplaceArguments("${version_type}")) Then
-            '若自定义信息为空,则去掉该部分
-            Arguments = Arguments.Replace(" --versionType ${version_type}", "")
-            ReplaceArguments("${version_type}") = """"""
-        End If
-        For Each entry As KeyValuePair(Of String, String) In ReplaceArguments
-            Arguments = Arguments.Replace(entry.Key, If(entry.Value.Contains(" ") OrElse entry.Value.Contains(":\"), """" & entry.Value & """", entry.Value))
-        Next
         'MJSB
         'MJSB
         Arguments = Arguments.Replace(" -Dos.name=Windows 10", " -Dos.name=""Windows 10""")
         Arguments = Arguments.Replace(" -Dos.name=Windows 10", " -Dos.name=""Windows 10""")
         '全屏
         '全屏
@@ -1374,31 +1379,47 @@ Retry:
         For Each Arg In CurrentLaunchOptions.ExtraArgs
         For Each Arg In CurrentLaunchOptions.ExtraArgs
             Arguments += " " & Arg.Trim
             Arguments += " " & Arg.Trim
         Next
         Next
+        '自定义
+        Dim ArgumentGame As String = Setup.Get("VersionAdvanceGame", Version:=McVersionCurrent)
+        Arguments += " " & If(ArgumentGame = "", Setup.Get("LaunchAdvanceGame"), ArgumentGame)
+        '进行参数替换
+        Dim ReplaceArguments = McLaunchArgumentsReplace(McVersionCurrent, Loader)
+        If String.IsNullOrWhiteSpace(ReplaceArguments("${version_type}")) Then
+            '若自定义信息为空,则去掉该部分
+            Arguments = Arguments.Replace(" --versionType ${version_type}", "")
+            ReplaceArguments("${version_type}") = """"""
+        End If
+        Dim FinalArguments As String = ""
+        For Each Argument In Arguments.Split(" ")
+            For Each Entry As KeyValuePair(Of String, String) In ReplaceArguments
+                Argument = Argument.Replace(Entry.Key, Entry.Value)
+            Next
+            If (Argument.Contains(" ") OrElse Argument.Contains(":\")) AndAlso Not Argument.EndsWithF("""") Then Argument = $"""{Argument}"""
+            FinalArguments += Argument & " "
+        Next
+        FinalArguments = FinalArguments.TrimEnd
         '进服
         '进服
         Dim Server As String = If(String.IsNullOrEmpty(CurrentLaunchOptions.ServerIp), Setup.Get("VersionServerEnter", McVersionCurrent), CurrentLaunchOptions.ServerIp)
         Dim Server As String = If(String.IsNullOrEmpty(CurrentLaunchOptions.ServerIp), Setup.Get("VersionServerEnter", McVersionCurrent), CurrentLaunchOptions.ServerIp)
         If Server.Length > 0 Then
         If Server.Length > 0 Then
             If McVersionCurrent.ReleaseTime > New Date(2023, 4, 4) Then
             If McVersionCurrent.ReleaseTime > New Date(2023, 4, 4) Then
                 'QuickPlay
                 'QuickPlay
-                Arguments += $" --quickPlayMultiplayer ""{Server}"""
+                FinalArguments += $" --quickPlayMultiplayer ""{Server}"""
             Else
             Else
                 '老版本
                 '老版本
                 If Server.Contains(":") Then
                 If Server.Contains(":") Then
                     '包含端口号
                     '包含端口号
-                    Arguments += " --server " & Server.Split(":")(0) & " --port " & Server.Split(":")(1)
+                    FinalArguments += $" --server {Server.Split(":")(0)} --port {Server.Split(":")(1)}"
                 Else
                 Else
                     '不包含端口号
                     '不包含端口号
-                    Arguments += " --server " & Server & " --port 25565"
+                    FinalArguments += $" --server {Server} --port 25565"
                 End If
                 End If
                 If McVersionCurrent.Version.HasOptiFine Then Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Critical)
                 If McVersionCurrent.Version.HasOptiFine Then Hint("OptiFine 与自动进入服务器可能不兼容,有概率导致材质丢失甚至游戏崩溃!", HintType.Critical)
             End If
             End If
         End If
         End If
-        '自定义
-        Dim ArgumentGame As String = Setup.Get("VersionAdvanceGame", Version:=McVersionCurrent)
-        Arguments += " " & If(ArgumentGame = "", Setup.Get("LaunchAdvanceGame"), ArgumentGame)
         '输出
         '输出
         McLaunchLog("Minecraft 启动参数:")
         McLaunchLog("Minecraft 启动参数:")
-        McLaunchLog(Arguments)
-        McLaunchArgument = Arguments
+        McLaunchLog(FinalArguments)
+        McLaunchArgument = FinalArguments
     End Sub
     End Sub
 
 
     'Jvm 部分(第一段)
     'Jvm 部分(第一段)
@@ -1424,12 +1445,12 @@ Retry:
         End If
         End If
         'Authlib-Injector
         'Authlib-Injector
         If McLoginLoader.Output.Type = "Auth" Then
         If McLoginLoader.Output.Type = "Auth" Then
-            If (McLaunchJavaSelected.VersionCode >= 6) Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT")
+            If McLaunchJavaSelected.VersionCode >= 6 Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") '信任系统根证书(5252#)
             Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
             Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
                 "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
                 "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
                 Setup.Get("VersionServerAuthServer", McVersionCurrent))
                 Setup.Get("VersionServerAuthServer", McVersionCurrent))
             Try
             Try
-                Dim Response As String = NetGetCodeByRequestRetry(Server, Encoding.UTF8)
+                Dim Response As String = NetRequestByClientRetry(Server, Encoding:=Encoding.UTF8)
                 DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
                 DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
                               " -Dauthlibinjector.side=client" &
                               " -Dauthlibinjector.side=client" &
                               " -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
                               " -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
@@ -1494,12 +1515,12 @@ NextVersion:
         End If
         End If
         'Authlib-Injector
         'Authlib-Injector
         If McLoginLoader.Output.Type = "Auth" Then
         If McLoginLoader.Output.Type = "Auth" Then
-            If (McLaunchJavaSelected.VersionCode >= 6) Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT")
+            If McLaunchJavaSelected.VersionCode >= 6 Then DataList.Add("-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT") '信任系统根证书(5252#)
             Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
             Dim Server As String = If(McLoginLoader.Input.Type = McLoginType.Legacy,
                 "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
                 "http://hiperauth.tech/api/yggdrasil-hiper/", 'HiPer 登录
                 Setup.Get("VersionServerAuthServer", Version:=McVersionCurrent))
                 Setup.Get("VersionServerAuthServer", Version:=McVersionCurrent))
             Try
             Try
-                Dim Response As String = NetGetCodeByRequestRetry(Server, Encoding.UTF8)
+                Dim Response As String = NetRequestByClientRetry(Server, Encoding:=Encoding.UTF8)
                 DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
                 DataList.Insert(0, "-javaagent:""" & PathPure & "authlib-injector.jar""=" & Server &
                               " -Dauthlibinjector.side=client" &
                               " -Dauthlibinjector.side=client" &
                               " -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
                               " -Dauthlibinjector.yggdrasil.prefetched=" & Convert.ToBase64String(Encoding.UTF8.GetBytes(Response)))
@@ -1533,10 +1554,8 @@ NextVersion:
             DeDuplicateDataList.Add(CurrentEntry.Trim.Replace("McEmu= ", "McEmu="))
             DeDuplicateDataList.Add(CurrentEntry.Trim.Replace("McEmu= ", "McEmu="))
         Next
         Next
 
 
-        '#3511 的清理
-        DeDuplicateDataList.Remove("-XX:MaxDirectMemorySize=256M")
-
         '去重
         '去重
+        DeDuplicateDataList.Remove("-XX:MaxDirectMemorySize=256M") '#3511 的清理
         Dim Result As String = Join(DeDuplicateDataList.Distinct.ToList, " ")
         Dim Result As String = Join(DeDuplicateDataList.Distinct.ToList, " ")
 
 
         '添加 MainClass
         '添加 MainClass

+ 108 - 86
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -169,8 +169,8 @@ Public Module ModMinecraft
             _McVersionCurrent = value '由于有可能是 Nothing,导致无法初始化,才得这样弄一圈
             _McVersionCurrent = value '由于有可能是 Nothing,导致无法初始化,才得这样弄一圈
             _McVersionLast = value
             _McVersionLast = value
             If value Is Nothing Then Return
             If value Is Nothing Then Return
-            '重置缓存的 Mod 文件夹
-            PageDownloadCompDetail.CachedFolder = Nothing
+            '重置缓存的下载文件夹
+            PageDownloadCompDetail.CachedFolder.Clear()
             '统一通行证重判
             '统一通行证重判
             If AniControlEnabled = 0 AndAlso
             If AniControlEnabled = 0 AndAlso
                Setup.Get("VersionServerNide", Version:=value) <> Setup.Get("CacheNideServer") AndAlso
                Setup.Get("VersionServerNide", Version:=value) <> Setup.Get("CacheNideServer") AndAlso
@@ -440,6 +440,33 @@ VersionSearchFinish:
         ''' </summary>
         ''' </summary>
         Public ReleaseTime As New Date(1970, 1, 1, 15, 0, 0)
         Public ReleaseTime As New Date(1970, 1, 1, 15, 0, 0)
 
 
+        ''' <summary>
+        ''' 获取该版本的 JSON 路径。
+        ''' </summary>
+        Public Function GetJsonPath() As String
+            Dim JsonPath As String = Path & Name & ".json"
+            If Not File.Exists(JsonPath) Then
+                '尝试寻找 JSON 文件
+                JsonPath = Nothing
+                For Each JsonCandidatePath In Directory.GetFiles(Path, "*.json")
+                    Try
+                        Dim JsonCandidate As JObject = GetJson(ReadFile(JsonCandidatePath))
+                        If Not JsonCandidate.ContainsKey("mainClass") Then Continue For
+                        If Not JsonCandidate.ContainsKey("type") Then Continue For
+                        If Not JsonCandidate.ContainsKey("id") Then Continue For
+                    Catch
+                    End Try
+                    JsonPath = JsonCandidatePath
+                    Exit For
+                Next
+                If JsonPath Is Nothing Then
+                    Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
+                Else
+                    Log("[Minecraft] 未找到同名版本 JSON,自动换用 " & JsonPath, LogLevel.Debug)
+                End If
+            End If
+            Return JsonPath
+        End Function
         ''' <summary>
         ''' <summary>
         ''' 该版本的 Json 文本。
         ''' 该版本的 Json 文本。
         ''' </summary>
         ''' </summary>
@@ -452,17 +479,7 @@ VersionSearchFinish:
                     Return TrimedJson.StartsWithF("{") AndAlso TrimedJson.EndsWithF("}")
                     Return TrimedJson.StartsWithF("{") AndAlso TrimedJson.EndsWithF("}")
                 End Function
                 End Function
                 If _JsonText Is Nothing Then
                 If _JsonText Is Nothing Then
-                    Dim JsonPath As String = Path & Name & ".json"
-                    If Not File.Exists(JsonPath) Then
-                        '如果文件夹下只有一个 JSON 文件,则将其作为版本 JSON
-                        Dim JsonFiles As String() = Directory.GetFiles(Path, "*.json")
-                        If JsonFiles.Count = 1 Then
-                            JsonPath = JsonFiles(0)
-                            Log("[Minecraft] 未找到同名版本 JSON,自动换用 " & JsonPath, LogLevel.Debug)
-                        Else
-                            Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
-                        End If
-                    End If
+                    Dim JsonPath As String = GetJsonPath()
                     _JsonText = ReadFile(JsonPath)
                     _JsonText = ReadFile(JsonPath)
                     '如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
                     '如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
                     If Not FastJsonCheck(_JsonText) Then
                     If Not FastJsonCheck(_JsonText) Then
@@ -540,9 +557,15 @@ Recheck:
                                 '继续循环
                                 '继续循环
                                 If Inherit.InheritVersion = InheritVersion Then Throw New Exception("版本依赖项出现嵌套:" & InheritVersion)
                                 If Inherit.InheritVersion = InheritVersion Then Throw New Exception("版本依赖项出现嵌套:" & InheritVersion)
                                 InheritVersion = Inherit.InheritVersion
                                 InheritVersion = Inherit.InheritVersion
-                                '合并
+                                '合并 Libraries 项:子版本放在前面,父版本放在后面(5978#:如果多个 jar 包中含有相同的类,Java 8 和之前的版本按照 -cp 指定的顺序选择第一个)
+                                Dim CurrentLib As JArray = _JsonObject("libraries").DeepClone()
+                                For Each LibToken In Inherit.JsonObject("libraries")
+                                    CurrentLib.Add(LibToken)
+                                Next
+                                '合并其他项:子版本优先于父版本
                                 Inherit.JsonObject.Merge(_JsonObject)
                                 Inherit.JsonObject.Merge(_JsonObject)
                                 _JsonObject = Inherit.JsonObject
                                 _JsonObject = Inherit.JsonObject
+                                _JsonObject("libraries") = CurrentLib
                                 GoTo Recheck
                                 GoTo Recheck
                             End If
                             End If
                         Catch ex As Exception
                         Catch ex As Exception
@@ -580,20 +603,18 @@ Recheck:
             Get
             Get
                 If Not JsonVersionInited Then
                 If Not JsonVersionInited Then
                     JsonVersionInited = True
                     JsonVersionInited = True
-                    If File.Exists(Path & Name & ".jar") Then
-                        Try
-                            Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
-                                Dim VersionJson As ZipArchiveEntry = JarArchive.GetEntry("version.json")
-                                If VersionJson IsNot Nothing Then
-                                    Using VersionJsonStream As New StreamReader(VersionJson.Open)
-                                        _JsonVersion = GetJson(VersionJsonStream.ReadToEnd)
-                                    End Using
-                                End If
+                    Try
+                        If Not File.Exists($"{Path}{Name}.jar") Then Exit Try
+                        Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+                            Dim VersionJson As ZipArchiveEntry = JarArchive.GetEntry("version.json")
+                            If VersionJson Is Nothing Then Exit Try
+                            Using VersionJsonStream As New StreamReader(VersionJson.Open)
+                                _JsonVersion = GetJson(VersionJsonStream.ReadToEnd)
                             End Using
                             End Using
-                        Catch ex As Exception
-                            Log(ex, "从版本 jar 中读取 version.json 失败")
-                        End Try
-                    End If
+                        End Using
+                    Catch ex As Exception
+                        Log(ex, $"从版本 jar 中读取 version.json 失败({Path}{Name}.jar)")
+                    End Try
                 End If
                 End If
                 Return _JsonVersion
                 Return _JsonVersion
             End Get
             End Get
@@ -1538,7 +1559,7 @@ OnLoaded:
     ''' 要求玩家选择一个皮肤文件,并进行相关校验。
     ''' 要求玩家选择一个皮肤文件,并进行相关校验。
     ''' </summary>
     ''' </summary>
     Public Function McSkinSelect() As McSkinInfo
     Public Function McSkinSelect() As McSkinInfo
-        Dim FileName As String = SelectFile("皮肤文件(*.png;*.jpg;*.webp)|*.png;*.jpg;*.webp", "选择皮肤文件")
+        Dim FileName As String = SelectFile("皮肤文件(*.png;*.jpg;*.jpeg;*.webp)|*.png;*.jpg;*.jpeg;*.webp", "选择皮肤文件")
 
 
         '验证有效性
         '验证有效性
         If FileName = "" Then Return New McSkinInfo With {.IsVaild = False}
         If FileName = "" Then Return New McSkinInfo With {.IsVaild = False}
@@ -1589,7 +1610,7 @@ OnLoaded:
             Case Else
             Case Else
                 Throw New ArgumentException("皮肤地址种类无效:" & If(Type, "null"))
                 Throw New ArgumentException("皮肤地址种类无效:" & If(Type, "null"))
         End Select
         End Select
-        Dim SkinString = NetGetCodeByRequestRetry(Url & Uuid)
+        Dim SkinString = NetRequestByClientRetry(Url & Uuid)
         If SkinString = "" Then Throw New Exception("皮肤返回值为空,可能是未设置自定义皮肤的用户")
         If SkinString = "" Then Throw New Exception("皮肤返回值为空,可能是未设置自定义皮肤的用户")
         '处理皮肤地址
         '处理皮肤地址
         Dim SkinValue As String
         Dim SkinValue As String
@@ -1611,7 +1632,7 @@ OnLoaded:
             Throw New Exception("用户未设置自定义皮肤")
             Throw New Exception("用户未设置自定义皮肤")
         Else
         Else
             Dim SkinUrl As String = SkinJson("textures")("skin")("url").ToString
             Dim SkinUrl As String = SkinJson("textures")("skin")("url").ToString
-            SkinValue = If(SkinUrl.Contains("minecraft.net"),SkinUrl.Replace("http:", "https:"),SkinUrl)
+            SkinValue = If(SkinUrl.Contains("minecraft.net/"), SkinUrl.Replace("http://", "https://"), SkinUrl)
         End If
         End If
         '保存缓存
         '保存缓存
         WriteIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", Uuid, SkinValue)
         WriteIni(PathTemp & "Cache\Skin\Index" & Type & ".ini", Uuid, SkinValue)
@@ -1628,9 +1649,9 @@ OnLoaded:
         Dim FileAddress As String = PathTemp & "Cache\Skin\" & GetHash(Address) & ".png"
         Dim FileAddress As String = PathTemp & "Cache\Skin\" & GetHash(Address) & ".png"
         SyncLock McSkinDownloadLock
         SyncLock McSkinDownloadLock
             If Not File.Exists(FileAddress) Then
             If Not File.Exists(FileAddress) Then
-                NetDownloadByClient(Address, FileAddress & NetDownloadEnd)
+                NetDownloadByClient(Address, FileAddress & ".PCLDownloading")
                 File.Delete(FileAddress)
                 File.Delete(FileAddress)
-                FileSystem.Rename(FileAddress & NetDownloadEnd, FileAddress)
+                FileSystem.Rename(FileAddress & ".PCLDownloading", FileAddress)
                 Log("[Minecraft] 皮肤下载成功:" & FileAddress)
                 Log("[Minecraft] 皮肤下载成功:" & FileAddress)
             End If
             End If
             Return FileAddress
             Return FileAddress
@@ -1773,61 +1794,60 @@ OnLoaded:
 
 
         '获取当前支持库列表
         '获取当前支持库列表
         Log("[Minecraft] 获取支持库列表:" & Version.Name)
         Log("[Minecraft] 获取支持库列表:" & Version.Name)
-        McLibListGet = McLibListGetWithJson(Version.JsonObject)
-        If Not IncludeVersionJar Then Return McLibListGet
+        Dim Result = McLibListGetWithJson(Version.JsonObject)
 
 
         '需要添加原版 Jar
         '需要添加原版 Jar
-        Dim RealVersion As McVersion
-        Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
-        If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
-            'HMCL 项直接使用自身的 Jar
-            '根据 Inherit 获取最深层版本
-            Dim OriginalVersion As McVersion = Version
-            '1.17+ 的 Forge 不寻找 Inherit
-            If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
-                Do Until OriginalVersion.InheritVersion = ""
-                    If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
-                    OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
-                Loop
+        If IncludeVersionJar Then
+            Dim RealVersion As McVersion
+            Dim RequiredJar As String = Version.JsonObject("jar")?.ToString
+            If Version.IsHmclFormatJson OrElse RequiredJar Is Nothing Then
+                'HMCL 项直接使用自身的 Jar
+                '根据 Inherit 获取最深层版本
+                Dim OriginalVersion As McVersion = Version
+                '1.17+ 的 Forge 不寻找 Inherit
+                If Not ((Version.Version.HasForge OrElse Version.Version.HasNeoForge) AndAlso Version.Version.McCodeMain >= 17) Then
+                    Do Until OriginalVersion.InheritVersion = ""
+                        If OriginalVersion.InheritVersion = OriginalVersion.Name Then Exit Do
+                        OriginalVersion = New McVersion(PathMcFolder & "versions\" & OriginalVersion.InheritVersion & "\")
+                    Loop
+                End If
+                '需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
+                '复现:启动一个 Snapshot 版本
+                RealVersion = New McVersion(OriginalVersion.Path)
+            Else
+                'Json 已提供 Jar 字段,使用该字段的信息
+                RealVersion = New McVersion(RequiredJar)
             End If
             End If
-            '需要新建对象,否则后面的 Check 会导致 McVersionCurrent 的 State 变回 Original
-            '复现:启动一个 Snapshot 版本
-            RealVersion = New McVersion(OriginalVersion.Path)
-        Else
-            'Json 已提供 Jar 字段,使用该字段的信息
-            RealVersion = New McVersion(RequiredJar)
-        End If
-        Dim ClientUrl As String, ClientSHA1 As String
-        '判断需求的版本是否存在
-        '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
-        If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
-            RealVersion = Version
-            Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
-        End If
-        '获取详细下载信息
-        If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
-            ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
-            ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
-        Else
-            ClientUrl = Nothing
-            ClientSHA1 = Nothing
+            Dim ClientUrl As String, ClientSHA1 As String
+            '判断需求的版本是否存在
+            '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在
+            If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then
+                RealVersion = Version
+                Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug)
+            End If
+            '获取详细下载信息
+            If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then
+                ClientUrl = RealVersion.JsonObject("downloads")("client")("url")
+                ClientSHA1 = RealVersion.JsonObject("downloads")("client")("sha1")
+            Else
+                ClientUrl = Nothing
+                ClientSHA1 = Nothing
+            End If
+            '把所需的原版 Jar 添加进去
+            Result.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
         End If
         End If
-        '把所需的原版 Jar 添加进去
-        McLibListGet.Add(New McLibToken With {.LocalPath = RealVersion.Path & RealVersion.Name & ".jar", .Size = 0, .IsNatives = False, .Url = ClientUrl, .SHA1 = ClientSHA1})
 
 
+        Return Result
     End Function
     End Function
     ''' <summary>
     ''' <summary>
     ''' 获取 Minecraft 某一版本忽视继承的支持库列表,即结果中没有继承项。
     ''' 获取 Minecraft 某一版本忽视继承的支持库列表,即结果中没有继承项。
     ''' </summary>
     ''' </summary>
     Public Function McLibListGetWithJson(JsonObject As JObject, Optional KeepSameNameDifferentVersionResult As Boolean = False, Optional CustomMcFolder As String = Nothing) As List(Of McLibToken)
     Public Function McLibListGetWithJson(JsonObject As JObject, Optional KeepSameNameDifferentVersionResult As Boolean = False, Optional CustomMcFolder As String = Nothing) As List(Of McLibToken)
         CustomMcFolder = If(CustomMcFolder, PathMcFolder)
         CustomMcFolder = If(CustomMcFolder, PathMcFolder)
-        Dim BasicArray As New List(Of McLibToken)
-
-        '添加基础 Json 项
-        Dim AllLibs As JArray = JsonObject("libraries")
 
 
         '转换为 LibToken
         '转换为 LibToken
-        For Each Library As JObject In AllLibs.Children
+        Dim BasicArray As New List(Of McLibToken)
+        For Each Library As JObject In CType(JsonObject("libraries"), JArray).Children
 
 
             '清理 null 项(BakaXL 会把没有的项序列化为 null,但会被 Newtonsoft 转换为 JValue,导致 Is Nothing = false;这导致了 #409)
             '清理 null 项(BakaXL 会把没有的项序列化为 null,但会被 Newtonsoft 转换为 JValue,导致 Is Nothing = false;这导致了 #409)
             For i = Library.Properties.Count - 1 To 0 Step -1
             For i = Library.Properties.Count - 1 To 0 Step -1
@@ -1946,8 +1966,7 @@ OnLoaded:
             Try
             Try
                 Log("[Minecraft] 开始获取统一通行证下载信息")
                 Log("[Minecraft] 开始获取统一通行证下载信息")
                 '测试链接:https://auth.mc-user.com:233/00000000000000000000000000000000/
                 '测试链接:https://auth.mc-user.com:233/00000000000000000000000000000000/
-                DownloadInfo = GetJson(NetGetCodeByLoader({
-                        "https://auth.mc-user.com:233/" & Setup.Get("VersionServerNide", Version:=Version)}, IsJson:=True))
+                DownloadInfo = GetJson(NetRequestByClientRetry("https://auth.mc-user.com:233/" & Setup.Get("VersionServerNide", Version:=Version)))
             Catch ex As Exception
             Catch ex As Exception
                 Log(ex, "获取统一通行证下载信息失败")
                 Log(ex, "获取统一通行证下载信息失败")
             End Try
             End Try
@@ -1969,10 +1988,9 @@ OnLoaded:
             '获取下载信息
             '获取下载信息
             Try
             Try
                 Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
                 Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
-                DownloadInfo = GetJson(NetGetCodeByLoader({
+                DownloadInfo = GetJson(NetRequestByClientRetry(
                         "https://authlib-injector.yushi.moe/artifact/latest.json",
                         "https://authlib-injector.yushi.moe/artifact/latest.json",
-                        "https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"
-                    }, IsJson:=True))
+                        BackupUrl:="https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"))
             Catch ex As Exception
             Catch ex As Exception
                 Log(ex, "获取 Authlib-Injector 下载信息失败")
                 Log(ex, "获取 Authlib-Injector 下载信息失败")
             End Try
             End Try
@@ -2017,12 +2035,16 @@ OnLoaded:
         '获取
         '获取
         For Each Token As McLibToken In Libs
         For Each Token As McLibToken In Libs
             '检查文件
             '检查文件
-            Dim Checker As New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
+            Dim Checker As FileChecker
+            If Token.Name.ContainsF("labymod") Then
+                Checker = New FileChecker '不检查 LabyMod 的文件,它们提供的文件校验信息是错的(#3225)
+            Else
+                Checker = New FileChecker(ActualSize:=If(Token.Size = 0, -1, Token.Size), Hash:=Token.SHA1)
+            End If
             If Checker.Check(Token.LocalPath) Is Nothing Then Continue For
             If Checker.Check(Token.LocalPath) Is Nothing Then Continue For
             '文件不符合,添加下载
             '文件不符合,添加下载
             Dim Urls As New List(Of String)
             Dim Urls As New List(Of String)
-            If Token.Url Is Nothing AndAlso Token.Name = "net.minecraftforge:forge:universal" Then
-                '特判修复 Forge 部分 universal 文件缺失 URL(#5455)
+            If Token.Url Is Nothing AndAlso Token.Name = "net.minecraftforge:forge:universal" Then '特判修复 Forge 部分 universal 文件缺失 URL(#5455)
                 Token.Url = "https://maven.minecraftforge.net" & Token.LocalPath.Replace(CustomMcFolder & "libraries", "").Replace("\", "/")
                 Token.Url = "https://maven.minecraftforge.net" & Token.LocalPath.Replace(CustomMcFolder & "libraries", "").Replace("\", "/")
             End If
             End If
             If Token.Url IsNot Nothing Then
             If Token.Url IsNot Nothing Then
@@ -2194,16 +2216,16 @@ OnLoaded:
                 Dim LocalPath As String
                 Dim LocalPath As String
                 If Json("map_to_resources") IsNot Nothing AndAlso Json("map_to_resources").ToObject(Of Boolean) Then
                 If Json("map_to_resources") IsNot Nothing AndAlso Json("map_to_resources").ToObject(Of Boolean) Then
                     'Remap
                     'Remap
-                    LocalPath = Version.PathIndie & "resources\" & File.Name.Replace("/", "\")
+                    LocalPath = $"{Version.PathIndie}resources\{File.Name}"
                 ElseIf Json("virtual") IsNot Nothing AndAlso Json("virtual").ToObject(Of Boolean) Then
                 ElseIf Json("virtual") IsNot Nothing AndAlso Json("virtual").ToObject(Of Boolean) Then
                     'Virtual
                     'Virtual
-                    LocalPath = PathMcFolder & "assets\virtual\legacy\" & File.Name.Replace("/", "\")
+                    LocalPath = $"{PathMcFolder}assets\virtual\legacy\{File.Name}"
                 Else
                 Else
                     '正常
                     '正常
-                    LocalPath = PathMcFolder & "assets\objects\" & Left(File.Value("hash").ToString, 2) & "\" & File.Value("hash").ToString
+                    LocalPath = $"{PathMcFolder}assets\objects\{Left(File.Value("hash").ToString, 2)}\{File.Value("hash")}"
                 End If
                 End If
                 Result.Add(New McAssetsToken With {
                 Result.Add(New McAssetsToken With {
-                    .LocalPath = LocalPath,
+                    .LocalPath = LocalPath.Replace("/", "\"),
                     .SourcePath = File.Name,
                     .SourcePath = File.Name,
                     .Hash = File.Value("hash").ToString,
                     .Hash = File.Value("hash").ToString,
                     .Size = File.Value("size").ToString
                     .Size = File.Value("size").ToString

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

@@ -1,7 +1,7 @@
 Imports System.IO.Compression
 Imports System.IO.Compression
 
 
 Public Module ModMod
 Public Module ModMod
-    Private Const LocalModCacheVersion As Integer = 7
+    Private Const LocalModCacheVersion As Integer = 8
 
 
     Public Class McMod
     Public Class McMod
 
 
@@ -946,8 +946,8 @@ Finished:
             Try
             Try
                 '步骤 1:获取 Hash 与对应的工程 ID
                 '步骤 1:获取 Hash 与对应的工程 ID
                 Dim ModrinthHashes = Mods.Select(Function(m) m.ModrinthHash).ToList()
                 Dim ModrinthHashes = Mods.Select(Function(m) m.ModrinthHash).ToList()
-                Dim ModrinthVersion = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files", "POST",
-                    $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
+                Dim ModrinthVersion As JObject = DlModRequest("https://api.modrinth.com/v2/version_files", HttpMethod.Post,
+                    $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")
                 Log($"[Mod] 从 Modrinth 获取到 {ModrinthVersion.Count} 个本地 Mod 的对应信息")
                 Log($"[Mod] 从 Modrinth 获取到 {ModrinthVersion.Count} 个本地 Mod 的对应信息")
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 If ModrinthVersion.Count = 0 Then Return
                 If ModrinthVersion.Count = 0 Then Return
@@ -967,9 +967,8 @@ Finished:
                 Log($"[Mod] 需要从 Modrinth 获取 {ModrinthMapping.Count} 个本地 Mod 的工程信息")
                 Log($"[Mod] 需要从 Modrinth 获取 {ModrinthMapping.Count} 个本地 Mod 的工程信息")
                 '步骤 3:获取工程信息
                 '步骤 3:获取工程信息
                 If Not ModrinthMapping.Any() Then Return
                 If Not ModrinthMapping.Any() Then Return
-                Dim ModrinthProject = CType(GetJson(DlModRequest(
-                    $"https://api.modrinth.com/v2/projects?ids=[""{ModrinthMapping.Keys.Join(""",""")}""]",
-                    "GET", "", "application/json")), JArray)
+                Dim ModrinthProject As JArray = DlModRequest(
+                    $"https://api.modrinth.com/v2/projects?ids=[""{ModrinthMapping.Keys.Join(""",""")}""]")
                 For Each ProjectJson In ModrinthProject
                 For Each ProjectJson In ModrinthProject
                     Dim Project As New CompProject(ProjectJson)
                     Dim Project As New CompProject(ProjectJson)
                     For Each Entry In ModrinthMapping(Project.Id)
                     For Each Entry In ModrinthMapping(Project.Id)
@@ -978,9 +977,9 @@ Finished:
                 Next
                 Next
                 Log($"[Mod] 已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
                 Log($"[Mod] 已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
                 '步骤 4:获取更新信息
                 '步骤 4:获取更新信息
-                Dim ModrinthUpdate = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files/update", "POST",
+                Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update", HttpMethod.Post,
                     $"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"", 
                     $"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"", 
-                    ""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")), JObject)
+                    ""loaders"": [""{ModLoaders.Join(""",""").ToLower}""],""game_versions"": [""{McVersion}""]}}", "application/json")
                 For Each Entry In Mods
                 For Each Entry In Mods
                     If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.CompFile Is Nothing Then Continue For
                     If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.CompFile Is Nothing Then Continue For
                     Dim UpdateFile As New CompFile(ModrinthUpdate(Entry.ModrinthHash), CompType.Mod)
                     Dim UpdateFile As New CompFile(ModrinthUpdate(Entry.ModrinthHash), CompType.Mod)
@@ -1015,8 +1014,8 @@ Finished:
                     CurseForgeHashes.Add(Entry.CurseForgeHash)
                     CurseForgeHashes.Add(Entry.CurseForgeHash)
                     If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return
                     If Loader.IsAbortedWithThread(CurrentTaskThread) Then Return
                 Next
                 Next
-                Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432", "POST",
-                    $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
+                Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432", HttpMethod.Post,
+                    $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")("data")("exactMatches")
                 Log($"[Mod] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
                 Log($"[Mod] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 If Not CurseForgeRaw.Any() Then Return
                 If Not CurseForgeRaw.Any() Then Return
@@ -1038,8 +1037,8 @@ Finished:
                 Log($"[Mod] 需要从 CurseForge 获取 {CurseForgeMapping.Count} 个本地 Mod 的工程信息")
                 Log($"[Mod] 需要从 CurseForge 获取 {CurseForgeMapping.Count} 个本地 Mod 的工程信息")
                 '步骤 3:获取工程信息
                 '步骤 3:获取工程信息
                 If Not CurseForgeMapping.Any() Then Return
                 If Not CurseForgeMapping.Any() Then Return
-                Dim CurseForgeProject = CType(GetJson(DlModRequest("https://api.curseforge.com/v1/mods", "POST",
-                    $"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")), JObject)("data")
+                Dim CurseForgeProject = DlModRequest("https://api.curseforge.com/v1/mods", HttpMethod.Post,
+                    $"{{""modIds"": [{CurseForgeMapping.Keys.Join(",")}]}}", "application/json")("data")
                 Dim UpdateFileIds As New Dictionary(Of Integer, List(Of McMod)) 'FileId -> 本地 Mod 文件列表
                 Dim UpdateFileIds As New Dictionary(Of Integer, List(Of McMod)) 'FileId -> 本地 Mod 文件列表
                 Dim FileIdToProjectSlug As New Dictionary(Of Integer, String)
                 Dim FileIdToProjectSlug As New Dictionary(Of Integer, String)
                 For Each ProjectJson In CurseForgeProject
                 For Each ProjectJson In CurseForgeProject
@@ -1079,8 +1078,8 @@ Finished:
                 Log($"[Mod] 已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
                 Log($"[Mod] 已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
                 '步骤 4:获取更新文件信息
                 '步骤 4:获取更新文件信息
                 If Not UpdateFileIds.Any() Then Return
                 If Not UpdateFileIds.Any() Then Return
-                Dim CurseForgeFiles = CType(GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST",
-                                    $"{{""fileIds"": [{UpdateFileIds.Keys.Join(",")}]}}", "application/json")), JObject)("data")
+                Dim CurseForgeFiles = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post,
+                                    $"{{""fileIds"": [{UpdateFileIds.Keys.Join(",")}]}}", "application/json")("data")
                 Dim UpdateFiles As New Dictionary(Of McMod, CompFile)
                 Dim UpdateFiles As New Dictionary(Of McMod, CompFile)
                 For Each FileJson In CurseForgeFiles
                 For Each FileJson In CurseForgeFiles
                     Dim File As New CompFile(FileJson, CompType.Mod)
                     Dim File As New CompFile(FileJson, CompType.Mod)

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

@@ -244,7 +244,7 @@ Retry:
             '获取 Mod 下载信息
             '获取 Mod 下载信息
             ModDownloadLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
             ModDownloadLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
             Sub(Task As LoaderTask(Of Integer, JArray))
             Sub(Task As LoaderTask(Of Integer, JArray))
-                Task.Output = GetJson(DlModRequest("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
+                Task.Output = DlModRequest("https://api.curseforge.com/v1/mods/files", HttpMethod.Post, "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json")("data")
                 '如果文件已被删除,则 API 会跳过那一项
                 '如果文件已被删除,则 API 会跳过那一项
                 If ModList.Count > Task.Output.Count Then Throw New Exception("整合包中的部分 Mod 版本已被 Mod 作者删除,所以没法继续安装了,请向整合包作者反馈该问题")
                 If ModList.Count > Task.Output.Count Then Throw New Exception("整合包中的部分 Mod 版本已被 Mod 作者删除,所以没法继续安装了,请向整合包作者反馈该问题")
             End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s
             End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s

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

@@ -1,6 +1,5 @@
 '由于包含加解密等安全信息,本文件中的部分代码已被删除
 '由于包含加解密等安全信息,本文件中的部分代码已被删除
 
 
-Imports System.Net.Http
 Imports System.Security.Cryptography
 Imports System.Security.Cryptography
 
 
 Friend Module ModSecret
 Friend Module ModSecret
@@ -14,7 +13,6 @@ Friend Module ModSecret
     'CurseForge API Key
     'CurseForge API Key
     Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "")
     Public CurseForgeAPIKey As String = If(Environment.GetEnvironmentVariable("PCL_CURSEFORGE_API_KEY"), "")
 
 
-
     Friend Sub SecretOnApplicationStart()
     Friend Sub SecretOnApplicationStart()
         '提升 UI 线程优先级
         '提升 UI 线程优先级
         Thread.CurrentThread.Priority = ThreadPriority.Highest
         Thread.CurrentThread.Priority = ThreadPriority.Highest
@@ -73,7 +71,7 @@ Friend Module ModSecret
 
 
 #Region "网络鉴权"
 #Region "网络鉴权"
 
 
-    Friend Function SecretCdnSign(UrlWithMark As String)
+    Friend Function SecretCdnSign(UrlWithMark As String) As String
         If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
         If Not UrlWithMark.EndsWithF("{CDN}") Then Return UrlWithMark
         Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
         Return UrlWithMark.Replace("{CDN}", "").Replace(" ", "%20")
     End Function
     End Function
@@ -92,7 +90,6 @@ Friend Module ModSecret
         If Url.Contains("api.curseforge.com") Then Req.Headers.Add("x-api-key", CurseForgeAPIKey)
         If Url.Contains("api.curseforge.com") Then Req.Headers.Add("x-api-key", CurseForgeAPIKey)
     End Sub
     End Sub
 
 
-
 #End Region
 #End Region
 
 
 #Region "字符串加解密"
 #Region "字符串加解密"

+ 0 - 59
Plain Craft Launcher 2/Modules/Reference/CookieWebClient.vb

@@ -1,59 +0,0 @@
-Imports System.Net
-
-Public Class CookieWebClient
-    Inherits WebClient
-
-    Public Sub New(container As CookieContainer, Headers As Dictionary(Of String, String))
-        Me.New(container)
-        For Each keyVal In Headers
-            Me.Headers(keyVal.Key) = keyVal.Value
-        Next
-    End Sub
-
-    Public Sub New()
-        Me.New(New CookieContainer())
-    End Sub
-
-    Public Sub New(container As CookieContainer)
-        Me.container = container
-    End Sub
-
-    Private Shadows ReadOnly container As New CookieContainer()
-
-    ''' <summary>
-    ''' 以毫秒为单位的超时。
-    ''' </summary>
-    Public Timeout As Integer = 600000
-
-    Protected Overrides Function GetWebRequest(address As Uri) As WebRequest
-        Dim r As WebRequest = MyBase.GetWebRequest(address)
-        Dim request = TryCast(r, HttpWebRequest)
-
-        If request IsNot Nothing Then
-            request.CookieContainer = container
-            request.Timeout = Timeout
-        End If
-
-        Return r
-    End Function
-
-    Protected Overrides Function GetWebResponse(request As WebRequest, result As IAsyncResult) As WebResponse
-        Dim response As WebResponse = MyBase.GetWebResponse(request, result)
-        ReadCookies(response)
-        Return response
-    End Function
-
-    Protected Overrides Function GetWebResponse(request As WebRequest) As WebResponse
-        Dim response As WebResponse = MyBase.GetWebResponse(request)
-        ReadCookies(response)
-        Return response
-    End Function
-
-    Private Sub ReadCookies(r As WebResponse)
-        Dim response = TryCast(r, HttpWebResponse)
-        If response IsNot Nothing Then
-            Dim cookies As CookieCollection = response.Cookies
-            container.Add(cookies)
-        End If
-    End Sub
-End Class

+ 22 - 23
Plain Craft Launcher 2/Modules/Base/ModDragHelper.vb → Plain Craft Launcher 2/Modules/ThirdParty/DragHelper.vb

@@ -1,11 +1,10 @@
-Imports System
+'来源:https://blog.csdn.net/simpleman2000/article/details/140952294
+
 Imports System.ComponentModel
 Imports System.ComponentModel
 Imports System.Runtime.InteropServices
 Imports System.Runtime.InteropServices
-Imports System.Text
 Imports System.Windows.Interop
 Imports System.Windows.Interop
 
 
-
-Public Class DragFileHelper
+Public Class DragHelper
 
 
     Public Event DragDrop As EventHandler
     Public Event DragDrop As EventHandler
 
 
@@ -32,22 +31,22 @@ Public Class DragFileHelper
     Public Property HwndIntPtrSource As HwndSource
     Public Property HwndIntPtrSource As HwndSource
 
 
     Public Sub AddHook()
     Public Sub AddHook()
-        Me.RemoveDragHook()
-        Me.HwndIntPtrSource.AddHook(AddressOf WndProc)
-        Dim handle As IntPtr = Me.HwndIntPtrSource.Handle
+        RemoveDragHook()
+        HwndIntPtrSource.AddHook(AddressOf WndProc)
+        Dim handle As IntPtr = HwndIntPtrSource.Handle
         If IsUserAnAdmin() Then RevokeDragDrop(handle)
         If IsUserAnAdmin() Then RevokeDragDrop(handle)
         DragAcceptFiles(handle, True)
         DragAcceptFiles(handle, True)
         ChangeMessageFilter(handle)
         ChangeMessageFilter(handle)
     End Sub
     End Sub
 
 
     Public Sub RemoveDragHook()
     Public Sub RemoveDragHook()
-        Me.HwndIntPtrSource.RemoveHook(AddressOf WndProc)
-        DragAcceptFiles(Me.HwndIntPtrSource.Handle, False)
+        HwndIntPtrSource.RemoveHook(AddressOf WndProc)
+        DragAcceptFiles(HwndIntPtrSource.Handle, False)
     End Sub
     End Sub
 
 
-    Private Function WndProc(ByVal hwnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByRef handled As Boolean) As IntPtr
+    Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
         Dim filePaths As String() = Nothing
         Dim filePaths As String() = Nothing
-        Dim point As POINT = New POINT()
+        Dim point As New POINT()
 
 
         If TryGetDropInfo(msg, wParam, filePaths, point) Then
         If TryGetDropInfo(msg, wParam, filePaths, point) Then
             DropPoint = point
             DropPoint = point
@@ -59,31 +58,31 @@ Public Class DragFileHelper
     End Function
     End Function
 
 
     <DllImport("user32.dll", SetLastError:=True)>
     <DllImport("user32.dll", SetLastError:=True)>
-    Private Shared Function ChangeWindowMessageFilterEx(ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean
+    Private Shared Function ChangeWindowMessageFilterEx(hWnd As IntPtr, msg As UInteger, action As UInteger, ByRef pChangeFilterStruct As CHANGEFILTERSTRUCT) As Boolean
     End Function
     End Function
 
 
     <DllImport("user32.dll", SetLastError:=True)>
     <DllImport("user32.dll", SetLastError:=True)>
-    Private Shared Function ChangeWindowMessageFilter(ByVal msg As UInteger, ByVal flags As UInteger) As Boolean
+    Private Shared Function ChangeWindowMessageFilter(msg As UInteger, flags As UInteger) As Boolean
     End Function
     End Function
 
 
     <DllImport("shell32.dll")>
     <DllImport("shell32.dll")>
-    Private Shared Sub DragAcceptFiles(ByVal hWnd As IntPtr, ByVal fAccept As Boolean)
+    Private Shared Sub DragAcceptFiles(hWnd As IntPtr, fAccept As Boolean)
     End Sub
     End Sub
 
 
     <DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
     <DllImport("shell32.dll", CharSet:=CharSet.Unicode)>
-    Private Shared Function DragQueryFile(ByVal hWnd As IntPtr, ByVal iFile As UInteger, ByVal lpszFile As StringBuilder, ByVal cch As Integer) As UInteger
+    Private Shared Function DragQueryFile(hWnd As IntPtr, iFile As UInteger, lpszFile As StringBuilder, cch As Integer) As UInteger
     End Function
     End Function
 
 
     <DllImport("shell32.dll")>
     <DllImport("shell32.dll")>
-    Private Shared Function DragQueryPoint(ByVal hDrop As IntPtr, ByRef lppt As POINT) As Boolean
+    Private Shared Function DragQueryPoint(hDrop As IntPtr, ByRef lppt As POINT) As Boolean
     End Function
     End Function
 
 
     <DllImport("shell32.dll")>
     <DllImport("shell32.dll")>
-    Private Shared Sub DragFinish(ByVal hDrop As IntPtr)
+    Private Shared Sub DragFinish(hDrop As IntPtr)
     End Sub
     End Sub
 
 
     <DllImport("ole32.dll")>
     <DllImport("ole32.dll")>
-    Private Shared Function RevokeDragDrop(ByVal hWnd As IntPtr) As Integer
+    Private Shared Function RevokeDragDrop(hWnd As IntPtr) As Integer
     End Function
     End Function
 
 
     <DllImport("shell32.dll")>
     <DllImport("shell32.dll")>
@@ -109,13 +108,13 @@ Public Class DragFileHelper
     Private Const MSGFLT_ADD As UInteger = 1
     Private Const MSGFLT_ADD As UInteger = 1
     Private Const MAX_PATH As Integer = 260
     Private Const MAX_PATH As Integer = 260
 
 
-    Private Shared Sub ChangeMessageFilter(ByVal handle As IntPtr)
+    Private Shared Sub ChangeMessageFilter(handle As IntPtr)
         Dim ver As Version = Environment.OSVersion.Version
         Dim ver As Version = Environment.OSVersion.Version
         Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0)
         Dim isVistaOrHigher As Boolean = ver >= New Version(6, 0)
         Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1)
         Dim isNt61OrHiger As Boolean = ver >= New Version(6, 1)
 
 
         If isVistaOrHigher Then
         If isVistaOrHigher Then
-            Dim status As CHANGEFILTERSTRUCT = New CHANGEFILTERSTRUCT With {.cbSize = 8}
+            Dim status As New CHANGEFILTERSTRUCT With {.cbSize = 8}
             For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA}
             For Each msg As UInteger In New UInteger() {WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA}
                 Dim [error] As Boolean = False
                 Dim [error] As Boolean = False
                 If isNt61OrHiger Then
                 If isNt61OrHiger Then
@@ -129,7 +128,7 @@ Public Class DragFileHelper
         End If
         End If
     End Sub
     End Sub
 
 
-    Private Shared Function TryGetDropInfo(ByVal msg As Integer, ByVal wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean
+    Private Shared Function TryGetDropInfo(msg As Integer, wParam As IntPtr, ByRef dropFilePaths As String(), ByRef dropPoint As POINT) As Boolean
         dropFilePaths = Nothing
         dropFilePaths = Nothing
         dropPoint = New POINT()
         dropPoint = New POINT()
 
 
@@ -141,7 +140,7 @@ Public Class DragFileHelper
         For i As UInteger = 0 To fileCount - 1
         For i As UInteger = 0 To fileCount - 1
             Dim sb As New StringBuilder(MAX_PATH)
             Dim sb As New StringBuilder(MAX_PATH)
             Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity)
             Dim result As UInteger = DragQueryFile(wParam, i, sb, sb.Capacity)
-            If result > 0 Then dropFilePaths(CInt(i)) = sb.ToString()
+            If result > 0 Then dropFilePaths(i) = sb.ToString()
         Next
         Next
 
 
         DragQueryPoint(wParam, dropPoint)
         DragQueryPoint(wParam, dropPoint)
@@ -149,4 +148,4 @@ Public Class DragFileHelper
         Return True
         Return True
     End Function
     End Function
 
 
-End Class
+End Class

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

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

+ 53 - 13
Plain Craft Launcher 2/My Project/Resources.Designer.vb

@@ -63,9 +63,29 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property Custom() As Byte()
+        Friend ReadOnly Property CacheCow_Client() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("CacheCow.Client", resourceCulture)
+                Return CType(obj,Byte())
+            End Get
+        End Property
+        
+        '''<summary>
+        '''  查找 System.Byte[] 类型的本地化资源。
+        '''</summary>
+        Friend ReadOnly Property CacheCow_Client_FileStore() As Byte()
+            Get
+                Dim obj As Object = ResourceManager.GetObject("CacheCow.Client.FileStore", resourceCulture)
+                Return CType(obj,Byte())
+            End Get
+        End Property
+        
+        '''<summary>
+        '''  查找 System.Byte[] 类型的本地化资源。
+        '''</summary>
+        Friend ReadOnly Property CacheCow_Common() As Byte()
+            Get
+                Dim obj As Object = ResourceManager.GetObject("CacheCow.Common", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -73,9 +93,9 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property Dialogs() As Byte()
+        Friend ReadOnly Property Custom() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("Dialogs", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("Custom", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -105,7 +125,7 @@ Namespace My.Resources
         '''</summary>
         '''</summary>
         Friend ReadOnly Property Imazen_WebP() As Byte()
         Friend ReadOnly Property Imazen_WebP() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("Imazen_WebP", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("Imazen.WebP", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -123,9 +143,9 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property Json() As Byte()
+        Friend ReadOnly Property libwebp64() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("Json", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("libwebp64", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -133,9 +153,9 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property libwebp64() As Byte()
+        Friend ReadOnly Property ModData() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("libwebp64", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("ModData", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -143,9 +163,9 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property ModData() As Byte()
+        Friend ReadOnly Property NAudio() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("ModData", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("NAudio", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property
@@ -153,9 +173,29 @@ Namespace My.Resources
         '''<summary>
         '''<summary>
         '''  查找 System.Byte[] 类型的本地化资源。
         '''  查找 System.Byte[] 类型的本地化资源。
         '''</summary>
         '''</summary>
-        Friend ReadOnly Property NAudio() As Byte()
+        Friend ReadOnly Property Newtonsoft_Json() As Byte()
             Get
             Get
-                Dim obj As Object = ResourceManager.GetObject("NAudio", resourceCulture)
+                Dim obj As Object = ResourceManager.GetObject("Newtonsoft.Json", resourceCulture)
+                Return CType(obj,Byte())
+            End Get
+        End Property
+        
+        '''<summary>
+        '''  查找 System.Byte[] 类型的本地化资源。
+        '''</summary>
+        Friend ReadOnly Property Ookii_Dialogs_Wpf() As Byte()
+            Get
+                Dim obj As Object = ResourceManager.GetObject("Ookii.Dialogs.Wpf", resourceCulture)
+                Return CType(obj,Byte())
+            End Get
+        End Property
+        
+        '''<summary>
+        '''  查找 System.Byte[] 类型的本地化资源。
+        '''</summary>
+        Friend ReadOnly Property System_Net_Http_Formatting() As Byte()
+            Get
+                Dim obj As Object = ResourceManager.GetObject("System.Net.Http.Formatting", resourceCulture)
                 Return CType(obj,Byte())
                 Return CType(obj,Byte())
             End Get
             End Get
         End Property
         End Property

+ 19 - 7
Plain Craft Launcher 2/My Project/Resources.resx

@@ -118,27 +118,30 @@
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   </resheader>
   <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
   <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="CacheCow.Client" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\CacheCow.Client.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="CacheCow.Client.FileStore" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\CacheCow.Client.FileStore.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="CacheCow.Common" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\CacheCow.Common.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
   <data name="Custom" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="Custom" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\resources\custom.xaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\resources\custom.xaml;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
-  <data name="Dialogs" type="System.Resources.ResXFileRef, System.Windows.Forms">
-    <value>..\resources\ookii.dialogs.wpf.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </data>
   <data name="ForgeInstaller" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="ForgeInstaller" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\forge-installer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\forge-installer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
   <data name="Help" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="Help" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\Help.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\Help.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
-  <data name="Imazen_WebP" type="System.Resources.ResXFileRef, System.Windows.Forms">
+  <data name="Imazen.WebP" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\Imazen.WebP.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\Imazen.WebP.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
   <data name="JavaWrapper" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="JavaWrapper" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\java-wrapper.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\java-wrapper.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
-  <data name="Json" type="System.Resources.ResXFileRef, System.Windows.Forms">
-    <value>..\Resources\Newtonsoft.Json.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </data>
   <data name="libwebp64" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="libwebp64" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\libwebp64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\libwebp64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
@@ -148,6 +151,15 @@
   <data name="NAudio" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="NAudio" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\NAudio.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\NAudio.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>
+  <data name="Newtonsoft.Json" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Newtonsoft.Json.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="Ookii.Dialogs.Wpf" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\resources\ookii.dialogs.wpf.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="System.Net.Http.Formatting" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\System.Net.Http.Formatting.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
   <data name="Transformer" type="System.Resources.ResXFileRef, System.Windows.Forms">
   <data name="Transformer" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\transformer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     <value>..\Resources\transformer.jar;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </data>
   </data>

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

@@ -92,4 +92,4 @@
             <local:MyLoading Text="正在获取XXX列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
             <local:MyLoading Text="正在获取XXX列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
         </local:MyCard>
         </local:MyCard>
     </StackPanel>
     </StackPanel>
-</local:MyScrollViewer>
+</local:MyScrollViewer>

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

@@ -99,7 +99,6 @@ Public Class PageComp
             End If
             End If
             TargetVersion = Nothing
             TargetVersion = Nothing
             If TargetName IsNot Nothing Then
             If TargetName IsNot Nothing Then
-                '设置搜索框内容
                 TextSearchName.Text = TargetName
                 TextSearchName.Text = TargetName
                 TargetName = Nothing
                 TargetName = Nothing
             End If
             End If

+ 10 - 15
Plain Craft Launcher 2/Pages/PageDownload/Comp/PageDownloadCompDetail.xaml.vb

@@ -73,13 +73,14 @@
     '筛选类型相同的结果(Modrinth 会返回 Mod、服务端插件、数据包混合的列表)
     '筛选类型相同的结果(Modrinth 会返回 Mod、服务端插件、数据包混合的列表)
     Private Function GetResults() As List(Of CompFile)
     Private Function GetResults() As List(Of CompFile)
         Dim Results As List(Of CompFile) = CompFileLoader.Output
         Dim Results As List(Of CompFile) = CompFileLoader.Output
-        If PageType = CompType.Any Then
-            Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList
-        ElseIf PageType = CompType.Shader OrElse PageType = CompType.ResourcePack Then
-            Results = Results.ToList
-        Else
-            Results = Results.Where(Function(r) r.Type = PageType).ToList
-        End If
+        Select Case PageType
+            Case CompType.Any
+                Results = Results.Where(Function(r) r.Type <> CompType.Plugin).ToList
+            Case CompType.Shader, CompType.ResourcePack
+                '不筛选光影和资源包,否则原版光影会因为是资源包格式而被过滤(#6473)
+            Case Else
+                Results = Results.Where(Function(r) r.Type = PageType).ToList
+        End Select
         Return Results
         Return Results
     End Function
     End Function
     Private Sub Load_OnFinish()
     Private Sub Load_OnFinish()
@@ -358,7 +359,7 @@
                         Return False
                         Return False
                     End Function
                     End Function
                     '获取常规资源默认下载位置
                     '获取常规资源默认下载位置
-                    If CachedFolder.ContainsKey(Project.Type) AndAlso Not String.IsNullOrEmpty(CachedFolder(Project.Type)) Then
+                    If CachedFolder.ContainsKey(Project.Type) Then
                         DefaultFolder = CachedFolder(Project.Type)
                         DefaultFolder = CachedFolder(Project.Type)
                         Log($"[Comp] 使用上次下载时的文件夹作为默认下载位置:{DefaultFolder}")
                         Log($"[Comp] 使用上次下载时的文件夹作为默认下载位置:{DefaultFolder}")
                     ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then
                     ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then
@@ -424,13 +425,7 @@
                     If Not Target.Contains("\") Then Return
                     If Not Target.Contains("\") Then Return
                     '构造步骤加载器
                     '构造步骤加载器
                     Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
                     Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
-                    If Target <> DefaultFolder Then
-                        If CachedFolder.ContainsKey(Project.Type) Then
-                            CachedFolder(Project.Type) = GetPathFromFullPath(Target)
-                        Else
-                            CachedFolder.Add(Project.Type, GetPathFromFullPath(Target))
-                        End If
-                    End If
+                    If Target <> DefaultFolder Then CachedFolder(Project.Type) = GetPathFromFullPath(Target)
                     Dim Loaders As New List(Of LoaderBase)
                     Dim Loaders As New List(Of LoaderBase)
                     Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
                     Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
                     '启动
                     '启动

+ 30 - 27
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -570,7 +570,8 @@ pause"
             '官方源
             '官方源
             Dim PageData As String
             Dim PageData As String
             Try
             Try
-                PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
+                PageData = NetRequestByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile,
+                    Encoding:=New UTF8Encoding(False), Timeout:=15000, Accept:="text/html", UseBrowserUserAgent:=True)
                 Task.Progress = 0.8
                 Task.Progress = 0.8
                 Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
                 Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
                 Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
                 Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
@@ -733,7 +734,8 @@ Retry:
             '官方源
             '官方源
             Dim PageData As String
             Dim PageData As String
             Try
             Try
-                PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html", True)
+                PageData = NetRequestByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile,
+                    Encoding:=New UTF8Encoding(False), Timeout:=15000, Accept:="text/html", UseBrowserUserAgent:=True)
                 Task.Progress = 0.8
                 Task.Progress = 0.8
                 Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
                 Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
                 Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
                 Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources.Last)
@@ -1340,7 +1342,7 @@ Retry:
                     If Json("data") IsNot Nothing AndAlso Json("data")("MOJMAPS") IsNot Nothing Then
                     If Json("data") IsNot Nothing AndAlso Json("data")("MOJMAPS") IsNot Nothing Then
                         '下载原版 Json 文件
                         '下载原版 Json 文件
                         Task.Progress = 0.4
                         Task.Progress = 0.4
-                        Dim RawJson As JObject = GetJson(NetGetCodeByLoader(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
+                        Dim RawJson As JObject = GetJson(NetRequestByLoader(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
                         '[net.minecraft:client:1.17.1-20210706.113038:mappings@txt] 或 @tsrg]
                         '[net.minecraft:client:1.17.1-20210706.113038:mappings@txt] 或 @tsrg]
                         Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).BeforeFirst("@")
                         Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).BeforeFirst("@")
                         Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
                         Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
@@ -1641,30 +1643,31 @@ Retry:
     Public Sub McDownloadForgeRecommendedRefresh()
     Public Sub McDownloadForgeRecommendedRefresh()
         If IsForgeRecommendedRefreshed Then Return
         If IsForgeRecommendedRefreshed Then Return
         IsForgeRecommendedRefreshed = True
         IsForgeRecommendedRefreshed = True
-        RunInNewThread(Sub()
-                           Try
-                               Log("[Download] 刷新 Forge 推荐版本缓存开始")
-                               Dim Result As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/forge/promos")
-                               If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
-                               Dim ResultJson As JContainer = GetJson(Result)
-                               '获取所有推荐版本列表
-                               Dim RecommendedList As New List(Of String)
-                               For Each Version As JObject In ResultJson
-                                   If Version("name") Is Nothing OrElse Version("build") Is Nothing Then Continue For
-                                   Dim Name As String = Version("name")
-                                   If Not Name.EndsWithF("-recommended") Then Continue For
-                                   '内容为:"1.15.2":"31.2.0"
-                                   RecommendedList.Add("""" & Name.Replace("-recommended", """:""" & Version("build")("version").ToString & """"))
-                               Next
-                               If RecommendedList.Count < 5 Then Throw New Exception("获取的推荐版本数过少(" & Result & ")")
-                               '保存
-                               Dim CacheJson As String = "{" & Join(RecommendedList, ",") & "}"
-                               WriteFile(PathTemp & "Cache\ForgeRecommendedList.json", CacheJson)
-                               Log("[Download] 刷新 Forge 推荐版本缓存成功")
-                           Catch ex As Exception
-                               Log(ex, "刷新 Forge 推荐版本缓存失败")
-                           End Try
-                       End Sub, "ForgeRecommendedRefresh")
+        RunInNewThread(
+        Sub()
+            Try
+                Log("[Download] 刷新 Forge 推荐版本缓存开始")
+                Dim Result As String = NetRequestByClientRetry("https://bmclapi2.bangbang93.com/forge/promos")
+                If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
+                Dim ResultJson As JContainer = GetJson(Result)
+                '获取所有推荐版本列表
+                Dim RecommendedList As New List(Of String)
+                For Each Version As JObject In ResultJson
+                    If Version("name") Is Nothing OrElse Version("build") Is Nothing Then Continue For
+                    Dim Name As String = Version("name")
+                    If Not Name.EndsWithF("-recommended") Then Continue For
+                    '内容为:"1.15.2":"31.2.0"
+                    RecommendedList.Add("""" & Name.Replace("-recommended", """:""" & Version("build")("version").ToString & """"))
+                Next
+                If RecommendedList.Count < 5 Then Throw New Exception("获取的推荐版本数过少(" & Result & ")")
+                '保存
+                Dim CacheJson As String = "{" & Join(RecommendedList, ",") & "}"
+                WriteFile(PathTemp & "Cache\ForgeRecommendedList.json", CacheJson)
+                Log("[Download] 刷新 Forge 推荐版本缓存成功")
+            Catch ex As Exception
+                Log(ex, "刷新 Forge 推荐版本缓存失败")
+            End Try
+        End Sub, "ForgeRecommendedRefresh")
     End Sub
     End Sub
     Private IsForgeRecommendedRefreshed As Boolean = False
     Private IsForgeRecommendedRefreshed As Boolean = False
 
 

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

@@ -27,8 +27,7 @@
                         <local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" />
                         <local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" />
                         <local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" />
                         <local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" />
                         <local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" />
                         <local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" />
-						<local:MyHint Text="1.20.5+ 没有 OptiFabric,无法使用 OptiFine,如需加载光影请考虑使用其他 Mod" Margin="0,10,0,0" x:Name="HintModOptiFineHigh" Theme="Blue" />
-						<local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
+                        <local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwaped="True" CanSwap="True" SwapLogoRight="True">
                         <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" />
                         <StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" />
                         <Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
                         <Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
                             <Grid.RenderTransform>
                             <Grid.RenderTransform>

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

@@ -476,11 +476,6 @@
         Else
         Else
             HintModOptiFine.Visibility = Visibility.Collapsed
             HintModOptiFine.Visibility = Visibility.Collapsed
         End If
         End If
-        If SelectedFabric IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.21.4") > 0 Then
-            HintModOptiFineHigh.Visibility = Visibility.Visible
-        Else
-            HintModOptiFineHigh.Visibility = Visibility.Collapsed
-        End If
         '结束
         '结束
         IsReloading = False
         IsReloading = False
     End Sub
     End Sub
@@ -676,13 +671,16 @@
         If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
         If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
         If LoadOptiFine Is Nothing OrElse LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……"
         If LoadOptiFine Is Nothing OrElse LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……"
         If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFine.State, Object).Error.Message
         If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFine.State, Object).Error.Message
-        '检查 Fabric 1.20.5+:没有 OptiFabric 故全部不兼容
-        If SelectedFabric IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then Return "与 Fabric 不兼容"
         '检查 Forge 1.13 - 1.14.3:全部不兼容
         '检查 Forge 1.13 - 1.14.3:全部不兼容
         If SelectedForge IsNot Nothing AndAlso
         If SelectedForge IsNot Nothing AndAlso
             VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then
             VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then
             Return "与 Forge 不兼容"
             Return "与 Forge 不兼容"
         End If
         End If
+        '检查 Fabric 1.20.5+:全部不兼容
+        If SelectedFabric IsNot Nothing AndAlso
+            VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then
+            Return "与 Fabric 不兼容"
+        End If
         '检查 Forge 版本
         '检查 Forge 版本
         Dim HasAny As Boolean = False
         Dim HasAny As Boolean = False
         Dim HasRequiredVersion As Boolean = False
         Dim HasRequiredVersion As Boolean = False
@@ -988,8 +986,11 @@
     Private Function LoadFabricGetError() As String
     Private Function LoadFabricGetError() As String
         If LoadFabric Is Nothing OrElse LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……"
         If LoadFabric Is Nothing OrElse LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "加载中……"
         If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message
         If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message
-        '检查 Fabric 1.20.5+:没有 OptiFabric 故全部不兼容
-        If SelectedOptiFine IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then Return "与 OptiFine 不兼容"
+        '检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容
+        If SelectedOptiFine IsNot Nothing AndAlso
+            VersionSortInteger(SelectedMinecraftId, "1.20.4") > 0 Then
+            Return "与 OptiFine 不兼容"
+        End If
         For Each Version As JObject In DlFabricListLoader.Output.Value("game")
         For Each Version As JObject In DlFabricListLoader.Output.Value("game")
             If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
             If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
                 If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
                 If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"

+ 11 - 9
Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb

@@ -105,13 +105,15 @@
         Dim UnknownFailureCount As Integer = 0
         Dim UnknownFailureCount As Integer = 0
         Do While Not MyConverter.IsExited
         Do While Not MyConverter.IsExited
             Try
             Try
-                Dim Result = NetRequestOnce(
-                    "https://login.microsoftonline.com/consumers/oauth2/v2.0/token", "POST",
-                    "grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
-                    "client_id=" & OAuthClientId & "&" &
-                    "device_code=" & DeviceCode & "&" &
-                    "scope=XboxLive.signin%20offline_access",
-                    "application/x-www-form-urlencoded", 5000 + UnknownFailureCount * 5000, MakeLog:=False)
+                Dim Result = NetRequestByClient(
+                    "https://login.microsoftonline.com/consumers/oauth2/v2.0/token", HttpMethod.Post,
+                    Content:=
+                        "grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" &
+                        "client_id=" & OAuthClientId & "&" &
+                        "device_code=" & DeviceCode & "&" &
+                        "scope=XboxLive.signin%20offline_access",
+                    ContentType:="application/x-www-form-urlencoded",
+                    Timeout:=5000 + UnknownFailureCount * 5000)
                 '获取结果
                 '获取结果
                 Dim ResultJson As JObject = GetJson(Result)
                 Dim ResultJson As JObject = GetJson(Result)
                 McLaunchLog($"令牌过期时间:{ResultJson("expires_in")} 秒")
                 McLaunchLog($"令牌过期时间:{ResultJson("expires_in")} 秒")
@@ -125,8 +127,8 @@
                 ElseIf ex.Message.Contains("expired_token") Then
                 ElseIf ex.Message.Contains("expired_token") Then
                     Finished(New Exception("$登录用时太长啦,重新试试吧!"))
                     Finished(New Exception("$登录用时太长啦,重新试试吧!"))
                     Return
                     Return
-                Else If ex.Message.Contains("Account security interrupt") Then
-                    Finished(New Exception("$非常抱歉,该账号由于安全问题无法登陆,请前往 Microsoft 账户页获取更多信息。"))
+                ElseIf ex.Message.Contains("Account security interrupt") Then
+                    Finished(New Exception("$该账号由于安全问题无法登陆,请前往微软账户页获取更多信息。"))
                     Return
                     Return
                 ElseIf ex.Message.Contains("service abuse") Then
                 ElseIf ex.Message.Contains("service abuse") Then
                     Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。"))
                     Finished(New Exception("$非常抱歉,该账号已被微软封禁,无法登录。"))

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

@@ -259,12 +259,13 @@ Retry:
                 End Sub)
                 End Sub)
                 If SelId Is Nothing Then Return
                 If SelId Is Nothing Then Return
                 '发送请求
                 '发送请求
-                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}})
+                Dim Result As String = NetRequestByClientRetry("https://api.minecraftservices.com/minecraft/profile/capes/active",
+                    If(SelId = 0, HttpMethod.Delete, HttpMethod.Put),
+                    Content:=If(SelId = 0, "", New JObject(New JProperty("capeId", SkinData("capes")(SelId - 1)("id"))).ToString(0)),
+                    ContentType:="application/json",
+                    Headers:={{"Authorization", "Bearer " & AccessToken}})
                 If Result.Contains("""errorMessage""") Then
                 If Result.Contains("""errorMessage""") Then
-                    Hint("更改披风失败:" & GetJson(Result)("errorMessage"), HintType.Critical)
+                    Hint("更改披风失败:" & GetJson(Result)("errorMessage").ToString, HintType.Critical)
                     Return
                     Return
                 Else
                 Else
                     Hint("更改披风成功!", HintType.Finish)
                     Hint("更改披风成功!", HintType.Finish)

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

@@ -156,7 +156,7 @@ Download:
             Dim Version As String = ""
             Dim Version As String = ""
             Dim NeedDownload As Boolean = True
             Dim NeedDownload As Boolean = True
             Try
             Try
-                Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
+                Version = NetRequestByClientRetry(VersionAddress)
                 If Version.Length > 1000 Then Throw New Exception($"获取的主页版本过长({Version.Length} 字符)")
                 If Version.Length > 1000 Then Throw New Exception($"获取的主页版本过长({Version.Length} 字符)")
                 Dim CurrentVersion As String = Setup.Get("CacheSavedPageVersion")
                 Dim CurrentVersion As String = Setup.Get("CacheSavedPageVersion")
                 If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
                 If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
@@ -171,7 +171,7 @@ Download:
             End Try
             End Try
             '实际下载
             '实际下载
             If NeedDownload Then
             If NeedDownload Then
-                Dim FileContent As String = NetGetCodeByRequestRetry(Address)
+                Dim FileContent As String = NetRequestByClientRetry(Address)
                 Log($"[Page] 已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
                 Log($"[Page] 已联网下载主页,内容长度:{FileContent.Length},来源:{Address}")
                 Setup.Set("CacheSavedPageUrl", Address)
                 Setup.Set("CacheSavedPageUrl", Address)
                 Setup.Set("CacheSavedPageVersion", Version)
                 Setup.Set("CacheSavedPageVersion", Version)
@@ -231,7 +231,9 @@ Download:
             Try
             Try
                 '修改时应同时修改 PageOtherHelpDetail.Init
                 '修改时应同时修改 PageOtherHelpDetail.Init
                 Content = HelpArgumentReplace(Content)
                 Content = HelpArgumentReplace(Content)
-                If Content.Contains("xmlns") Then Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
+                Do While Content.Contains("xmlns")
+                    Content = Content.RegexReplace("xmlns[^""']*(""|')[^""']*(""|')", "").Replace("xmlns", "") '禁止声明命名空间
+                Loop
                 Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
                 Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:sys=""clr-namespace:System;assembly=mscorlib"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
                 Log($"[Page] 实例化:加载主页 UI 开始,最终内容长度:{Content.Count}")
                 Log($"[Page] 实例化:加载主页 UI 开始,最终内容长度:{Content.Count}")
                 PanCustom.Children.Add(GetObjectFromXML(Content))
                 PanCustom.Children.Add(GetObjectFromXML(Content))

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

@@ -5,7 +5,7 @@
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       xmlns:local="clr-namespace:PCL"
       xmlns:local="clr-namespace:PCL"
       mc:Ignorable="d" d:DesignWidth="297.6">
       mc:Ignorable="d" d:DesignWidth="297.6">
-    <StackPanel Margin="0,0,0,5">
+    <StackPanel Margin="0,0,0,5" x:Name="PanMain">
         <local:MyHint x:Name="HintChinese" Theme="Red" Text="玩家名如果包含英文、数字、下划线以外的内容,可能会无法进入 Minecraft 1.18 以后的世界!" Margin="0,0,0,15" Visibility="Collapsed" />
         <local:MyHint x:Name="HintChinese" Theme="Red" Text="玩家名如果包含英文、数字、下划线以外的内容,可能会无法进入 Minecraft 1.18 以后的世界!" Margin="0,0,0,15" Visibility="Collapsed" />
         <local:MySkin HorizontalAlignment="Center" x:Name="Skin" HasCape="False" Margin="0,0,0,15" />
         <local:MySkin HorizontalAlignment="Center" x:Name="Skin" HasCape="False" Margin="0,0,0,15" />
         <local:MyComboBox IsEditable="True" x:Name="ComboName" Height="28" IsTextSearchEnabled="False" HintText="游戏用户名" />
         <local:MyComboBox IsEditable="True" x:Name="ComboName" Height="28" IsTextSearchEnabled="False" HintText="游戏用户名" />

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

@@ -64,6 +64,7 @@ Public Class PageLoginLegacy
     Private Sub ComboLegacy_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
     Private Sub ComboLegacy_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
         If Setup.Get("LaunchSkinType") = 0 Then PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
         If Setup.Get("LaunchSkinType") = 0 Then PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
         HintChinese.Visibility = If(RegexCheck(ComboName.Text, "^[0-9A-Za-z_]*$"), Visibility.Collapsed, Visibility.Visible)
         HintChinese.Visibility = If(RegexCheck(ComboName.Text, "^[0-9A-Za-z_]*$"), Visibility.Collapsed, Visibility.Visible)
+        RunInUi(Sub() PanMain.InvalidateMeasure(), True) '由于 WPF 的 Bug,它不一定会自动更新 HintChinese 的大小(#6627)
     End Sub
     End Sub
     Private Sub Skin_Click() Handles Skin.Click
     Private Sub Skin_Click() Handles Skin.Click
         If (Setup.Get("UiHiddenPageSetup") OrElse Setup.Get("UiHiddenSetupLaunch")) AndAlso Not PageSetupUI.HiddenForceShow Then
         If (Setup.Get("UiHiddenPageSetup") OrElse Setup.Get("UiHiddenSetupLaunch")) AndAlso Not PageSetupUI.HiddenForceShow Then

+ 4 - 2
Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml

@@ -23,9 +23,11 @@
                     <local:MyIconButton.ContextMenu>
                     <local:MyIconButton.ContextMenu>
                         <ContextMenu Closed="HidePanel" HorizontalOffset="10" VerticalOffset="18">
                         <ContextMenu Closed="HidePanel" HorizontalOffset="10" VerticalOffset="18">
                             <local:MyMenuItem Click="BtnSkinEdit_Click" Header="修改皮肤"/>
                             <local:MyMenuItem Click="BtnSkinEdit_Click" Header="修改皮肤"/>
-                            <local:MyMenuItem Click="BtnSkinSave_Click" Header="保存皮肤"/>
-                            <local:MyMenuItem Click="BtnSkinRefresh_Click" Header="刷新头像"/>
+                            <local:MyMenuItem Click="BtnSkinSave_Click" Header="保存皮肤文件"/>
+                            <Separator />
                             <local:MyMenuItem Click="BtnSkinCape_Click" Header="修改披风"/>
                             <local:MyMenuItem Click="BtnSkinCape_Click" Header="修改披风"/>
+                            <Separator />
+                            <local:MyMenuItem Click="BtnSkinRefresh_Click" Header="刷新"/>
                         </ContextMenu>
                         </ContextMenu>
                     </local:MyIconButton.ContextMenu>
                     </local:MyIconButton.ContextMenu>
                 </local:MyIconButton>
                 </local:MyIconButton>

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

@@ -107,7 +107,7 @@ Retry:
                     McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True)
                     McLoginMsLoader.Start(GetLoginData(), IsForceRestart:=True)
                     GoTo Retry
                     GoTo Retry
                 ElseIf Result.Contains("""error""") Then
                 ElseIf Result.Contains("""error""") Then
-                    Hint("更改皮肤失败:" & GetJson(Result)("error"), HintType.Critical)
+                    Hint("更改皮肤失败:" & GetJson(Result)("error").ToString, HintType.Critical)
                     Return
                     Return
                 End If
                 End If
                 '获取新皮肤地址
                 '获取新皮肤地址

+ 491 - 468
Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml

@@ -1,468 +1,491 @@
-<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="PageOtherAbout"
-    PanScroll="{Binding ElementName=PanBack}" RenderOptions.BitmapScalingMode="LowQuality">
-    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
-        <StackPanel x:Name="PanMain" Margin="25,10" Grid.IsSharedSizeScope="True">
-            <local:MyCard Margin="0,15" Title="关于">
-                <Grid Margin="21,40,21,16">
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                    </Grid.RowDefinitions>
-                    <Grid.ColumnDefinitions>
-                        <ColumnDefinition Width="40" />
-                        <ColumnDefinition />
-                        <ColumnDefinition Width="150" />
-                    </Grid.ColumnDefinitions>
-                    <local:MyListItem IsHitTestVisible="False" Title="龙腾猫跃" Margin="-5,0,15,0" Info="Plain Craft Launcher 的作者!" Grid.Row="0" Grid.Column="1" />
-                    <local:MyListItem IsHitTestVisible="False" Title="Plain Craft Launcher 2" Margin="-5,0,15,0" Info="当前版本:%VERSION%(%VERSIONCODE%.%BRANCH%)" Grid.Row="1" Grid.Column="1" x:Name="ItemAboutPcl" />
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Logo.png" Grid.Row="1" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/LTCat.jpg" Grid.Row="0" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <local:MyButton Text="赞助作者" Grid.Column="2" Grid.Row="0" Height="35" EventType="打开网页" EventData="https://afdian.com/a/LTCat" />
-                    <local:MyButton Text="查看源代码" Grid.Column="2" Grid.Row="1" Height="35" EventType="打开网页" EventData="https://github.com/Meloong-Git/PCL" />
-                </Grid>
-            </local:MyCard>
-            <local:MyCard Margin="0,0,0,15" Title="特别鸣谢">
-                <Grid Margin="21,40,21,16">
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                        <RowDefinition Height="45" />
-                    </Grid.RowDefinitions>
-                    <Grid.ColumnDefinitions>
-                        <ColumnDefinition Width="40" />
-                        <ColumnDefinition />
-                        <ColumnDefinition Width="150" />
-                    </Grid.ColumnDefinitions>
-                    <local:MyListItem Title="bangbang93" Margin="-5,0,15,0" IsHitTestVisible="False" Info="提供 BMCLAPI 镜像源和 Forge 安装工具,详见 https://bmclapi.bangbang93.com" Grid.Row="0" Grid.Column="1" />
-                    <local:MyButton x:Name="BtnAboutBmclapi" Text="赞助镜像源" Grid.Row="0" Grid.Column="2" Height="35" />
-                    <local:MyListItem Title="MC 百科" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 Mod 名称的中文翻译和更多相关信息!" Grid.Row="1" Grid.Column="1" />
-                    <local:MyButton x:Name="BtnAboutWiki" Text="打开百科" Grid.Row="1" Grid.Column="2" Height="35" />
-                    <local:MyListItem Title="00ll00" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 Java Launch Wrapper 和一些重要服务支持!" Grid.Row="2" Grid.Column="1" />
-                    <local:MyListItem Title="z0z0r4" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 MCIM 中国 Mod 下载镜像源和帮助库图床!" Grid.Row="3" Grid.Column="1" />
-                    <local:MyListItem Title="Patrick" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="设计并制作了 PCL 图标,让我从做图标的水深火热中得到了解脱……" Grid.Row="4" Grid.Column="1" />
-                    <local:MyListItem Title="Hao_Tian" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="在内测中找出了一大堆没人想得到的诡异 Bug,有非同寻常的 Bug 体质" Grid.Row="5" Grid.Column="1" />
-                    <local:MyListItem Title="Minecraft 中文论坛" Margin="-5,0,15,0" IsHitTestVisible="False" Info="虽然已经关站了,但感谢此前提供了 MCBBS 镜像源……" Grid.Row="6" Grid.Column="1" />
-                    <local:MyListItem Title="PCL 内群的各位" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="感谢内群的沙雕网友们这么久以来对我和 PCL 的支持与鼓励!" Grid.Row="7" Grid.Column="1" />
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/bangbang93.png" Grid.Row="0" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/wiki.png" Grid.Row="1" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/00ll00.png" Grid.Row="2" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/z0z0r4.png" Grid.Row="3" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Patrick.png" Grid.Row="4" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Hao_Tian.jpg" Grid.Row="5" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/MCBBS.png" Grid.Row="6" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/PCL2.png" Grid.Row="7" Margin="3">
-                        <Image.Clip>
-                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
-                        </Image.Clip>
-                    </Image>
-                </Grid>
-            </local:MyCard>
-            <local:MyCard Margin="0,0,0,15" Title="赞助者">
-                <StackPanel Margin="25,40,10,20">
-                    <TextBlock HorizontalAlignment="Left" Text="以下是 2025 年 5 月的高级赞助者们!感谢各位对 PCL 和伟大的咕咕咕事业的支持!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" />
-                    <WrapPanel Margin="0,0,0,10">
-                        <TextBlock Width="120" Margin="0,1.5" Text="Alliance" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="风之轻语" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="大博文" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="狐狸不吃酸葡萄" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="仰望星空的牧星人" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="某个网友" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="114514" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Emmanuel" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="云漫雾影" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="**葵" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="吴同学" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Anderson521314" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Alrosa" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="可爱电鳗" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小野冢小町" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="田坎村西头王二狗" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="令月廿一" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Mrdandan" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="111" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="27733" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="韦韦神父" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="土豆" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="隔岸流年" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="冥吻" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="向阳" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="轻浮" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小涵0v0" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="祈愿小狐" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="爱莉爱莉爱" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Bydold" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="SMRWXH" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="翷月" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="husky" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Hz_yuki" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="先知" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="2683672592" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="龖龖" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="陨树" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Gripen" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="棒槌" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="北北ovo" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="TombName" />
-                        <TextBlock Width="240" Margin="0,1.5" Text="莳倾梦のshiqingmeng" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Bortor" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="OuO6006" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="MKSeKai" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="清尘墨雨" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="墨染柊" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="福福" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="夕尽暮染" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="光年摆渡人" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="梦清韩" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="孤城辞典" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="灰" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小周周" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="孤星痕" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="月孤" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="团团" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="J_ONE" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="初尘ya" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="MooBHise" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="凌之芬芳" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="土豆回锅土豆" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Reisen" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Rong" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="FiZone" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="橡素" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="逆光の思" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Edifier" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="迭风" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="mumu" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="欧润几" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="SiberianHusky" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="虫" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="闪电" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Bob_Marcus" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="瘦瘦爱吃呕吼" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="盛夏" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="飞起来的Mine" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="cc" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="简麟" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="郴州火车迷" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="守白" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="天长地忙" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="月无痕" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="HWW" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="樱岛麻衣" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="桌子" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="bushihuankong" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="戏梦simi" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Metal_Yoshika" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="H+H" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="junhuigege" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Kallen" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="x" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="随便玩" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="魇夜星渊" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="taomiao13579" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="无敌大鱼" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="忆兀安" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="无梦" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="SlyBlackCat" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="180" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="总督大人" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="sc99" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Fumi" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="豪放" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Canqiu" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="朱利安" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="白露辰霜" />
-                        <TextBlock Width="240" Margin="0,1.5" Text="点燃心中火的大音乐家" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="度你入魔" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小宝" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="无" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="青出梦名" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="雾日斋主人" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="鸣纳" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="苏卜辰" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="3827" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="水里游的老鹰" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="雪桜" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Yukiteru" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="零依" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="玻璃糖罐" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="JFF_2005" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="流星" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="ccc" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="PennyLee2023" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="伯劳" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Sy5H" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Fomalhaut" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="PERLICA" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="北冥鱼吃" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="千万别自闭" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="00号玩家" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="亗竹" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="云一梦" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="久念" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="继晷" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="史蒂夫" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="椎罗太难了" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="DiamondSYP" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="木林西" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="雩月" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="6628" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="ZoCc" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="wan_yao" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="White_c" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="尉迟羽晨" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="栀梓" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小命" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="金" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="倾墨画君颜" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="南宫若兮" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="沐言" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="傻预备" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="蒸木" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="那菈" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="suanqiu" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="人见人爱的熊熊" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Furan255" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="MachineCrab" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="五只大花狗" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="威尔特" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="StarNCosmos" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Eason" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="风之幻城" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="王舒晨" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="H" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="咏夕" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="不及君子柒" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="GUAN_SKD" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="雅柏菲卡" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="AtDusk" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="九月想睡觉" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="六七" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="老登" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="wd_xuan" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Wake" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Jannik" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="cybone" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="mc12345zhl123" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="解" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Akights" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="秋刀驴" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="樟涛竹" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="sikuno" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="鬼目" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="拜拜白" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="绮铃kirin" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="洛逍遥" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="文文新闻" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="GM" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="小咪" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Fuxi Fuji" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="?独爱蝉HIA" />
-                        <TextBlock Width="240" Margin="0,1.5" Text="没有咖啡的猫屎咖啡" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="雪风保佑我" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="zhnj" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="火苟" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="梦尽" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="阿泽free" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="阿乐" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="米线" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="依凡" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="涟燃556" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="夏季" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Game_ReAi" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="秦屎皇" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="6" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Zik yeung" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="awawa" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="智慧村村长" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="海中燕" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="就叫我好人吧" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="梦的回响" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="。。" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="墨子吉安" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="有风" />
-                        <TextBlock Width="240" Margin="0,1.5" Text="孙嘉昊まごかひろし" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="C" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Haio" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="陆光" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="chuveiro" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="无名氏34613" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="mr_kevin_x" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="来早点" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="烤肉真香" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Chr0n0s" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="七" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="H?O" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="瑞霖RLD" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="AAcfun" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="鼬嘁" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="名为冉冉的猫" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="hduqggd55379" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="唠叨的土豆" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Lonely_Grey" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="Vergilio" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="このdioた" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="没睡醒的肉啊" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="铃鸢" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="lvke" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="V-Merlin" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="美式加冰?" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="半岛弥音" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="clclFL" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="事与愿违" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="SamXU1322" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="OCEANI" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="shenpli" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="咸鱼嫌鱼咸" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="kirsnow" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="白庶" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="pengyihang" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="憨憨剑踏" />
-                        <TextBlock Width="120" Margin="0,1.5" Text="SinceCN" />
-                    </WrapPanel>
-                    <Grid Height="35">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
-                        </Grid.ColumnDefinitions>
-                        <local:MyButton x:Name="BtnDonateOutput" Grid.Column="0" MinWidth="140" Text="生成解锁码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" Visibility="Collapsed" />
-                        <local:MyButton x:Name="BtnDonateDonate" Grid.Column="0" MinWidth="140" Text="赞助作者" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" EventType="打开网页" EventData="https://afdian.com/a/LTCat" />
-                        <local:MyButton x:Name="BtnDonateCopy" Grid.Column="1" MinWidth="140" Text="复制识别码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
-                        <local:MyButton x:Name="BtnDonateInput" Grid.Column="2" MinWidth="140" Text="输入解锁码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
-                    </Grid>
-                </StackPanel>
-            </local:MyCard>
-            <local:MyCard Margin="0,0,0,15" Title="法律信息" IsSwaped="True" CanSwap="True">
-                <StackPanel Margin="25,39,23,20">
-                    <TextBlock Text="隐私说明" TextWrapping="Wrap" Margin="0,6,0,4" FontWeight="Bold" />
-                    <TextBlock Margin="0,0,0,2" LineHeight="17" Text="本软件的 User Agent 中包含启动器的版本号,以统计各版本的用户数量。&#13;除此之外,本软件不会向开发者上传任何其他信息。" TextWrapping="Wrap" />
-                    <TextBlock Text="其他信息" TextWrapping="Wrap" Margin="0,12,0,4" FontWeight="Bold" />
-                    <TextBlock Margin="0,0,0,2" LineHeight="17" Text="Copyright © 龙腾猫跃 2016. All Rights Reserved.&#13;计算机软件著作权登记号:2020SR0875133&#13;违法违规行为举报投诉邮箱:[email protected]&#13;非 MINECRAFT 官方产品。未经 MOJANG 或 MICROSOFT 批准,也不与 MOJANG 或 MICROSOFT 关联。" TextWrapping="Wrap" />
-                    <StackPanel Orientation="Horizontal" Height="35" HorizontalAlignment="Left" Margin="0,12,0,0">
-                        <local:MyButton Text="用户协议与免责声明" Width="170" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" EventType="打开网页" EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW" />
-                        <local:MyButton Text="开源代码" Width="140" Padding="13,0" EventType="打开网页" EventData="https://github.com/Meloong-Git/PCL" />
-                    </StackPanel>
-                </StackPanel>
-            </local:MyCard>
-            <local:MyCard Margin="0,0,0,15" Title="许可与版权声明" IsSwaped="True" CanSwap="True">
-                <Grid Margin="25,42,23,4">
-                    <Grid.ColumnDefinitions>
-                        <ColumnDefinition Width="Auto" />
-                        <ColumnDefinition Width="20" />
-                        <ColumnDefinition Width="Auto" />
-                        <ColumnDefinition />
-                    </Grid.ColumnDefinitions>
-                    <Grid.RowDefinitions>
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                        <RowDefinition Height="60" />
-                        <RowDefinition Height="Auto" />
-                    </Grid.RowDefinitions>
-                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Java Launch Wrapper" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2022 00ll00&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="1" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/00ll00/java_launch_wrapper" />
-                    <local:MyButton Grid.Row="1" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/00ll00/java_launch_wrapper?tab=MIT-1-ov-file#readme" />
-
-                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Newtonsoft.Json" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2007 James Newton-King&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="3" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://www.newtonsoft.com/json" />
-                    <local:MyButton Grid.Row="3" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://opensource.org/licenses/MIT" />
-                    
-                    <TextBlock Grid.Row="4" Grid.Column="0" Text="NAudio" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath ([email protected])&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="5" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/naudio/NAudio" />
-                    <local:MyButton Grid.Row="5" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" 
-                                    EventType="打开网页" EventData="https://github.com/naudio/NAudio/blob/master/license.txt" />
-                    
-                    <TextBlock Grid.Row="6" Grid.Column="0" Text="Ookii.Dialogs" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2009 Sven Groot (Ookii.org)" TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="7" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://www.ookii.org/Software/Dialogs/" />
-                    <local:MyButton Grid.Row="7" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/caioproiete/ookii-dialogs-wpf/blob/master/LICENSE" />
-                    
-                    <TextBlock Grid.Row="8" Grid.Column="0" Text="Iconmonstr" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2019 iconmonstr (Alexander Kahlkopf)" TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="9" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://iconmonstr.com/" />
-                    <local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://iconmonstr.com/license/" />
-                    
-                    <TextBlock Grid.Row="10" Grid.Column="0" Text="libwebp wrapper for .NET" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2012 Imazen&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/imazen/libwebp-net" />
-                    <local:MyButton Grid.Row="11" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://github.com/imazen/libwebp-net/blob/master/LICENSE.txt" />
-
-                    <TextBlock Grid.Row="12" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
-                    <TextBlock Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
-                    <local:MyButton Grid.Row="13" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
-                                    EventType="打开网页" EventData="https://www.iconfont.cn/" />
-
-                </Grid>
-            </local:MyCard>
-        </StackPanel>
-    </local:MyScrollViewer>
-</local:MyPageRight>
+<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="PageOtherAbout"
+    PanScroll="{Binding ElementName=PanBack}" RenderOptions.BitmapScalingMode="LowQuality">
+    <local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
+        <StackPanel x:Name="PanMain" Margin="25,10" Grid.IsSharedSizeScope="True">
+            <local:MyCard Margin="0,15" Title="关于">
+                <Grid Margin="21,40,21,16">
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="40" />
+                        <ColumnDefinition />
+                        <ColumnDefinition Width="150" />
+                    </Grid.ColumnDefinitions>
+                    <local:MyListItem IsHitTestVisible="False" Title="龙腾猫跃" Margin="-5,0,15,0" Info="Plain Craft Launcher 的作者!" Grid.Row="0" Grid.Column="1" />
+                    <local:MyListItem IsHitTestVisible="False" Title="Plain Craft Launcher 2" Margin="-5,0,15,0" Info="当前版本:%VERSION%(%VERSIONCODE%.%BRANCH%)" Grid.Row="1" Grid.Column="1" x:Name="ItemAboutPcl" />
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Logo.png" Grid.Row="1" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/LTCat.jpg" Grid.Row="0" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <local:MyButton Text="赞助作者" Grid.Column="2" Grid.Row="0" Height="35" EventType="打开网页" EventData="https://afdian.com/a/LTCat" />
+                    <local:MyButton Text="查看源代码" Grid.Column="2" Grid.Row="1" Height="35" EventType="打开网页" EventData="https://github.com/Hex-Dragon/PCL2" />
+                </Grid>
+            </local:MyCard>
+            <local:MyCard Margin="0,0,0,15" Title="特别鸣谢">
+                <Grid Margin="21,40,21,16">
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                        <RowDefinition Height="45" />
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="40" />
+                        <ColumnDefinition />
+                        <ColumnDefinition Width="150" />
+                    </Grid.ColumnDefinitions>
+                    <local:MyListItem Title="bangbang93" Margin="-5,0,15,0" IsHitTestVisible="False" Info="提供 BMCLAPI 镜像源和 Forge 安装工具,详见 https://bmclapi.bangbang93.com" Grid.Row="0" Grid.Column="1" />
+                    <local:MyButton x:Name="BtnAboutBmclapi" Text="赞助镜像源" Grid.Row="0" Grid.Column="2" Height="35" />
+                    <local:MyListItem Title="MC 百科" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 Mod 名称的中文翻译和更多相关信息!" Grid.Row="1" Grid.Column="1" />
+                    <local:MyButton x:Name="BtnAboutWiki" Text="打开百科" Grid.Row="1" Grid.Column="2" Height="35" />
+                    <local:MyListItem Title="00ll00" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 Java Launch Wrapper 和一些重要服务支持!" Grid.Row="2" Grid.Column="1" />
+                    <local:MyListItem Title="z0z0r4" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="提供了 MCIM 中国 Mod 下载镜像源和帮助库图床!" Grid.Row="3" Grid.Column="1" />
+                    <local:MyListItem Title="Patrick" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="设计并制作了 PCL 图标,让我从做图标的水深火热中得到了解脱……" Grid.Row="4" Grid.Column="1" />
+                    <local:MyListItem Title="Hao_Tian" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="在内测中找出了一大堆没人想得到的诡异 Bug,有非同寻常的 Bug 体质" Grid.Row="5" Grid.Column="1" />
+                    <local:MyListItem Title="Minecraft 中文论坛" Margin="-5,0,15,0" IsHitTestVisible="False" Info="虽然已经关站了,但感谢此前提供了 MCBBS 镜像源……" Grid.Row="6" Grid.Column="1" />
+                    <local:MyListItem Title="PCL 内群的各位" Margin="-5,0,0,0" IsHitTestVisible="False" Grid.ColumnSpan="2" Info="感谢内群的沙雕网友们这么久以来对我和 PCL 的支持与鼓励!" Grid.Row="7" Grid.Column="1" />
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/bangbang93.png" Grid.Row="0" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/wiki.png" Grid.Row="1" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/00ll00.png" Grid.Row="2" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/z0z0r4.png" Grid.Row="3" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Patrick.png" Grid.Row="4" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/Hao_Tian.jpg" Grid.Row="5" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/MCBBS.png" Grid.Row="6" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                    <Image Source="/Plain Craft Launcher 2;component/Images/Heads/PCL2.png" Grid.Row="7" Margin="3">
+                        <Image.Clip>
+                            <EllipseGeometry Center="17 17" RadiusX="17" RadiusY="17" />
+                        </Image.Clip>
+                    </Image>
+                </Grid>
+            </local:MyCard>
+            <local:MyCard Margin="0,0,0,15" Title="赞助者">
+                <StackPanel Margin="25,40,10,20">
+                    <TextBlock HorizontalAlignment="Left" Text="以下是 2025 年 6 月的高级赞助者们!感谢各位对 PCL 和伟大的咕咕咕事业的支持!" Margin="0,0,25,5.5" TextTrimming="None" TextWrapping="WrapWithOverflow" />
+                    <WrapPanel Margin="0,0,0,10">
                        <TextBlock Width="120" Margin="0,1.5" Text="星月" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Nemo" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="长歌当天" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="飒黎lIlIlI" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="hy" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="SaDhhJe" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="风铃子啊" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="柠娜" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="W丶北辰" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="才KRE" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Yang_mistakeof" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="close eyes" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="顾顾" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="LTuuxj" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="花吃" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="FICazk" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="屑屑你" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="凉凉" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="WDSJ" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Tohka" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="lz" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="哦嚯嚯嚯皮筋头" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="陈肖杰" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="MC小满" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="残光" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="玖玖璃" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="水晶OTZ" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="伯克礼" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="白猫海贼船" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="zhang1232" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Never" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Fish_pomelo" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="生风" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="富士山下" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="小七" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="发呆的呆毛君" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="天黑时看月亮" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="夜枫" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="_章333_" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="孙杰" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="wby" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="封尾==" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="你与空白皆无名" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="鱼" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="zzxniubi" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="炎燚火淼淼" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="三巿" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="奈落" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="华佗" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="oran9e" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="赤冰雨" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Urizen" />
+                        <TextBlock Width="120" Margin="0,1.5" Text=":)" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="skyqyzm" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="橘子皮丶" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="额外awa" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="安上月" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="念城南" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="多低山丘陵" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="书童呀" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="sxy192425" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="李好啊" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="MH_Nine" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="周先生" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="原" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="良田满穗" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="柴" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="庭师" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="。,。" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="DHLH" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Xayj" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="绳网用户#37" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="LLLL" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="深绯红" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="奇伢七星" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Akapigo" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Shink_guns" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Aqua 极&amp;坏." />
+                        <TextBlock Width="120" Margin="0,1.5" Text="大事化小蛋糕" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Nakiya233" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="花小妖" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="septebr" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="一只马喽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="下暴雨BWB" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="QAQ" />
+                        <TextBlock Width="240" Margin="0,1.5" Text="sl土豆社区服主B友" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Wang" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="......" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ResTmoW" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="bochibochibo" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="铸魂之灵" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="柏柏柏" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Mart1s" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="KuruM148" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="KaiTouCrow" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="LIE-SHAN" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="慈悲方舟" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="于析" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="陌道" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="摇椅上的猫" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="五个麦卷" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Miraclepoi" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="XiaoLI" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="啊吧啊吧的肥宅" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="不娶雨婷不改名" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="oppo19" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="博麗靈夢" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="莹 of yours" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ChinaFZXPVP" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="城之内sama" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ENXI" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="steve_c418" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="洛长安" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="magic_muggle" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="小猫游六喵" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="JY" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="雪豹起司可颂" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="wlsy" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="齐先生爱溜达" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="是小泠啊" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="xioyiao" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="BunnyMiku" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="dadagga" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="无极之笔" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="xiaoyu" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="王梓杰" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="wjbjjd" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Candy_Mo" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="lettmin_" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="七九·Alnasl" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="辉" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="何为最优解" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="crazy丶天人" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="XsBlueSky" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="冬将軍" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="tiechui" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Liu2Shi2Pu2" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Yu_s" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="刘四妹" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="枝皙" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="CC" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Slana23" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="MMMy" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Venuse" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="microjie" />
+                        <TextBlock Width="240" Margin="0,1.5" Text="Miner#Cetyleno~^" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="春江花月夜" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="落南子" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="AL1Su-爱丽丝" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="我木有呆毛" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="乌羽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="知性de含羞草" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="星の殇" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="IED LTAM" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="格罗夫斯基" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="梦启泉" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="岁月无痕" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="????" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="超级脑壳" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="空梦" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="薛小欣" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="蛋黄豆角" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="芒果茶州" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="癫风院院长" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="三十一" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="美醉了" />
+                        <TextBlock Width="240" Margin="0,1.5" Text="中科院在逃量子计算机" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="." />
+                        <TextBlock Width="120" Margin="0,1.5" Text="枫" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="jjj" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="clilick" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="我很快乐" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Phoenix tamer" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Lsp77" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="太阳神" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ikun0014" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="IncredibleYu" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="在下粉毛控" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="SuperCrazyK" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="阿柔" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="HaKNa" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="胡十一" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="菲利絲" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="迷梦的羽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="20018" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="波比科普员" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Xiao_JIA" />
+                        <TextBlock Width="240" Margin="0,1.5" Text="龙腾我是你的头号粉丝" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="keer1563" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="百鬼まル" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="DPSYnt" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="七色彩羽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="臭氧化碳" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="情来风" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="柒月" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="汪汪队队长" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="迷路的猫" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ktyxlan_lan" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="琳琅039" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="黑色额度" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="sahaua" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="宦昭" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="镜刹" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="房石诶多" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="熊" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="NQ" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="我志永纯" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="漫城" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="憨憨的小草" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="泽涛" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="星海鹿樱" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="祭°" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="赛冷特呀" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="无妄之灾" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ZGW2008" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="没事多喝感冒灵" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="一梦筱筱" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="twokness" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="hhy7171" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="子虚" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="猫羽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="有氧丁真" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="dddr" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="天气热" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Emmanuel" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="夕尽暮染" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Bob_Marcus" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="初心无尘" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="朱利安" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="461863352" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="庭" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Xing_K" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="yzt" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="nuycn" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="鸡人" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="爱无界i" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Zik yeung" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="墨子吉安" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="柠果籽" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="cai1" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="小比登T^T" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="hb" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="caizzz" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="yyin" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="無言" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="修" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="改头换面" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="无敌百搭的款式" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="御坂1354号" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="木子" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="萤火焱攸" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="盼盼不是Panda" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="烤肉真香" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ZhuTan12" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="tom" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="洛竹" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="foob" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="Garylu" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="阮绍轩" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="xin822" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="BAIgivele" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="ljc" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="暖笑" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="焦糖流酥" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="绚丽的星河" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="魔导兔" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="吾乃斗笔" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="君殇" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="沙轻" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="邓布利多" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="红薯散养专业户" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="十言" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="vvvghjvvv" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="法斯" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="DASENSUN" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="HTS" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="握不住信新心" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="sycuuj133" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="天杉大将军" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="风见翎" />
+                        <TextBlock Width="120" Margin="0,1.5" Text="江心Rain" />
+                    </WrapPanel>
+                    <Grid Height="35">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                            <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
+                        </Grid.ColumnDefinitions>
+                        <local:MyButton x:Name="BtnDonateOutput" Grid.Column="0" MinWidth="140" Text="生成解锁码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" Visibility="Collapsed" />
+                        <local:MyButton x:Name="BtnDonateDonate" Grid.Column="0" MinWidth="140" Text="赞助作者" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" EventType="打开网页" EventData="https://afdian.com/a/LTCat" />
+                        <local:MyButton x:Name="BtnDonateCopy" Grid.Column="1" MinWidth="140" Text="复制识别码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
+                        <local:MyButton x:Name="BtnDonateInput" Grid.Column="2" MinWidth="140" Text="输入解锁码" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
+                    </Grid>
+                </StackPanel>
+            </local:MyCard>
+            <local:MyCard Margin="0,0,0,15" Title="法律信息" IsSwaped="True" CanSwap="True">
+                <StackPanel Margin="25,39,23,20">
+                    <TextBlock Text="隐私说明" TextWrapping="Wrap" Margin="0,6,0,4" FontWeight="Bold" />
+                    <TextBlock Margin="0,0,0,2" LineHeight="17" Text="本软件的 User Agent 中包含启动器的版本号,以统计各版本的用户数量。&#13;除此之外,本软件不会向开发者上传任何其他信息。" TextWrapping="Wrap" />
+                    <TextBlock Text="其他信息" TextWrapping="Wrap" Margin="0,12,0,4" FontWeight="Bold" />
+                    <TextBlock Margin="0,0,0,2" LineHeight="17" Text="Copyright © 龙腾猫跃 2016. All Rights Reserved.&#13;计算机软件著作权登记号:2020SR0875133&#13;违法违规行为举报投诉邮箱:[email protected]&#13;非 MINECRAFT 官方产品。未经 MOJANG 或 MICROSOFT 批准,也不与 MOJANG 或 MICROSOFT 关联。" TextWrapping="Wrap" />
+                    <StackPanel Orientation="Horizontal" Height="35" HorizontalAlignment="Left" Margin="0,12,0,0">
+                        <local:MyButton Text="用户协议与免责声明" Width="170" Padding="13,0" Margin="0,0,20,0" ColorType="Highlight" EventType="打开网页" EventData="https://shimo.im/docs/rGrd8pY8xWkt6ryW" />
+                        <local:MyButton Text="开源代码" Width="140" Padding="13,0" EventType="打开网页" EventData="https://github.com/Hex-Dragon/PCL2" />
+                    </StackPanel>
+                </StackPanel>
+            </local:MyCard>
+            <local:MyCard Margin="0,0,0,15" Title="许可与版权声明" IsSwaped="True" CanSwap="True">
+                <Grid Margin="25,42,23,4">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto" />
+                        <ColumnDefinition Width="20" />
+                        <ColumnDefinition Width="Auto" />
+                        <ColumnDefinition />
+                    </Grid.ColumnDefinitions>
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="60" />
+                        <RowDefinition Height="Auto" />
+                    </Grid.RowDefinitions>
+                    <TextBlock Grid.Row="0" Grid.Column="0" Text="Java Launch Wrapper" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2022 00ll00&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="1" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/00ll00/java_launch_wrapper" />
+                    <local:MyButton Grid.Row="1" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/00ll00/java_launch_wrapper?tab=MIT-1-ov-file#readme" />
+
+                    <TextBlock Grid.Row="2" Grid.Column="0" Text="Newtonsoft.Json" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2007 James Newton-King&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="3" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://www.newtonsoft.com/json" />
+                    <local:MyButton Grid.Row="3" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://opensource.org/licenses/MIT" />
+                    
+                    <TextBlock Grid.Row="4" Grid.Column="0" Text="NAudio" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2015 Mark Heath ([email protected])&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="5" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/naudio/NAudio" />
+                    <local:MyButton Grid.Row="5" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left" 
+                                    EventType="打开网页" EventData="https://github.com/naudio/NAudio/blob/master/license.txt" />
+                    
+                    <TextBlock Grid.Row="6" Grid.Column="0" Text="Ookii.Dialogs" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="6" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2009 Sven Groot (Ookii.org)" TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="7" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://www.ookii.org/Software/Dialogs/" />
+                    <local:MyButton Grid.Row="7" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/caioproiete/ookii-dialogs-wpf/blob/master/LICENSE" />
+                    
+                    <TextBlock Grid.Row="8" Grid.Column="0" Text="Iconmonstr" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2019 iconmonstr (Alexander Kahlkopf)" TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="9" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://iconmonstr.com/" />
+                    <local:MyButton Grid.Row="9" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://iconmonstr.com/license/" />
+                    
+                    <TextBlock Grid.Row="10" Grid.Column="0" Text="libwebp wrapper for .NET" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="10" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © 2012 Imazen&#xa;Licensed under the MIT License." TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="11" Grid.Column="2" MinWidth="140" Text="查看来源网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/imazen/libwebp-net" />
+                    <local:MyButton Grid.Row="11" Grid.Column="3" MinWidth="140" Text="查看许可文档" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://github.com/imazen/libwebp-net/blob/master/LICENSE.txt" />
+
+                    <TextBlock Grid.Row="12" Grid.Column="0" Text="Iconfont" TextWrapping="Wrap" FontWeight="Bold" />
+                    <TextBlock Grid.Row="12" Grid.Column="2" Grid.ColumnSpan="2" Text="Copyright © ALIMAMA MUX, powered by alimama THX." TextWrapping="Wrap" />
+                    <local:MyButton Grid.Row="13" Grid.Column="2" MinWidth="140" Text="查看原网站" Padding="13,0" Margin="0,7,20,18" HorizontalAlignment="Left"
+                                    EventType="打开网页" EventData="https://www.iconfont.cn/" />
+
+                </Grid>
+            </local:MyCard>
+        </StackPanel>
+    </local:MyScrollViewer>
+</local:MyPageRight>

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

@@ -227,7 +227,7 @@
                         DeleteDirectory(Version.Path)
                         DeleteDirectory(Version.Path)
                         Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish)
                         Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish)
                     Else
                     Else
-                        FileIO.FileSystem.DeleteDirectory(Version.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
+                        FileIO.FileSystem.DeleteDirectory(Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
                         Hint("版本 " & Version.Name & " 已删除到回收站!", HintType.Finish)
                         Hint("版本 " & Version.Name & " 已删除到回收站!", HintType.Finish)
                     End If
                     End If
                 Case 2
                 Case 2

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

@@ -205,7 +205,7 @@
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
                                 <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
                             </Grid.ColumnDefinitions>
                             </Grid.ColumnDefinitions>
                             <local:MyButton x:Name="BtnSkinSave" Text="保存皮肤" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Row="1" />
                             <local:MyButton x:Name="BtnSkinSave" Text="保存皮肤" MinWidth="140" Padding="13,0" Margin="0,0,20,0" Grid.Row="1" />
-                            <local:MyButton x:Name="BtnSkinCache" Text="刷新缓存" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ToolTip="刷新并重新下载 PCL 当前显示的皮肤" Grid.Row="1" Grid.Column="1" />
+                            <local:MyButton x:Name="BtnSkinCache" Text="刷新" MinWidth="140" Padding="13,0" Margin="0,0,20,0" ToolTip="刷新并重新下载 PCL 当前显示的皮肤" Grid.Row="1" Grid.Column="1" />
                         </Grid>
                         </Grid>
                     </Grid>
                     </Grid>
                     <Grid x:Name="PanSkinChange" Margin="0,14,0,2" Height="35" Visibility="Collapsed">
                     <Grid x:Name="PanSkinChange" Margin="0,14,0,2" Height="35" Visibility="Collapsed">
@@ -240,10 +240,10 @@
                                         ToolTip="还原" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1"
                                         ToolTip="还原" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1"
                                         Logo="M530 0c287 0 521 229 521 511s-233 511-521 511c-233 0-436-151-500-368a63 63 0 0 1 44-79 65 65 0 0 1 80 43c48 162 200 276 375 276 215 0 390-171 390-383s-174-383-390-383c-103 0-199 39-270 106l21-5a63 63 0 0 1 33 123l-157 42a65 65 0 0 1-90-42l-49-183a65 65 0 1 1 126-33l6 26A524 524 0 0 1 530 0z" LogoScale="0.9" />
                                         Logo="M530 0c287 0 521 229 521 511s-233 511-521 511c-233 0-436-151-500-368a63 63 0 0 1 44-79 65 65 0 0 1 80 43c48 162 200 276 375 276 215 0 390-171 390-383s-174-383-390-383c-103 0-199 39-270 106l21-5a63 63 0 0 1 33 123l-157 42a65 65 0 0 1-90-42l-49-183a65 65 0 1 1 126-33l6 26A524 524 0 0 1 530 0z" LogoScale="0.9" />
                     </StackPanel>
                     </StackPanel>
-                    <local:MyTextBox x:Name="TextAdvanceJvm" Grid.ColumnSpan="2" Tag="LaunchAdvanceJvm" Grid.Column="1" ToolTip="启动 Minecraft 时使用的额外 JVM 参数,除非有确定把握,否则请不要修改" MaxLength="4000"
+                    <local:MyTextBox x:Name="TextAdvanceJvm" Grid.ColumnSpan="2" Tag="LaunchAdvanceJvm" Grid.Column="1" ToolTip="启动 Minecraft 时使用的额外 JVM 参数,除非有确定把握,否则请不要修改。&#xa;支持 Minecraft 版本 JSON 中的字符串替换标记,例如 ${library_directory}。" MaxLength="4000"
                                  Height="100" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" VerticalScrollBarVisibility="Auto" />
                                  Height="100" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" VerticalScrollBarVisibility="Auto" />
                     <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏参数尾部" Margin="0,0,25,0" />
                     <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏参数尾部" Margin="0,0,25,0" />
-                    <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。" />
+                    <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如,输入 --demo 则会以试玩模式启动游戏。&#xa;支持 Minecraft 版本 JSON 中的字符串替换标记,例如 ${library_directory}。" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" />
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText=""
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText=""
                                  ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}options.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
                                  ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}options.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />

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

@@ -198,7 +198,7 @@
                             <ColumnDefinition />
                             <ColumnDefinition />
                             <ColumnDefinition />
                             <ColumnDefinition />
                         </Grid.ColumnDefinitions>
                         </Grid.ColumnDefinitions>
-                        <local:MyCheckBox Grid.Column="0" Text="禁止在下载时复制已存在的文件" Height="22" x:Name="CheckDebugSkipCopy" Tag="SystemDebugSkipCopy" ToolTip="在下载时不直接复制已经存在的文件,而是重新下载每个文件。&#xa;只建议在测试下载速度时开启。" />
+                        <local:MyCheckBox Grid.Column="0" Text="禁止在下载时从其他文件夹复制文件" Height="22" x:Name="CheckDebugSkipCopy" Tag="SystemDebugSkipCopy" ToolTip="在下载时不直接复制其他 MC 文件夹中已经存在的相同文件,而是重新下载文件。&#xa;只建议在测试下载速度时开启。" />
                         <local:MyCheckBox Grid.Column="1" Text="调试模式" Height="22" x:Name="CheckDebugMode" Tag="SystemDebugMode" ToolTip="显示调试信息与更多错误信息。&#xa;这会导致启动器性能略有下降,若无特殊需要不建议开启。" />
                         <local:MyCheckBox Grid.Column="1" Text="调试模式" Height="22" x:Name="CheckDebugMode" Tag="SystemDebugMode" ToolTip="显示调试信息与更多错误信息。&#xa;这会导致启动器性能略有下降,若无特殊需要不建议开启。" />
                         <local:MyCheckBox Grid.Column="2" Text="添加延迟" Height="22" x:Name="CheckDebugDelay" Tag="SystemDebugDelay" ToolTip="在各个环节添加随机的延迟,拖慢加载速度,以测试部分功能是否正常运行。&#xa;这会严重影响启动器运行,若无特殊需要不建议开启。" />
                         <local:MyCheckBox Grid.Column="2" Text="添加延迟" Height="22" x:Name="CheckDebugDelay" Tag="SystemDebugDelay" ToolTip="在各个环节添加随机的延迟,拖慢加载速度,以测试部分功能是否正常运行。&#xa;这会严重影响启动器运行,若无特殊需要不建议开启。" />
                     </Grid>
                     </Grid>

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

@@ -1,6 +1,4 @@
-Imports NAudio.Wave
-
-Public Class PageSetupUI
+Public Class PageSetupUI
 
 
     Public Shadows IsLoaded As Boolean = False
     Public Shadows IsLoaded As Boolean = False
 
 
@@ -276,7 +274,7 @@ Public Class PageSetupUI
 
 
     '顶部栏
     '顶部栏
     Private Sub BtnLogoChange_Click(sender As Object, e As EventArgs) Handles BtnLogoChange.Click
     Private Sub BtnLogoChange_Click(sender As Object, e As EventArgs) Handles BtnLogoChange.Click
-        Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.webp;*.jpeg)|*.png;*.jpg;*.gif;*.webp;*.jpeg", "选择图片")
+        Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.jpeg;*.gif;*.webp)|*.png;*.jpg;*.jpeg;*.gif;*.webp", "选择图片")
         If FileName = "" Then Return
         If FileName = "" Then Return
         Try
         Try
             '拷贝文件
             '拷贝文件
@@ -321,7 +319,7 @@ Refresh:
             Return
             Return
         End If
         End If
         '没有图片则要求选择
         '没有图片则要求选择
-        Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.webp)|*.png;*.jpg;*.gif;*.webp", "选择图片")
+        Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpeg;*.jpg;*.gif;*.webp)|*.png;*.jpeg;*.jpg;*.gif;*.webp", "选择图片")
         If FileName = "" Then
         If FileName = "" Then
             FrmMain.ImageTitleLogo.Source = Nothing
             FrmMain.ImageTitleLogo.Source = Nothing
             e.Handled = True
             e.Handled = True
@@ -359,15 +357,9 @@ Refresh:
             PanMusicVolume.Visibility = Visibility.Visible
             PanMusicVolume.Visibility = Visibility.Visible
             PanMusicDetail.Visibility = Visibility.Visible
             PanMusicDetail.Visibility = Visibility.Visible
             BtnMusicClear.Visibility = Visibility.Visible
             BtnMusicClear.Visibility = Visibility.Visible
-            Dim validMusicCount As Integer = 0
-            For Each File In EnumerateFiles(Path & "PCL\Musics\")
-                Try
-                    If {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(File.Extension.ToLower) Then Continue For
-                    validMusicCount += 1
-                Catch ex As Exception
-                End Try
-            Next
-            CardMusic.Title = "背景音乐(" & validMusicCount & " 首)"
+            CardMusic.Title = "背景音乐(" &
+                EnumerateFiles(Path & "PCL\Musics\").Count(Function(f) {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(f.Extension.ToLower)) &
+                " 首)"
         Else
         Else
             PanMusicVolume.Visibility = Visibility.Collapsed
             PanMusicVolume.Visibility = Visibility.Collapsed
             PanMusicDetail.Visibility = Visibility.Collapsed
             PanMusicDetail.Visibility = Visibility.Collapsed

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

@@ -564,8 +564,8 @@ Public Class PageVersionExport
             Sub()
             Sub()
                 Try
                 Try
                     Dim ModrinthHashes = Loader.Input.Select(Function(m) m.ModrinthHash)
                     Dim ModrinthHashes = Loader.Input.Select(Function(m) m.ModrinthHash)
-                    Dim ModrinthRaw = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files", "POST",
-                        $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
+                    Dim ModrinthRaw As JObject = DlModRequest("https://api.modrinth.com/v2/version_files", HttpMethod.Post,
+                        $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")
                     For Each ModFile In Loader.Input
                     For Each ModFile In Loader.Input
                         '查找对应的文件
                         '查找对应的文件
                         If Not ModrinthRaw.ContainsKey(ModFile.ModrinthHash) Then Continue For
                         If Not ModrinthRaw.ContainsKey(ModFile.ModrinthHash) Then Continue For
@@ -589,8 +589,8 @@ Public Class PageVersionExport
                 Try
                 Try
                     If ModrinthUploadMode Then Return 'Modrinth 上传模式下,不能从 CurseForge 获取信息
                     If ModrinthUploadMode Then Return 'Modrinth 上传模式下,不能从 CurseForge 获取信息
                     Dim CurseForgeHashes = Loader.Input.Select(Function(m) m.CurseForgeHash)
                     Dim CurseForgeHashes = Loader.Input.Select(Function(m) m.CurseForgeHash)
-                    Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432/", "POST",
-                        $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
+                    Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432/", HttpMethod.Post,
+                        $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")("data")("exactMatches")
                     For Each ResultJson As JObject In CurseForgeRaw
                     For Each ResultJson As JObject In CurseForgeRaw
                         If Not ResultJson.ContainsKey("file") Then Continue For
                         If Not ResultJson.ContainsKey("file") Then Continue For
                         Dim File As JObject = ResultJson("file")
                         Dim File As JObject = ResultJson("file")

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

@@ -381,8 +381,9 @@ Install:
     ''' 下载 Mod。
     ''' 下载 Mod。
     ''' </summary>
     ''' </summary>
     Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click
     Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click
-        PageComp.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器
-        PageComp.TargetName = SearchBox.Text '将当前搜索框内容设置为筛选器
+        '预设置当前版本和搜索框内容
+        PageComp.TargetVersion = PageVersionLeft.Version
+        PageComp.TargetName = SearchBox.Text
         FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)
         FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)
     End Sub
     End Sub
 
 
@@ -429,6 +430,11 @@ Install:
         If FrmMain.PageRight IsNot Me Then Return
         If FrmMain.PageRight IsNot Me Then Return
         If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
         If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
     End Sub
     End Sub
+    Private Sub SearchBox_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles SearchBox.PreviewKeyDown
+        'Ctrl + A 会被搜索框捕获,导致无法全选,所以在按下 Ctrl + A 时转移焦点以便捕获
+        If SearchBox.Text.Any Then Return
+        If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then PanBack.Focus()
+    End Sub
 
 
 #End Region
 #End Region
 
 
@@ -637,14 +643,14 @@ Install:
                 Try
                 Try
                     For Each Entry As McMod In ModList
                     For Each Entry As McMod In ModList
                         If File.Exists(Entry.Path) Then
                         If File.Exists(Entry.Path) Then
-                            My.Computer.FileSystem.DeleteFile(Entry.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
+                            My.Computer.FileSystem.DeleteFile(Entry.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
                         Else
                         Else
                             Log($"[Mod] 未找到更新前的 Mod 文件,跳过对它的删除:{Entry.Path}", LogLevel.Debug)
                             Log($"[Mod] 未找到更新前的 Mod 文件,跳过对它的删除:{Entry.Path}", LogLevel.Debug)
                         End If
                         End If
                     Next
                     Next
                     For Each Entry As KeyValuePair(Of String, String) In FileCopyList
                     For Each Entry As KeyValuePair(Of String, String) In FileCopyList
                         If File.Exists(Entry.Value) Then
                         If File.Exists(Entry.Value) Then
-                            My.Computer.FileSystem.DeleteFile(Entry.Value, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
+                            My.Computer.FileSystem.DeleteFile(Entry.Value, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
                             Log($"[Mod] 更新后的 Mod 文件已存在,将会把它放入回收站:{Entry.Value}", LogLevel.Debug)
                             Log($"[Mod] 更新后的 Mod 文件已存在,将会把它放入回收站:{Entry.Value}", LogLevel.Debug)
                         End If
                         End If
                         If Directory.Exists(GetPathFromFullPath(Entry.Value)) Then
                         If Directory.Exists(GetPathFromFullPath(Entry.Value)) Then
@@ -733,7 +739,7 @@ Install:
                     If IsShiftPressed Then
                     If IsShiftPressed Then
                         File.Delete(ModEntity.Path)
                         File.Delete(ModEntity.Path)
                     Else
                     Else
-                        My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
+                        My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
                     End If
                     End If
                 Catch ex As OperationCanceledException
                 Catch ex As OperationCanceledException
                     Log(ex, "删除 Mod 被主动取消")
                     Log(ex, "删除 Mod 被主动取消")

+ 25 - 33
Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb

@@ -116,12 +116,14 @@
             Dim TempName As String = NewName & "_temp"
             Dim TempName As String = NewName & "_temp"
             Dim TempPath As String = PathMcFolder & "versions\" & TempName & "\"
             Dim TempPath As String = PathMcFolder & "versions\" & TempName & "\"
             Dim IsCaseChangedOnly As Boolean = NewName.ToLower = OldName.ToLower
             Dim IsCaseChangedOnly As Boolean = NewName.ToLower = OldName.ToLower
-            '重新加载版本 Json 信息,避免 HMCL 项被合并
+            '重新读取版本 JSON 信息,避免 JsonObject 中已被合并的项被重新存储
             Dim JsonObject As JObject
             Dim JsonObject As JObject
             Try
             Try
-                JsonObject = GetJson(ReadFile(PageVersionLeft.Version.Path & PageVersionLeft.Version.Name & ".json"))
+                Dim JsonPath As String = PageVersionLeft.Version.GetJsonPath()
+                JsonObject = GetJson(ReadFile(JsonPath))
+                File.Delete(JsonPath)
             Catch ex As Exception
             Catch ex As Exception
-                Log(ex, "重命名读取 json 时失败")
+                Log(ex, "在重命名读取 json 时失败")
                 JsonObject = PageVersionLeft.Version.JsonObject
                 JsonObject = PageVersionLeft.Version.JsonObject
             End Try
             End Try
             '重命名主文件夹
             '重命名主文件夹
@@ -130,32 +132,24 @@
             '清理 ini 缓存
             '清理 ini 缓存
             IniClearCache(PageVersionLeft.Version.PathIndie & "options.txt")
             IniClearCache(PageVersionLeft.Version.PathIndie & "options.txt")
             IniClearCache(PageVersionLeft.Version.Path & "PCL\Setup.ini")
             IniClearCache(PageVersionLeft.Version.Path & "PCL\Setup.ini")
-            '重命名 JAR 文件、JSON 文件与 natives 文件夹
-            If Directory.Exists(NewPath & OldName & "-natives") Then
+            '重命名 jar 文件与 natives 文件夹
+            '不能进行遍历重命名,否则在版本名很短的时候容易误伤其他文件(#6443)
+            If Directory.Exists($"{NewPath}{OldName}-natives") Then
                 If IsCaseChangedOnly Then
                 If IsCaseChangedOnly Then
-                    My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives", OldName & "natives" & "_temp")
-                    My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives" & "_temp", NewName & "-natives")
+                    My.Computer.FileSystem.RenameDirectory($"{NewPath}{OldName}-natives", $"{OldName}natives_temp")
+                    My.Computer.FileSystem.RenameDirectory($"{NewPath}{OldName}-natives_temp", $"{NewName}-natives")
                 Else
                 Else
-                    DeleteDirectory(NewPath & NewName & "-natives")
-                    My.Computer.FileSystem.RenameDirectory(NewPath & OldName & "-natives", NewName & "-natives")
+                    DeleteDirectory($"{NewPath}{NewName}-natives")
+                    My.Computer.FileSystem.RenameDirectory($"{NewPath}{OldName}-natives", $"{NewName}-natives")
                 End If
                 End If
             End If
             End If
-            If File.Exists(NewPath & OldName & ".jar") Then
+            If File.Exists($"{NewPath}{OldName}.jar") Then
                 If IsCaseChangedOnly Then
                 If IsCaseChangedOnly Then
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & ".jar", OldName & "_temp" & ".jar")
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & "_temp" & ".jar", NewName & ".jar")
+                    My.Computer.FileSystem.RenameFile($"{NewPath}{OldName}.jar", $"{OldName}_temp.jar")
+                    My.Computer.FileSystem.RenameFile($"{NewPath}{OldName}_temp.jar", $"{NewName}.jar")
                 Else
                 Else
-                    File.Delete(NewPath & NewName & ".jar")
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & ".jar", NewName & ".jar")
-                End If
-            End If
-            If File.Exists(NewPath & OldName & ".json") Then
-                If IsCaseChangedOnly Then
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & ".json", OldName & "_temp" & ".json")
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & "_temp" & ".json", NewName & ".json")
-                Else
-                    File.Delete(NewPath & NewName & ".json")
-                    My.Computer.FileSystem.RenameFile(NewPath & OldName & ".json", NewName & ".json")
+                    File.Delete($"{NewPath}{NewName}.jar")
+                    My.Computer.FileSystem.RenameFile($"{NewPath}{OldName}.jar", $"{NewName}.jar")
                 End If
                 End If
             End If
             End If
             '替换版本设置文件中的路径
             '替换版本设置文件中的路径
@@ -166,15 +160,13 @@
             If ReadIni(PathMcFolder & "PCL.ini", "Version") = OldName Then
             If ReadIni(PathMcFolder & "PCL.ini", "Version") = OldName Then
                 WriteIni(PathMcFolder & "PCL.ini", "Version", NewName)
                 WriteIni(PathMcFolder & "PCL.ini", "Version", NewName)
             End If
             End If
-            '更改版本 Json
-            If File.Exists(NewPath & NewName & ".json") Then
-                Try
-                    JsonObject("id") = NewName
-                    WriteFile(NewPath & NewName & ".json", JsonObject.ToString)
-                Catch ex As Exception
-                    Log(ex, "重命名版本 json 失败")
-                End Try
-            End If
+            '写入版本 Json
+            Try
+                JsonObject("id") = NewName
+                WriteFile(NewPath & NewName & ".json", JsonObject.ToString)
+            Catch ex As Exception
+                Log(ex, "重命名版本 json 失败")
+            End Try
             '刷新与提示
             '刷新与提示
             Hint("重命名成功!", HintType.Finish)
             Hint("重命名成功!", HintType.Finish)
             PageVersionLeft.Version = New McVersion(NewName).Load()
             PageVersionLeft.Version = New McVersion(NewName).Load()
@@ -192,7 +184,7 @@
         '选择 自定义 时修改图片
         '选择 自定义 时修改图片
         Try
         Try
             If ComboDisplayLogo.SelectedItem Is ItemDisplayLogoCustom Then
             If ComboDisplayLogo.SelectedItem Is ItemDisplayLogoCustom Then
-                Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif;*.jpeg)|*.png;*.jpg;*.gif;*.jpeg", "选择图片")
+                Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpeg;*.jpg;*.gif;*.webp)|*.png;*.jpeg;*.jpg;*.gif;*.webp", "选择图片")
                 If FileName = "" Then
                 If FileName = "" Then
                     Reload() '还原选项
                     Reload() '还原选项
                     Return
                     Return

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

@@ -209,10 +209,10 @@
                         <RowDefinition Height="Auto" />
                         <RowDefinition Height="Auto" />
                     </Grid.RowDefinitions>
                     </Grid.RowDefinitions>
                     <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="JVM 参数头部" Margin="0,0,25,0" />
                     <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="JVM 参数头部" Margin="0,0,25,0" />
-                    <local:MyTextBox x:Name="TextAdvanceJvm" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionAdvanceJvm" Grid.Column="1" MaxLength="4000" ToolTip="启动 Minecraft 时使用的额外 JVM 参数,在没有确定把握的情况下请不要尝试修改。&#xa;若留空,则跟随全局设置的值。" HintText="跟随全局设置"
+                    <local:MyTextBox x:Name="TextAdvanceJvm" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionAdvanceJvm" Grid.Column="1" MaxLength="4000" ToolTip="启动 Minecraft 时使用的额外 JVM 参数,在没有确定把握的情况下请不要尝试修改。&#xa;支持 Minecraft 版本 JSON 中的字符串替换标记,例如 ${library_directory}。&#xa;&#xa;若留空,则跟随全局设置的值。" HintText="跟随全局设置"
                                      Height="100" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" VerticalScrollBarVisibility="Auto" />
                                      Height="100" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" VerticalScrollBarVisibility="Auto" />
                     <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏参数尾部" Margin="0,0,25,0" />
                     <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏参数尾部" Margin="0,0,25,0" />
-                    <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。&#xa;若留空,则跟随全局设置的值。" HintText="跟随全局设置" />
+                    <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如,输入 --demo 则会以试玩模式启动游戏。&#xa;支持 Minecraft 版本 JSON 中的字符串替换标记,例如 ${library_directory}。&#xa;&#xa;若留空,则跟随全局设置的值。" HintText="跟随全局设置" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
                     <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
                     <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
                                      ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}options.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
                                      ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}options.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />

+ 45 - 6
Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj

@@ -140,6 +140,21 @@
     <Prefer32Bit>false</Prefer32Bit>
     <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
+    <Reference Include="CacheCow.Client, Version=2.13.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\CacheCow.Client.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="CacheCow.Client.FileStore, Version=2.13.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\CacheCow.Client.FileStore.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="CacheCow.Common, Version=2.13.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\CacheCow.Common.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="Imazen.WebP, Version=10.0.1.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="Imazen.WebP, Version=10.0.1.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
       <HintPath>Resources\Imazen.WebP.dll</HintPath>
       <HintPath>Resources\Imazen.WebP.dll</HintPath>
@@ -149,8 +164,9 @@
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
       <Private>False</Private>
     </Reference>
     </Reference>
-    <Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
+    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\Newtonsoft.Json.dll</HintPath>
       <Private>False</Private>
       <Private>False</Private>
     </Reference>
     </Reference>
     <Reference Include="Ookii.Dialogs.Wpf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c15020868fd6249, processorArchitecture=MSIL">
     <Reference Include="Ookii.Dialogs.Wpf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c15020868fd6249, processorArchitecture=MSIL">
@@ -158,13 +174,20 @@
       <HintPath>Resources\Ookii.Dialogs.Wpf.dll</HintPath>
       <HintPath>Resources\Ookii.Dialogs.Wpf.dll</HintPath>
       <Private>False</Private>
       <Private>False</Private>
     </Reference>
     </Reference>
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Core" />
     <Reference Include="System.Drawing" />
     <Reference Include="System.Drawing" />
     <Reference Include="System.IO.Compression" />
     <Reference Include="System.IO.Compression" />
     <Reference Include="System.IO.Compression.FileSystem" />
     <Reference Include="System.IO.Compression.FileSystem" />
     <Reference Include="System.Management" />
     <Reference Include="System.Management" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Net.Http" />
+    <Reference Include="System.Net.Http.Formatting, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\System.Net.Http.Formatting.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="System.Numerics" />
     <Reference Include="System.Numerics" />
+    <Reference Include="System.Runtime.Caching" />
     <Reference Include="System.ServiceModel" />
     <Reference Include="System.ServiceModel" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Xml.Linq" />
@@ -195,11 +218,14 @@
     <Compile Include="Controls\MyIconTextButton.xaml.vb">
     <Compile Include="Controls\MyIconTextButton.xaml.vb">
       <DependentUpon>MyIconTextButton.xaml</DependentUpon>
       <DependentUpon>MyIconTextButton.xaml</DependentUpon>
     </Compile>
     </Compile>
-    <Compile Include="Modules\Base\ModDragHelper.vb" />
+
+
+
     <Compile Include="Modules\Minecraft\ModComp.vb" />
     <Compile Include="Modules\Minecraft\ModComp.vb" />
     <Compile Include="Modules\Minecraft\ModJava.vb" />
     <Compile Include="Modules\Minecraft\ModJava.vb" />
     <Compile Include="Modules\Minecraft\ModMod.vb" />
     <Compile Include="Modules\Minecraft\ModMod.vb" />
     <Compile Include="Modules\Minecraft\ModModpack.vb" />
     <Compile Include="Modules\Minecraft\ModModpack.vb" />
+    <Compile Include="Modules\ThirdParty\DragHelper.vb" />
     <Compile Include="Pages\PageVersion\MyLocalModItem.xaml.vb">
     <Compile Include="Pages\PageVersion\MyLocalModItem.xaml.vb">
       <DependentUpon>MyLocalModItem.xaml</DependentUpon>
       <DependentUpon>MyLocalModItem.xaml</DependentUpon>
     </Compile>
     </Compile>
@@ -233,9 +259,6 @@
     <Compile Include="Modules\ModDevelop.vb" />
     <Compile Include="Modules\ModDevelop.vb" />
     <Compile Include="Modules\Minecraft\ModWatcher.vb" />
     <Compile Include="Modules\Minecraft\ModWatcher.vb" />
     <Compile Include="Modules\ModSecret.vb" />
     <Compile Include="Modules\ModSecret.vb" />
-    <Compile Include="Modules\Reference\CookieWebClient.vb">
-      <SubType>Component</SubType>
-    </Compile>
     <Compile Include="Pages\PageDownload\Comp\PageComp.xaml.vb">
     <Compile Include="Pages\PageDownload\Comp\PageComp.xaml.vb">
       <DependentUpon>PageComp.xaml</DependentUpon>
       <DependentUpon>PageComp.xaml</DependentUpon>
     </Compile>
     </Compile>
@@ -499,6 +522,7 @@
       <DependentUpon>FormMain.xaml</DependentUpon>
       <DependentUpon>FormMain.xaml</DependentUpon>
       <SubType>Code</SubType>
       <SubType>Code</SubType>
     </Compile>
     </Compile>
+
     <Page Include="Pages\PageVersion\MyLocalModItem.xaml">
     <Page Include="Pages\PageVersion\MyLocalModItem.xaml">
       <Generator>MSBuild:Compile</Generator>
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
       <SubType>Designer</SubType>
@@ -721,6 +745,7 @@
     <Import Include="System.IO" />
     <Import Include="System.IO" />
     <Import Include="System.Linq" />
     <Import Include="System.Linq" />
     <Import Include="System.Net" />
     <Import Include="System.Net" />
+    <Import Include="System.Net.Http" />
     <Import Include="System.Text" />
     <Import Include="System.Text" />
     <Import Include="System.Threading" />
     <Import Include="System.Threading" />
     <Import Include="System.Xml.Linq" />
     <Import Include="System.Xml.Linq" />
@@ -946,6 +971,7 @@
     <Resource Include="Images\Heads\MCBBS.png" />
     <Resource Include="Images\Heads\MCBBS.png" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Resource Include="Images\Blocks\NeoForge.png" />
     <Resource Include="Images\Blocks\NeoForge.png" />
@@ -957,5 +983,18 @@
     <None Include="Resources\Imazen.WebP.dll" />
     <None Include="Resources\Imazen.WebP.dll" />
     <None Include="Resources\libwebp64.dll" />
     <None Include="Resources\libwebp64.dll" />
   </ItemGroup>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\CacheCow.Client.FileStore.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\CacheCow.Common.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\CacheCow.Client.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\System.Net.Http.Formatting.dll" />
+  </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
 </Project>
 </Project>

BIN
Plain Craft Launcher 2/Resources/CacheCow.Client.FileStore.dll


BIN
Plain Craft Launcher 2/Resources/CacheCow.Client.dll


BIN
Plain Craft Launcher 2/Resources/CacheCow.Common.dll


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

@@ -134,6 +134,10 @@
                     Text="切换到下载页面" EventType="切换页面" EventData="1|1"/>
                     Text="切换到下载页面" EventType="切换页面" EventData="1|1"/>
         <local:MyButton Margin="0,5,0,6" Width="250" Height="35"
         <local:MyButton Margin="0,5,0,6" Width="250" Height="35"
                     Text="切换到启动器设置" EventType="切换页面" EventData="3|2"/>
                     Text="切换到启动器设置" EventType="切换页面" EventData="3|2"/>
+		<TextBlock Margin="0,4,0,4"
+                    Text="· 下载版本:切换到 EventData 对应的 Minecraft 下载页面"/>
+		<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
+                    Text="下载 Minecraft 1.12.2" EventType="下载版本" EventData="1.12.2"/>
     </StackPanel>
     </StackPanel>
 </local:MyCard>
 </local:MyCard>
 
 

BIN
Plain Craft Launcher 2/Resources/Help.zip


Datei-Diff unterdrückt, da er zu groß ist
+ 117 - 117
Plain Craft Launcher 2/Resources/ModData.txt


BIN
Plain Craft Launcher 2/Resources/Newtonsoft.Json.dll


BIN
Plain Craft Launcher 2/Resources/System.Net.Http.Formatting.dll


BIN
Plain Craft Launcher 2/Resources/java-wrapper.jar


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.