diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 082755ea..6a790926 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -6,9 +6,6 @@ contact_links: - name: 帮助文档反馈 url: https://github.com/LTCatt/PCL2Help/issues about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈 - - name: 提问 - url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE - about: 我想问一些 PCL 相关的问题…… - - name: 讨论 - url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E8%AE%A8%E8%AE%BA - about: 我想讨论一些 PCL 相关的事情…… + - name: 提问 / 讨论 + url: https://github.com/PCL-Community/PCL2-CE/discussions/new + about: 我想问问或谈谈关于社区版的事情... diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf88c42b..d4dbb7d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: windows-latest strategy: matrix: - configuration: [Debug, Release] + configuration: [Beta, Release] steps: - name: Checkout uses: actions/checkout@v4 @@ -35,6 +35,7 @@ jobs: run: | (gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public Const OAuthClientId As String = ""', 'Public Const OAuthClientId As String = "${{ secrets.CLIENT_ID }}"' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb" (gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public Const CurseForgeAPIKey As String = ""', 'Public Const CurseForgeAPIKey As String = "${{ secrets.CURSEFORGE_API_KEY }}"' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb" + (gc "Plain Craft Launcher 2\Modules\ModSecret.vb") -replace 'Public Const LittleSkinClientId As String = ""', 'Public Const LittleSkinClientId As String = "${{ secrets.LITTLESKIN_CLIENTID }}"' | Out-File "Plain Craft Launcher 2\Modules\ModSecret.vb" (gc "Plain Craft Launcher 2\Modules\Base\ModBase.vb") -replace 'Public Const CommitHash As String = ""', 'Public Const CommitHash As String = "${{ github.sha }}"' | Out-File "Plain Craft Launcher 2\Modules\Base\ModBase.vb" rm "Plain Craft Launcher 2\Resources\Help.zip" aria2c "--out=Plain Craft Launcher 2\Resources\Help.zip" "https://codeload.github.com/LTCatt/PCL2Help/zip/refs/heads/master" diff --git a/Plain Craft Launcher 2/Controls/MyCard.vb b/Plain Craft Launcher 2/Controls/MyCard.vb index 30c65882..821c5b05 100644 --- a/Plain Craft Launcher 2/Controls/MyCard.vb +++ b/Plain Craft Launcher 2/Controls/MyCard.vb @@ -164,6 +164,8 @@ Stack.Children.Add(FabricDownloadListItem(CType(Data, JObject), AddressOf FrmDownloadInstall.Fabric_Selected)) Case 13 Stack.Children.Add(NeoForgeDownloadListItem(Data, AddressOf NeoForgeSave_Click, True)) + Case 14 + Stack.Children.Add(QuiltDownloadListItem(CType(Data, JObject), AddressOf FrmDownloadInstall.Quilt_Selected)) Case Else Log("未知的虚拟化种类:" & Type, LogLevel.Feedback) End Select diff --git a/Plain Craft Launcher 2/Controls/MyHint.xaml.vb b/Plain Craft Launcher 2/Controls/MyHint.xaml.vb index fd025f03..6b36de52 100644 --- a/Plain Craft Launcher 2/Controls/MyHint.xaml.vb +++ b/Plain Craft Launcher 2/Controls/MyHint.xaml.vb @@ -10,23 +10,23 @@ Set(value As Boolean) If _IsWarn = value Then Exit Property _IsWarn = value - If _IsWarn Then - BorderBrush = New MyColor("#CCFF4444") - Gradient1.Color = New MyColor("#BBFFBBBB") - Gradient2.Color = New MyColor("#BBFF8888") - Path.Fill = New MyColor("#BF0000") - LabText.Foreground = New MyColor("#BF0000") - BtnClose.Foreground = New MyColor("#BF0000") - Path.Data = (New GeometryConverter).ConvertFromString("F1 M 58.5832,55.4172L 17.4169,55.4171C 15.5619,53.5621 15.5619,50.5546 17.4168,48.6996L 35.201,15.8402C 37.056,13.9852 40.0635,13.9852 41.9185,15.8402L 58.5832,48.6997C 60.4382,50.5546 60.4382,53.5622 58.5832,55.4172 Z M 34.0417,25.7292L 36.0208,41.9584L 39.9791,41.9583L 41.9583,25.7292L 34.0417,25.7292 Z M 38,44.3333C 36.2511,44.3333 34.8333,45.7511 34.8333,47.5C 34.8333,49.2489 36.2511,50.6667 38,50.6667C 39.7489,50.6667 41.1666,49.2489 41.1666,47.5C 41.1666,45.7511 39.7489,44.3333 38,44.3333 Z ") - Else - BorderBrush = New MyColor("#CC4D76FF") - Gradient1.Color = New MyColor("#BBB0D0FF") - Gradient2.Color = New MyColor("#BB9EBAFF") - Path.Fill = New MyColor("#0062BF") - LabText.Foreground = New MyColor("#0062BF") - BtnClose.Foreground = New MyColor("#0062BF") - Path.Data = (New GeometryConverter).ConvertFromString("F1M38,19C48.4934,19 57,27.5066 57,38 57,48.4934 48.4934,57 38,57 27.5066,57 19,48.4934 19,38 19,27.5066 27.5066,19 38,19z M33.25,33.25L33.25,36.4167 36.4166,36.4167 36.4166,47.5 33.25,47.5 33.25,50.6667 44.3333,50.6667 44.3333,47.5 41.1666,47.5 41.1666,36.4167 41.1666,33.25 33.25,33.25z M38.7917,25.3333C37.48,25.3333 36.4167,26.3967 36.4167,27.7083 36.4167,29.02 37.48,30.0833 38.7917,30.0833 40.1033,30.0833 41.1667,29.02 41.1667,27.7083 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333z") - End If + SetStyle() + End Set + End Property + + Public Enum HintType + Note + Warning + Caution + End Enum + Private _Type As HintType = HintType.Note + Public Property Type As HintType + Get + Return _Type + End Get + Set(value As HintType) + _Type = value + SetStyle() End Set End Property @@ -93,6 +93,54 @@ End Property Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyHint), New PropertyMetadata(Nothing)) + Private Sub SetStyle() + If Type = HintType.Note Then + If IsWarn Then + BorderBrush = New MyColor("#CCFF4444") + Gradient1.Color = New MyColor("#BBFFBBBB") + Gradient2.Color = New MyColor("#BBFF8888") + Path.Fill = New MyColor("#BF0000") + LabText.Foreground = New MyColor("#BF0000") + BtnClose.Foreground = New MyColor("#BF0000") + Path.Data = (New GeometryConverter).ConvertFromString("F1 M 58.5832,55.4172L 17.4169,55.4171C 15.5619,53.5621 15.5619,50.5546 17.4168,48.6996L 35.201,15.8402C 37.056,13.9852 40.0635,13.9852 41.9185,15.8402L 58.5832,48.6997C 60.4382,50.5546 60.4382,53.5622 58.5832,55.4172 Z M 34.0417,25.7292L 36.0208,41.9584L 39.9791,41.9583L 41.9583,25.7292L 34.0417,25.7292 Z M 38,44.3333C 36.2511,44.3333 34.8333,45.7511 34.8333,47.5C 34.8333,49.2489 36.2511,50.6667 38,50.6667C 39.7489,50.6667 41.1666,49.2489 41.1666,47.5C 41.1666,45.7511 39.7489,44.3333 38,44.3333 Z ") + Else + BorderBrush = New MyColor("#CC4D76FF") + Gradient1.Color = New MyColor("#BBB0D0FF") + Gradient2.Color = New MyColor("#BB9EBAFF") + Path.Fill = New MyColor("#0062BF") + LabText.Foreground = New MyColor("#0062BF") + BtnClose.Foreground = New MyColor("#0062BF") + Path.Data = (New GeometryConverter).ConvertFromString("F1M38,19C48.4934,19 57,27.5066 57,38 57,48.4934 48.4934,57 38,57 27.5066,57 19,48.4934 19,38 19,27.5066 27.5066,19 38,19z M33.25,33.25L33.25,36.4167 36.4166,36.4167 36.4166,47.5 33.25,47.5 33.25,50.6667 44.3333,50.6667 44.3333,47.5 41.1666,47.5 41.1666,36.4167 41.1666,33.25 33.25,33.25z M38.7917,25.3333C37.48,25.3333 36.4167,26.3967 36.4167,27.7083 36.4167,29.02 37.48,30.0833 38.7917,30.0833 40.1033,30.0833 41.1667,29.02 41.1667,27.7083 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333z") + End If + End If + + Select Case Type + Case HintType.Warning + BorderBrush = New MyColor("#CCE69900") + Gradient1.Color = New MyColor("#BBFFF4CE") + Gradient2.Color = New MyColor("#BBFFF5CE") + Path.Fill = New MyColor("#957500") + LabText.Foreground = New MyColor("#957500") + BtnClose.Foreground = New MyColor("#957500") + Path.Data = (New GeometryConverter).ConvertFromString("F1 M 58.5832,55.4172L 17.4169,55.4171C 15.5619,53.5621 15.5619,50.5546 17.4168,48.6996L 35.201,15.8402C 37.056,13.9852 40.0635,13.9852 41.9185,15.8402L 58.5832,48.6997C 60.4382,50.5546 60.4382,53.5622 58.5832,55.4172 Z M 34.0417,25.7292L 36.0208,41.9584L 39.9791,41.9583L 41.9583,25.7292L 34.0417,25.7292 Z M 38,44.3333C 36.2511,44.3333 34.8333,45.7511 34.8333,47.5C 34.8333,49.2489 36.2511,50.6667 38,50.6667C 39.7489,50.6667 41.1666,49.2489 41.1666,47.5C 41.1666,45.7511 39.7489,44.3333 38,44.3333 Z ") + Case HintType.Caution + BorderBrush = New MyColor("#CCFF4444") + Gradient1.Color = New MyColor("#BBFFBBBB") + Gradient2.Color = New MyColor("#BBFF8888") + Path.Fill = New MyColor("#BF0000") + LabText.Foreground = New MyColor("#BF0000") + BtnClose.Foreground = New MyColor("#BF0000") + Path.Data = (New GeometryConverter).ConvertFromString("F1 M1024,1024z M0,0z M512,0C229.23,0 0,229.23 0,512 0,794.77 229.23,1024 512,1024 794.768,1024 1024,794.77 1024,512 1024,229.23 794.77,0 512,0z M746.76,656.252C754.568,664.06,754.566,676.724,746.762,684.536L684.534,746.76C676.726,754.568,664.064,754.574,656.248,746.762L512,602.51 367.75,746.76C359.94,754.572,347.276,754.568,339.466,746.76L277.24,684.536C269.43,676.728,269.428,664.064,277.24,656.252L421.492,512 277.242,367.75C269.432,359.942,269.432,347.276,277.242,339.466L339.468,277.242C347.278,269.43,359.942,269.432,367.752,277.242L512,421.49 656.252,277.24C664.058,269.428,676.722,269.43,684.534,277.24L746.76,339.464C754.566,347.276,754.568,359.938,746.76,367.748L602.51,512 746.76,656.252z") + Case Else + BorderBrush = New MyColor("#CC4D76FF") + Gradient1.Color = New MyColor("#BBB0D0FF") + Gradient2.Color = New MyColor("#BB9EBAFF") + Path.Fill = New MyColor("#0062BF") + LabText.Foreground = New MyColor("#0062BF") + BtnClose.Foreground = New MyColor("#0062BF") + Path.Data = (New GeometryConverter).ConvertFromString("F1M38,19C48.4934,19 57,27.5066 57,38 57,48.4934 48.4934,57 38,57 27.5066,57 19,48.4934 19,38 19,27.5066 27.5066,19 38,19z M33.25,33.25L33.25,36.4167 36.4166,36.4167 36.4166,47.5 33.25,47.5 33.25,50.6667 44.3333,50.6667 44.3333,47.5 41.1666,47.5 41.1666,36.4167 41.1666,33.25 33.25,33.25z M38.7917,25.3333C37.48,25.3333 36.4167,26.3967 36.4167,27.7083 36.4167,29.02 37.48,30.0833 38.7917,30.0833 40.1033,30.0833 41.1667,29.02 41.1667,27.7083 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333z") + End Select + End Sub End Class Partial Public Module ModAnimation Public Sub AniDispose(Control As MyHint, RemoveFromChildren As Boolean, Optional CallBack As ParameterizedThreadStart = Nothing) diff --git a/Plain Craft Launcher 2/Controls/MyListItem.xaml b/Plain Craft Launcher 2/Controls/MyListItem.xaml index 1624ab83..19b54c45 100644 --- a/Plain Craft Launcher 2/Controls/MyListItem.xaml +++ b/Plain Craft Launcher 2/Controls/MyListItem.xaml @@ -10,6 +10,7 @@ + @@ -21,7 +22,14 @@ - - + + diff --git a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb index 02594cf1..2efeecae 100644 --- a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb +++ b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb @@ -46,6 +46,62 @@ '勾选条 Public RectCheck As Border + + ''' + ''' Tags 的存放 StackPanel + ''' + Public _PanTags As StackPanel + Public ReadOnly Property PanTags As StackPanel + Get + If _PanTags IsNot Nothing Then Return _PanTags + Dim NewStack As New StackPanel With { + .IsHitTestVisible = False, + .Orientation = Orientation.Horizontal, + .VerticalAlignment = VerticalAlignment.Bottom, + .Margin = New Thickness(0, 0, -3, 0) + } + SetColumn(NewStack, 3) + SetRow(NewStack, 2) + PanBack.Children.Add(NewStack) + _PanTags = NewStack + Return _PanTags + End Get + End Property + + ''' + ''' 标签,可以传入 String 和 List(Of String) + ''' + Public WriteOnly Property Tags As Object + Set(value As Object) + Dim list As New List(Of String) + If TypeOf (value) Is String Then + list = CType(value, String).Split("|").ToList() + End If + If TypeOf (value) Is List(Of String) Then + list = CType(value, List(Of String)) + End If + PanTags.Children.Clear() + PanTags.Visibility = If(list.Any(), Visibility.Visible, Visibility.Collapsed) + For Each TagText In list + Dim NewTag As New Border With { + .Background = New SolidColorBrush(Color.FromArgb(17, 0, 0, 0)), + .Padding = New Thickness(3, 1, 3, 1), + .CornerRadius = New CornerRadius(3), + .Margin = New Thickness(0, 0, 3, 0), + .SnapsToDevicePixels = True, + .UseLayoutRounding = False + } + Dim TagTextBlock As New TextBlock With { + .Text = TagText, + .Foreground = New SolidColorBrush(Color.FromRgb(134, 134, 134)), + .FontSize = 11 + } + NewTag.Child = TagTextBlock + PanTags.Children.Add(NewTag) + Next + End Set + End Property + '副文本 Private _LabInfo As TextBlock = Nothing Public ReadOnly Property LabInfo As TextBlock @@ -63,9 +119,9 @@ .Margin = New Thickness(4, 0, 0, 0), .Opacity = 0.6 } - SetColumn(Lab, 3) + SetColumn(Lab, 4) SetRow(Lab, 2) - Children.Add(Lab) + PanBack.Children.Add(Lab) _LabInfo = Lab ' diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb index 46d09341..862fc716 100644 --- a/Plain Craft Launcher 2/FormMain.xaml.vb +++ b/Plain Craft Launcher 2/FormMain.xaml.vb @@ -9,7 +9,14 @@ Public Class FormMain Dim FeatureCount As Integer = 0, BugCount As Integer = 0 Dim FeatureList As New List(Of KeyValuePair(Of Integer, String)) '统计更新日志条目 -#If BETA Then +#If RELEASE Then + If LastVersion < 350 Then 'Release 2.9.2 + FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "支持下载资源包和光影包")) + End If + If LastVersion < 349 Then 'Release 2.9.1 + FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "添加了本体更新(实验性)")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "关于页面新增了详细的版本信息")) + End If If LastVersion < 347 Then 'Release 2.8.12 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面添加下载 Mod、安装 Mod 选项")) FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 详情页面支持按加载器、游戏版本进行分类和筛选")) @@ -90,6 +97,13 @@ Public Class FormMain '3:BUG+ IMP* FEAT- '2:BUG* IMP- '1:BUG- + If LastVersion < 350 Then 'Snapshot 2.9.2 + FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "支持下载资源包和光影包")) + End If + If LastVersion < 349 Then 'Snapshot 2.9.1 + FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "添加了本体更新(实验性)")) + FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "关于页面新增了详细的版本信息")) + End If If LastVersion < 346 Then 'Snapshot 2.8.12 If LastVersion = 345 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复帮助页面报错的 Bug")) End If @@ -376,12 +390,6 @@ Public Class FormMain Private Sub RunCountSub() Setup.Set("SystemCount", Setup.Get("SystemCount") + 1) #If Not BETA Then - Select Case Setup.Get("SystemCount") - Case 1 - MyMsgBox("欢迎使用 PCL 快照版!" & vbCrLf & - "快照版包含尚未在正式版发布的测试性功能,仅用于赞助者本人尝鲜。所以请不要发给其他人或者用于制作整合包哦!" & vbCrLf & - "如果你并非通过赞助或赞助者本人邀请进群获得的本程序,那么可能是有人在违规传播,记得提醒他一下啦。", "快照版使用说明") - End Select If Setup.Get("SystemCount") >= 99 Then If ThemeUnlock(6, False) Then MyMsgBox("你已经使用了 99 次 PCL 啦,感谢你长期以来的支持!" & vbCrLf & @@ -772,7 +780,7 @@ Public Class FormMain End If '多文件拖拽 If FilePathList.Count > 1 Then - '必须要求全部为 jar 文件 + '必须要求全部为 Jar 文件 For Each File In FilePathList If Not {"jar", "litemod", "disabled", "old"}.Contains(File.AfterLast(".").ToLower) Then Hint("一次请只拖入一个文件!", HintType.Critical) @@ -961,9 +969,13 @@ Public Class FormMain DownloadForge = 6 DownloadNeoForge = 7 DownloadFabric = 8 + DownloadQuilt = 10 DownloadLiteLoader = 9 DownloadMod = 11 DownloadPack = 12 + DownloadResourcePack = 13 + DownloadShader = 14 + DownloadCompFavorites = 15 SetupLaunch = 0 SetupUI = 1 SetupSystem = 2 @@ -976,6 +988,8 @@ Public Class FormMain OtherHelp = 0 OtherAbout = 1 OtherTest = 2 + OtherFeedback = 3 + OtherVote = 4 VersionOverall = 0 VersionSetup = 1 VersionWorld = 3 @@ -1003,8 +1017,10 @@ Public Class FormMain Return "Mod 下载 - " & Project.TranslatedName Case CompType.ModPack Return "整合包下载 - " & Project.TranslatedName - Case Else 'CompType.ResourcePack + Case CompType.ResourcePack Return "资源包下载 - " & Project.TranslatedName + Case Else 'CompType.Shader + Return "光影包下载 - " & Project.TranslatedName End Select Case PageType.HelpDetail Dim Entry As HelpEntry = Stack.Additional(0) diff --git a/Plain Craft Launcher 2/Images/Blocks/Quilt.png b/Plain Craft Launcher 2/Images/Blocks/Quilt.png new file mode 100644 index 00000000..7a29ce57 Binary files /dev/null and b/Plain Craft Launcher 2/Images/Blocks/Quilt.png differ diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb index a4fc1239..9f9179ce 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb @@ -12,21 +12,22 @@ Public Module ModBase #Region "声明" '下列版本信息由更新器自动修改 - Public Const VersionBaseName As String = "2.8.12" '不含分支前缀的显示用版本名 - Public Const VersionStandardCode As String = "2.8.12." & VersionBranchCode '标准格式的四段式版本号 + Public Const VersionBaseName As String = "2.9.2" '不含分支前缀的显示用版本名 + Public Const VersionStandardCode As String = "2.9.2." & VersionBranchCode '标准格式的四段式版本号 Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换 -#If BETA Then - Public Const VersionCode As Integer = 347 'Release + Public Const UpstreamVersion As String = "2.8.12" '上游版本 +#If RELEASE Then + Public Const VersionCode As Integer = 350 'Release #Else - Public Const VersionCode As Integer = 346 'Snapshot + Public Const VersionCode As Integer = 350 'Snapshot #End If '自动生成的版本信息 Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName #If RELEASE Then - Public Const VersionBranchName As String = "Snapshot" + Public Const VersionBranchName As String = "Release" Public Const VersionBranchCode As String = "0" #ElseIf BETA Then - Public Const VersionBranchName As String = "Release" + Public Const VersionBranchName As String = "Snapshot" Public Const VersionBranchCode As String = "50" #Else Public Const VersionBranchName As String = "Debug" @@ -1216,7 +1217,7 @@ Re: Try GetJson(Content) Catch ex As Exception - Throw New Exception("不是有效的 json 文件", ex) + Throw New Exception("不是有效的 Json 文件", ex) End Try End If Return Nothing @@ -1228,7 +1229,7 @@ Re: End Class ''' - ''' 尝试根据后缀名判断文件种类并解压文件,支持 gz 与 zip,会尝试将 jar 以 zip 方式解压。 + ''' 尝试根据后缀名判断文件种类并解压文件,支持 gz 与 zip,会尝试将 Jar 以 zip 方式解压。 ''' 会尝试创建,但不会清空目标文件夹。 ''' Public Sub ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing, @@ -2786,7 +2787,12 @@ Retry: End Select End Sub + Public Function Base64Decode(Text As String) As String + + Dim decodedBytes As Byte() = Convert.FromBase64String(Text) + Return System.Text.Encoding.UTF8.GetString(decodedBytes) + End Function '反馈 Public Sub Feedback(Optional ShowMsgbox As Boolean = True, Optional ForceOpenLog As Boolean = False) On Error Resume Next @@ -2868,4 +2874,4 @@ Retry: #End Region -End Module \ No newline at end of file +End Module diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb index f046d349..3a2baed7 100644 --- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb +++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb @@ -3,6 +3,7 @@ Public Module ModNet Public Const NetDownloadEnd As String = ".PCLDownloading" + ''' ''' 测试 Ping。失败则返回 -1。 ''' @@ -185,7 +186,7 @@ RequestFinished: Public Function NetGetCodeByRequestOnce(Url As String, Optional Encode As Encoding = Nothing, Optional Timeout As Integer = 30000, Optional IsJson As Boolean = False, Optional Accept As String = "", Optional UseBrowserUserAgent As Boolean = False) If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求") Url = SecretCdnSign(Url) - Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 json", "")}") + Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 Json", "")}") Dim Request As HttpWebRequest = WebRequest.Create(Url) Dim Result As New List(Of Byte) Try @@ -376,6 +377,8 @@ RequestFinished: Dim DataStream As Stream = Nothing Dim Resp As WebResponse = Nothing Dim Req As HttpWebRequest + Dim Res = "" + Try Req = WebRequest.Create(Url) Req.Method = Method @@ -406,9 +409,11 @@ RequestFinished: DataStream = Resp.GetResponseStream() DataStream.WriteTimeout = Timeout DataStream.ReadTimeout = Timeout + Dim Status As Integer = CType(Resp, HttpWebResponse).StatusCode Using Reader As New StreamReader(DataStream) - Return Reader.ReadToEnd() + Res = Reader.ReadToEnd() End Using + Return Res Catch ex As ThreadInterruptedException Throw Catch ex As WebException @@ -416,7 +421,6 @@ RequestFinished: ex = New WebException($"连接服务器超时,请检查你的网络环境是否良好({ex.Message},{Url})", ex) Else '获取请求失败的返回 - Dim Res As String = "" Try If ex.Response Is Nothing Then Exit Try DataStream = ex.Response.GetResponseStream() @@ -429,6 +433,7 @@ RequestFinished: End Try If Res = "" Then ex = New WebException($"网络请求失败({ex.Status},{ex.Message},{Url})", ex) + Throw ex Else ex = New ResponsedWebException($"服务器返回错误({ex.Status},{ex.Message},{Url}){vbCrLf}{Res}", Res, ex) End If @@ -443,6 +448,7 @@ RequestFinished: If DataStream IsNot Nothing Then DataStream.Dispose() If Resp IsNot Nothing Then Resp.Dispose() End Try + Return Res End Function Public Class ResponsedWebException Inherits WebException @@ -2009,4 +2015,4 @@ Retry: Return False End Function -End Module +End Module \ No newline at end of file diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb index 36cc7ee5..66a03147 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb @@ -13,6 +13,10 @@ ''' 资源包。 ''' ResourcePack = 2 + ''' + ''' 光影包。 + ''' + Shader = 3 End Enum Public Enum CompModLoaderType 'https://docs.curseforge.com/?http#tocS_ModLoaderType @@ -279,8 +283,10 @@ Type = CompType.Mod ElseIf Website.Contains("/modpacks/") Then Type = CompType.ModPack - Else + ElseIf Website.Contains("/texture-packs/") Then Type = CompType.ResourcePack + Else 'Website.Contains("/shaders/") + Type = CompType.Shader End If 'Tags Tags = New List(Of String) @@ -328,7 +334,28 @@ Case 4480 : Tags.Add("基于地图") Case 4481 : Tags.Add("轻量") Case 4482 : Tags.Add("大型") - 'FUTURE: Res + '光影包 + Case 6553 : Tags.Add("写实") + Case 6554 : Tags.Add("幻想") + Case 6555 : Tags.Add("原版风") + '资源包 + Case 5244 : Tags.Add("字体包") + Case 5193 : Tags.Add("数据包") + Case 399 : Tags.Add("蒸汽朋克") + Case 396 : Tags.Add("128x") + Case 398 : Tags.Add("512x 或更高") + Case 397 : Tags.Add("256x") + Case 405 : Tags.Add("其他") + Case 395 : Tags.Add("64x") + Case 400 : Tags.Add("仿真") + Case 393 : Tags.Add("16x") + Case 403 : Tags.Add("传统") + Case 394 : Tags.Add("32x") + Case 404 : Tags.Add("动态效果") + Case 4465 : Tags.Add("模组支持") + Case 402 : Tags.Add("中世纪") + Case 401 : Tags.Add("现代") + End Select Next If Not Tags.Any() Then Tags.Add("杂项") @@ -356,6 +383,7 @@ Case "mod" : Type = CompType.Mod Case "modpack" : Type = CompType.ModPack Case "resourcepack" : Type = CompType.ResourcePack + Case "shader" : Type = CompType.Shader End Select 'Tags & ModLoaders Tags = New List(Of String) @@ -394,7 +422,63 @@ Case "adventure" : Tags.Add("冒险") Case "kitchen-sink" : Tags.Add("大杂烩") Case "lightweight" : Tags.Add("轻量") - 'FUTURE: Res + '光影包 + Case "cartoon" : Tags.Add("卡通") + Case "cursed" : Tags.Add("Cursed") + Case "fantasy" : Tags.Add("幻想") + Case "realistic" : Tags.Add("写实") + Case "semi-realistic" : Tags.Add("半写实") + Case "vanilla-like" : Tags.Add("原版风") + + Case "atmosphere" : Tags.Add("大气环境") + Case "bloom" : Tags.Add("植被") + Case "colored-lighting" : Tags.Add("光源着色") + Case "foliage" : Tags.Add("树叶") + Case "path-tracing" : Tags.Add("路径追踪") + Case "pbr" : Tags.Add("PBR") + Case "reflections" : Tags.Add("反射") + Case "shadows" : Tags.Add("阴影") + + Case "potato" : Tags.Add("土豆画质") + Case "low" : Tags.Add("低性能影响") + Case "medium" : Tags.Add("中性能影响") + Case "high" : Tags.Add("高性能影响") + Case "screenshot" : Tags.Add("极致画质") + + Case "canvas" : Tags.Add("Canvas") + Case "iris" : Tags.Add("Iris") + Case "optifine" : Tags.Add("OptiFine") + Case "vanilla" : Tags.Add("原版光影") + '资源包 + Case "8x-" : Tags.Add("8x-") + Case "16x" : Tags.Add("16x") + Case "32x" : Tags.Add("32x") + Case "48x" : Tags.Add("48x") + Case "64x" : Tags.Add("64x") + Case "128x" : Tags.Add("128x") + Case "256x" : Tags.Add("256x") + Case "512x+" : Tags.Add("512x+") + Case "audio" : Tags.Add("声音") + Case "blocks" : Tags.Add("方块") + Case "combat" : Tags.Add("战斗") + Case "core-shaders" : Tags.Add("核心着色器") + Case "cursed" : Tags.Add("Cursed") + Case "decoration" : Tags.Add("装饰") + Case "entities" : Tags.Add("实体") + Case "environment" : Tags.Add("环境") + Case "equipment" : Tags.Add("装备") + Case "fonts" : Tags.Add("字体") + Case "gui" : Tags.Add("GUI") + Case "items" : Tags.Add("物品") + Case "locale" : Tags.Add("本地化") + Case "modded" : Tags.Add("Modded") + Case "models" : Tags.Add("模型") + Case "realistic" : Tags.Add("写实") + Case "simplistic" : Tags.Add("扁平") + Case "themed" : Tags.Add("主题") + Case "tweaks" : Tags.Add("优化") + Case "utility" : Tags.Add("实用") + Case "vanilla-like" : Tags.Add("类原生") End Select Next If Not Tags.Any() Then Tags.Add("杂项") @@ -547,6 +631,15 @@ If(DownloadCount > 100000, Math.Floor(DownloadCount / 10000) & " 万", DownloadCount)) Return NewItem End Function + Public Function ToListItem() As MyListItem + Dim Result As New MyListItem() + Result.Title = TranslatedName + Result.Info = Description.Replace(vbCr, "").Replace(vbLf, "") + Result.Logo = LogoUrl + Result.Tags = Tags + Result.Tag = Me + Return Result + End Function Public Function GetControlLogo() As String If String.IsNullOrEmpty(LogoUrl) Then Return PathImage & "Icons/NoIcon.png" @@ -762,7 +855,9 @@ NoSubtitle: Case CompType.ModPack Address += "&classId=4471" Case CompType.ResourcePack - 'FUTURE: Res + Address += "&classId=12" + Case CompType.Shader + Address += "&classId=6552" End Select Address += "&categoryId=" & If(Tag = "", "0", Tag.BeforeFirst("/")) If ModLoader <> CompModLoaderType.Any Then Address += "&modLoaderType=" & CType(ModLoader, Integer) @@ -1516,5 +1611,117 @@ Retry: End Sub #End Region + Class CompFavorites + + ''' + ''' 收藏的工程列表 + ''' + Private Shared _FavoritesList As List(Of String) = Nothing + Public Shared Property FavoritesList As List(Of String) + Get + If _FavoritesList Is Nothing Then + Dim RawData As String = Setup.Get("CompFavorites") + Dim RawList As JArray = JArray.Parse(RawData) + _FavoritesList = RawList.ToObject(Of List(Of String))() + End If + Return _FavoritesList + End Get + Set + _FavoritesList = Value + Dim RawList = JArray.FromObject(_FavoritesList) + Setup.Set("CompFavorites", RawList.ToString()) + End Set + End Property + + Public Shared Sub Save() + FavoritesList = _FavoritesList + End Sub + End Class + + Class CompRequest + ''' + ''' 通过项目 Id 判断是否来自 CurseForge + ''' + ''' + ''' + Public Shared Function IsFromCurseForge(Id As String) As Boolean + Dim res As Integer = 0 + Return Integer.TryParse(Id, res) 'CurseForge 数字 ID Modrinth 乱序 ID + End Function + + ''' + ''' 通过一堆 ID 从 Modrinth 那获取项目信息 + ''' + ''' + ''' + Public Shared Function GetListByIdsFromModrinth(Ids As List(Of String)) As List(Of CompProject) + Dim Res As New List(Of CompProject) + Dim RawProjectsData = DlModRequest($"https://api.modrinth.com/v2/projects?ids=[""{Ids.Join(""",""")}""]", IsJson:=True) + For Each RawData In RawProjectsData + Res.Add(New CompProject(RawData)) + Next + Return Res + End Function + + ''' + ''' 通过一堆 ID 从 CurseForge 那获取项目信息 + ''' + ''' + ''' + Public Shared Function GetListByIdsFromCurseforge(Ids As List(Of String)) As List(Of CompProject) + Dim Res As New List(Of CompProject) + Dim RawProjectsData = GetJson(DlModRequest("https://api.curseforge.com/v1/mods", + "POST", "{""modIds"": [" & Ids.Join(",") & "]}", "application/json"))("data") + For Each RawData In RawProjectsData + Res.Add(New CompProject(RawData)) + Next + Return Res + End Function + Public Shared Function GetCompProjectsByIds(Input As List(Of String)) As List(Of CompProject) + If Not Input.Any() Then Return New List(Of CompProject) + Dim RawList As List(Of String) = Input + Dim ModrinthProjectIds As New List(Of String) + Dim CurseForgeProjectIds As New List(Of String) + Dim Res As List(Of CompProject) = New List(Of CompProject) + For Each Id In RawList + If IsFromCurseForge(Id) Then + CurseForgeProjectIds.Add(Id) + Else + ModrinthProjectIds.Add(Id) + End If + Next + '在线信息获取 + Dim FinishedTask = 0 + Dim NeedCompleteTask = 0 + If CurseForgeProjectIds.Any() Then + NeedCompleteTask += 1 + RunInNewThread(Sub() + Try + Res.AddRange(CompRequest.GetListByIdsFromCurseforge(CurseForgeProjectIds)) + Catch ex As Exception + Log(ex, "[Favorites] 获取 CurseForge 数据失败", LogLevel.Hint) + Finally + FinishedTask += 1 + End Try + End Sub, "Favorites CurseForge") + End If + If ModrinthProjectIds.Any() Then + NeedCompleteTask += 1 + RunInNewThread(Sub() + Try + Res.AddRange(CompRequest.GetListByIdsFromModrinth(ModrinthProjectIds)) + Catch ex As Exception + Log(ex, "[Favorites] 获取 Modrinth 数据失败", LogLevel.Hint) + Finally + FinishedTask += 1 + End Try + End Sub, "Favorites Modrinth") + End If + Do Until FinishedTask = NeedCompleteTask + Thread.Sleep(50) + Loop + Return Res + End Function + End Class End Module diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb index 81c1a664..8f9e3fab 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb @@ -17,7 +17,7 @@ End Try '检查 Json 是否标准 If Version.JsonObject("downloads") Is Nothing OrElse Version.JsonObject("downloads")("client") Is Nothing OrElse Version.JsonObject("downloads")("client")("url") Is Nothing Then - Throw New Exception("底层版本 " & Version.Name & " 中无 jar 文件下载信息") + Throw New Exception("底层版本 " & Version.Name & " 中无 Jar 文件下载信息") End If '检查文件 Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1")) @@ -1091,6 +1091,84 @@ #End Region +#Region "DlQuiltList | Quilt 列表" + + Public Structure DlQuiltListResult + ''' + ''' 数据来源名称,如“Official”,“BMCLAPI”。 + ''' + Public SourceName As String + ''' + ''' 是否为官方的实时数据。 + ''' + Public IsOfficial As Boolean + ''' + ''' 获取到的数据。 + ''' + Public Value As JObject + End Structure + + ''' + ''' Quilt 列表,主加载器。 + ''' + Public DlQuiltListLoader As New LoaderTask(Of Integer, DlQuiltListResult)("DlQuiltList Main", AddressOf DlQuiltListMain) + Private Sub DlQuiltListMain(Loader As LoaderTask(Of Integer, DlQuiltListResult)) + Select Case Setup.Get("ToolDownloadVersion") + Case 0 + DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)) From { + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 30), + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 60) + }, Loader.IsForceRestarting) + Case 1 + DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)) From { + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 5), + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 35) + }, Loader.IsForceRestarting) + Case Else + DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)) From { + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 60), + New KeyValuePair(Of LoaderTask(Of Integer, DlQuiltListResult), Integer)(DlQuiltListOfficialLoader, 60) + }, Loader.IsForceRestarting) + End Select + End Sub + + ''' + ''' Quilt 列表,官方源。 + ''' + Public DlQuiltListOfficialLoader As New LoaderTask(Of Integer, DlQuiltListResult)("DlQuiltList Official", AddressOf DlQuiltListOfficialMain) + Private Sub DlQuiltListOfficialMain(Loader As LoaderTask(Of Integer, DlQuiltListResult)) + Dim Result As JObject = NetGetCodeByRequestRetry("https://meta.quiltmc.org/v3/versions", IsJson:=True) + Try + Dim Output = New DlQuiltListResult With {.IsOfficial = True, .SourceName = "Quilt 官方源", .Value = Result} + If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项") + Loader.Output = Output + Catch ex As Exception + Throw New Exception("Quilt 官方源版本列表解析失败(" & Result.ToString & ")", ex) + End Try + End Sub + + '''' + '''' TODO: Quilt 列表,BMCLAPI。 + '''' + 'Public DlQuiltListBmclapiLoader As New LoaderTask(Of Integer, DlQuiltListResult)("DlQuiltList Bmclapi", AddressOf DlQuiltListBmclapiMain) + 'Private Sub DlQuiltListBmclapiMain(Loader As LoaderTask(Of Integer, DlQuiltListResult)) + ' Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/Quilt-meta/v2/versions", IsJson:=True) + ' Try + ' Dim Output = New DlQuiltListResult 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("获取到的列表缺乏必要项") + ' Loader.Output = Output + ' Catch ex As Exception + ' Throw New Exception("Quilt BMCLAPI 版本列表解析失败(" & Result.ToString & ")", ex) + ' End Try + 'End Sub + + ''' + ''' QSL 列表,官方源。 + ''' + Public DlQSLLoader As New LoaderTask(Of Integer, List(Of CompFile))("QSL List Loader", + Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet("qsl", False)) +#End Region + #Region "DlMod | Mod 镜像源请求" ''' diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb index 62e457f5..daaba4d7 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb @@ -218,7 +218,7 @@ NextInner: Dim CheckResult As String = "" RunInUiWait(Sub() CheckResult = McLoginAble(McLoginInput())) If CheckResult <> "" Then Throw New ArgumentException(CheckResult) -#If BETA Then +#If RELEASE Then '求赞助 RunInNewThread( Sub() @@ -501,7 +501,7 @@ NextInner: #Region "分方式登录模块" '各个登录方式的主对象与输入构造 - Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart) With {.ReloadTimeout = 1} + Public McLoginMsLoader As New LoaderTask(Of McLoginMs, McLoginResult)("Loader Login Ms", AddressOf McLoginMsStart) Public McLoginLegacyLoader As New LoaderTask(Of McLoginLegacy, McLoginResult)("Loader Login Legacy", AddressOf McLoginLegacyStart) Public McLoginNideLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Nide", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10} Public McLoginAuthLoader As New LoaderTask(Of McLoginServer, McLoginResult)("Loader Login Auth", AddressOf McLoginServerStart) With {.ReloadTimeout = 1000 * 60 * 10} @@ -629,6 +629,7 @@ LoginFinish: Dim Dict As New Dictionary(Of String, String) Dim Emails As New List(Of String) Dim Passwords As New List(Of String) + Try If Not Setup.Get("Login" & Input.Token & "Email") = "" Then Emails.AddRange(Setup.Get("Login" & Input.Token & "Email").ToString.Split("¨")) If Not Setup.Get("Login" & Input.Token & "Pass") = "" Then Passwords.AddRange(Setup.Get("Login" & Input.Token & "Pass").ToString.Split("¨")) @@ -696,7 +697,41 @@ LoginFinish: End Sub Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean) McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")") - Dim LoginJson As JObject = GetJson(NetRequestRetry( + Dim LittleSkinAccess = Setup.Get("Cache" & Data.Input.Token & "APIToken") + Dim LittleSkinRefresh = Setup.Get("Cache" & Data.Input.Token & "Refresh") + Dim RefreshData As New JObject() + Dim UserName As String + Dim UUID As String + Dim AccessToken As String + Dim RefreshToken As String + Dim IDToken As String + Dim LoginJson As JObject + 'AccessToken 和 RefreshToken 都要有,不然没法刷新 + If Not LittleSkinAccess = "" AndAlso Not LittleSkinRefresh = "" Then + RefreshData.Add(New JProperty("grant_type", "refresh_token")) + RefreshData.Add(New JProperty("refresh_token", LittleSkinRefresh)) + RefreshData.Add(New JProperty("access_token", LittleSkinAccess)) + RefreshData.Add(New JProperty("client_id", LittleSkinClientId)) + LoginJson = GetJson(NetRequestOnce( + Url:="https://open.littleskin.cn/oauth/token", + Method:="POST", + Data:=RefreshData, + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, + ContentType:="application/json; charset=utf-8")) + AccessToken = LoginJson("access_token").ToString(0) + RefreshToken = LoginJson("refresh_token").ToString(0) + IDToken = LoginJson("id_token").ToString(0) + Dim SelectPrpfile As String = Base64Decode(IDToken.Split(".")(1)) + UserName = GetJson(GetJson(SelectPrpfile)("selectedProfile").ToString)("name").ToString + UUID = GetJson(GetJson(SelectPrpfile)("selectedProfile").ToString)("id").ToString + Data.Output.Uuid = UUID + Data.Output.Name = UserName + Setup.Set("Cache" & Data.Input.Token & "Refresh", RefreshToken) + Setup.Set("Cache" & Data.Input.Token & "APIToken", AccessToken) + Else + Throw New Exception("登录信息无效") + End If + LoginJson = GetJson(NetRequestRetry( Url:=Data.Input.BaseUrl & "/refresh", Method:="POST", Data:="{" & @@ -713,39 +748,148 @@ LoginFinish: If LoginJson("selectedProfile") Is Nothing Then Throw New Exception("选择的角色 " & Setup.Get("Cache" & Data.Input.Token & "Name") & " 无效!") Data.Output.AccessToken = LoginJson("accessToken").ToString Data.Output.ClientToken = LoginJson("clientToken").ToString - Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString - Data.Output.Name = LoginJson("selectedProfile")("name").ToString Data.Output.Type = Data.Input.Token + If Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString AndAlso Data.Output.Name = LoginJson("selectedProfile")("name").ToString Then + + Else + Data.Output.Uuid = LoginJson("selectedProfile")("id").ToString + Data.Output.Name = LoginJson("selectedProfile")("name").ToString + End If + '保存缓存 + Setup.Set("Cache" & Data.Input.Token & "Access", Data.Output.AccessToken) Setup.Set("Cache" & Data.Input.Token & "Client", Data.Output.ClientToken) Setup.Set("Cache" & Data.Input.Token & "Uuid", Data.Output.Uuid) Setup.Set("Cache" & Data.Input.Token & "Name", Data.Output.Name) - Setup.Set("Cache" & Data.Input.Token & "Username", Data.Input.UserName) - Setup.Set("Cache" & Data.Input.Token & "Pass", Data.Input.Password) + If Data.Input.UserName Is Nothing Then + ' 用一个通用字符模板填充内容 + Setup.Set("Cache" & Data.Input.Token & "Username", "LittleSkinOAuth 登录") + Setup.Set("Cache" & Data.Input.Token & "Pass", "LittleSkinOAuth 登录") + End If McLaunchLog("刷新登录成功(Refresh, " & Data.Input.Token & ")") + End Sub + + Private Function McLoginRequestLogin(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult)) As Boolean Try Dim NeedRefresh As Boolean = False - McLaunchLog("登录开始(Login, " & Data.Input.Token & ")") - Dim RequestData As New JObject( - New JProperty("agent", New JObject(New JProperty("name", "Minecraft"), New JProperty("version", 1))), - New JProperty("username", Data.Input.UserName), - New JProperty("password", Data.Input.Password), - New JProperty("requestUser", True)) - Dim LoginJson As JObject = GetJson(NetRequestRetry( + Dim RequestData As New JObject() + Dim LittleSkinOAuth As Boolean = False + Dim LoginJson + Dim AccessToken As String + Dim RefreshToken As String + 'LittleSkin OAuth 登录检查 + + If Data.Input.BaseUrl.ToLower.Contains("littleskin.cn") And Not LittleSkinClientId = "" Then + 'ClientId + RequestData.Add(New JProperty("client_id", LittleSkinClientId)) + '一堆权限 + RequestData.Add(New JProperty("scope", "offline_access openid Yggdrasil.PlayerProfiles.Select Yggdrasil.MinecraftToken.Create")) + LittleSkinOAuth = True + Else + RequestData.Add(New JProperty("agent", New JObject(New JProperty("name", "Minecraft"), New JProperty("version", 1)))) + RequestData.Add(New JProperty("username", Data.Input.UserName)) + RequestData.Add(New JProperty("password", Data.Input.Password)) + RequestData.Add(New JProperty("requestUser", True)) + LittleSkinOAuth = False + End If + + ' LittleSkin OAuth 登录步骤 1: 获取授权代码 + If LittleSkinOAuth Then + McLaunchLog("开始 LittleSkin OAuth 登录步骤 1/3(原始登录)") + LoginJson = GetJson(NetRequestOnce( + Url:="https://open.littleskin.cn/oauth/device_code", + Method:="POST", + Data:=RequestData.ToString(0), + Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, + ContentType:="application/json; charset=utf-8")) + Else + LoginJson = GetJson(NetRequestRetry( Url:=Data.Input.BaseUrl & "/authenticate", Method:="POST", Data:=RequestData.ToString(0), Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh_CN"}}, ContentType:="application/json; charset=utf-8")) + + + End If '检查登录结果 - If LoginJson("availableProfiles").Count = 0 Then - If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色,无法更换!", HintType.Critical) - Throw New Exception("$你还没有创建角色,请在创建角色后再试!") - ElseIf Data.Input.ForceReselectProfile AndAlso LoginJson("availableProfiles").Count = 1 Then - Hint("你的账户中只有一个角色,无法更换!", HintType.Critical) + ' LittleSkin + If LittleSkinOAuth Then + ' 轮询验证 + '花了点时间改了逻辑。现在按照 OAuth 标准编写的服务端应该都能用这个处理轮询登录 + Dim Converter As New MyMsgBoxConverter With {.Content = LoginJson, .ForceWait = True, .Type = MyMsgBoxType.Login, .AuthUrl = "https://open.littleskin.cn/oauth/token"} + WaitingMyMsgBox.Add(Converter) + While Converter.Result Is Nothing + Thread.Sleep(100) + End While + 'LittleSkin OAuth 登录步骤 2: 获取用户选定档案 + McLaunchLog("LittleSkin OAuth 登录步骤 2/3") + '因为还有个 ID Token,硬编码只解析 AccessToken 和 RefreshToken + Dim Result = Converter.Result + Dim IDToken As String + AccessToken = Result("access_token") + RefreshToken = Result("refresh_token") + Dim ProfileData As New JObject() + Dim UserName As String + Dim UUID As String + ' IDToken 编码的档案 + ' 这一行不能用 Try Catch,否则后面解析会抛出 NullReferenceException + IDToken = Result("id_token") + Dim SelectProfile = GetJson(Base64Decode(IDToken.ToString.Split(".")(1)))("selectedProfile").ToString + UserName = GetJson(SelectProfile)("name") + UUID = GetJson(SelectProfile)("id") + + 'LittleSkin OAuth 登录步骤 3::获取 Minecraft AccessToken 启动令牌 + McLaunchLog("LittleSkin OAuth 登录步骤 3/3") + Dim RequestInfo = New JObject() + RequestInfo.Add(New JProperty("uuid", UUID)) + '需要 Yggdrasil.MinecraftToken.Create + Result = GetJson(NetRequestOnce(Url:=Data.Input.BaseUrl & "/oauth", + Method:="POST", + Data:=RequestInfo.ToString(0), + Headers:=New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}}, + ContentType:="application/json; charset=utf-8")) + + + + Dim ClientToken = Result("clientToken").ToString + Dim MCAccessToken = Result("accessToken").ToString + + McLaunchLog("LittleSkin OAuth 登录完成") + + Data.Output.AccessToken = MCAccessToken + Data.Output.ClientToken = ClientToken + Data.Output.Name = UserName + Data.Output.Uuid = UUID + Data.Output.Type = Data.Input.Token + + Setup.Set("Cache" & Data.Input.Token & "Access", Data.Output.AccessToken) + Setup.Set("Cache" & Data.Input.Token & "Refresh", RefreshToken) + Setup.Set("Cache" & Data.Input.Token & "Uuid", Data.Output.Uuid) + Setup.Set("Cache" & Data.Input.Token & "Name", Data.Output.Name) + '刷新需要 AccessToken 和 RefreshToken 配合使用 + Setup.Set("Cache" & Data.Input.Token & "APIToken", AccessToken) + If Data.Input.UserName Is Nothing AndAlso Data.Input.Password Is Nothing Then + Setup.Set("Cache" & Data.Input.Token & "Username", "LittleSkinOAuth 登录") + Setup.Set("Cache" & Data.Input.Token & "Pass", "LittleSkinOAuth 登录") + Else + Setup.Set("Cache" & Data.Input.Token & "Username", Data.Input.UserName) + Setup.Set("Cache" & Data.Input.Token & "Pass", Data.Input.Password) + End If + Return True + '原始登录 + Else + If LoginJson("availableProfiles").Count = 0 Then + If Data.Input.ForceReselectProfile Then Hint("你还没有创建角色, 无法更换!", HintType.Critical) + Throw New Exception("$你还没有创建角色, 请在创建角色后再试!") + ElseIf Data.Input.ForceReselectProfile AndAlso LoginJson("availableProfiles").Count = 1 Then + Hint("你的账户中只有一个角色, 无法更换!", HintType.Critical) + + End If + End If Dim SelectedName As String = Nothing Dim SelectedId As String = Nothing @@ -757,7 +901,7 @@ LoginFinish: If Profile("id").ToString = CacheId Then SelectedName = Profile("name").ToString SelectedId = Profile("id").ToString - McLaunchLog("根据缓存选择的角色:" & SelectedName) + McLaunchLog("根据缓存选择的角色: " & SelectedName) End If Next '缓存无效,要求玩家选择 @@ -873,7 +1017,8 @@ Retry: ElseIf TypeOf Converter.Result Is Exception Then Throw CType(Converter.Result, Exception) Else - Return Converter.Result + Dim LoginData = Converter.Result + Return {LoginData("access_token"), LoginData("refresh_token")} End If End Function '微软登录步骤 1,刷新登录:从 OAuth Code 或 OAuth RefreshToken 获取 {OAuth AccessToken, OAuth RefreshToken} @@ -893,7 +1038,10 @@ Retry: End If End Try + + Dim ResultJson As JObject = GetJson(Result) + Dim AccessToken As String = ResultJson("access_token").ToString Dim RefreshToken As String = ResultJson("refresh_token").ToString Return {AccessToken, RefreshToken} @@ -901,7 +1049,6 @@ Retry: '微软登录步骤 2:从 OAuth AccessToken 获取 XBLToken Private Function MsLoginStep2(AccessToken As String) As String McLaunchLog("开始微软登录步骤 2/6") - Dim Request As String = "{ ""Properties"": { ""AuthMethod"": ""RPS"", @@ -1418,7 +1565,7 @@ Retry: '添加 MainClass If Version.JsonObject("mainClass") Is Nothing Then - Throw New Exception("版本 json 中没有 mainClass 项!") + Throw New Exception("版本 Json 中没有 mainClass 项!") Else DataList.Add(Version.JsonObject("mainClass")) End If @@ -1509,7 +1656,7 @@ NextVersion: '添加 MainClass If Version.JsonObject("mainClass") Is Nothing Then - Throw New Exception("版本 json 中没有 mainClass 项!") + Throw New Exception("版本 Json 中没有 mainClass 项!") Else Result += " " & Version.JsonObject("mainClass").ToString End If diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb index 2d87c220..3b43e9b7 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb @@ -290,7 +290,7 @@ Public Module ModMinecraft Public ReadOnly Property Modable As Boolean Get If Not IsLoaded Then Load() - Return Version.HasFabric OrElse Version.HasForge OrElse Version.HasLiteLoader OrElse Version.HasNeoForge OrElse + Return Version.HasFabric OrElse Version.HasQuilt OrElse Version.HasForge OrElse Version.HasLiteLoader OrElse Version.HasNeoForge OrElse DisplayType = McVersionCardType.API '#223 End Get End Property @@ -381,19 +381,18 @@ Public Module ModMinecraft _Version.McName = Regex GoTo VersionSearchFinish End If - '从 Fabric 版本中获取版本号 + '从 Fabric / Quilt 版本中获取版本号 Regex = RegexSeek(LibrariesString, "(?<=((fabricmc)|(quiltmc)):intermediary:)[^""]*") If Regex IsNot Nothing Then _Version.McName = Regex GoTo VersionSearchFinish End If - 'FUTURE: [Quilt 支持] 从 Quilt 版本中获取版本号 - '从 jar 项中获取版本号 + '从 Jar 项中获取版本号 If JsonObject("jar") IsNot Nothing Then _Version.McName = JsonObject("jar").ToString GoTo VersionSearchFinish End If - '从 jar 文件的 version.json 中获取版本号 + '从 Jar 文件的 version.json 中获取版本号 If File.Exists(Path & Name & ".jar") Then Try Using JarArchive As New ZipArchive(New FileStream(Path & Name & ".jar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) @@ -405,7 +404,7 @@ Public Module ModMinecraft Dim VersionId As String = VersionJsonObj("id").ToString If VersionId.Length < 32 Then '因为 wiki 说这玩意儿可能是个 hash,虽然我没发现 _Version.McName = VersionId - Log("[Minecraft] 从版本 jar 中的 version.json 获取到版本号:" & VersionId) + Log("[Minecraft] 从版本 Jar 中的 version.json 获取到版本号:" & VersionId) GoTo VersionSearchFinish End If End If @@ -413,7 +412,7 @@ Public Module ModMinecraft End If End Using Catch ex As Exception - Log(ex, "从版本 jar 中的 version.json 获取版本号失败") + Log(ex, "从版本 Jar 中的 version.json 获取版本号失败") End Try End If '非准确的版本判断警告 @@ -728,12 +727,15 @@ Recheck: State = McVersionState.LiteLoader Version.HasLiteLoader = True End If - 'Fabric、Forge - 'FUTURE: [Quilt 支持] 确认这里的玩意儿对不对 - If RealJson.Contains("net.fabricmc:fabric-loader") OrElse RealJson.Contains("org.quiltmc:quilt-loader") Then + 'Fabric、Forge、Quilt + If RealJson.Contains("net.fabricmc:fabric-loader") Then State = McVersionState.Fabric Version.HasFabric = True - Version.FabricVersion = If(RegexSeek(RealJson, "(?<=(net.fabricmc:fabric-loader:)|(org.quiltmc:quilt-loader:))[0-9\.]+(\+build.[0-9]+)?"), "未知版本").Replace("+build", "") + Version.FabricVersion = If(RegexSeek(RealJson, "(?<=(net.fabricmc:fabric-loader:))[0-9\.]+(\+build.[0-9]+)?"), "未知版本").Replace("+build", "") + ElseIf RealJson.Contains("org.quiltmc:quilt-loader") Then + State = McVersionState.Quilt + Version.HasQuilt = True + Version.QuiltVersion = If(RegexSeek(RealJson, "(?<=(org.quiltmc:quilt-loader:))[0-9\.]+(\+build.[0-9]+)?"), "未知版本").Replace("+build", "") ElseIf RealJson.Contains("minecraftforge") AndAlso Not RealJson.Contains("net.neoforge") Then State = McVersionState.Forge Version.HasForge = True @@ -767,6 +769,8 @@ ExitDataLoad: Logo = PathImage & "Blocks/NeoForge.png" Case McVersionState.Fabric Logo = PathImage & "Blocks/Fabric.png" + Case McVersionState.Quilt + Logo = PathImage & "Blocks/Quilt.png" Case McVersionState.OptiFine Logo = PathImage & "Blocks/GrassPath.png" Case McVersionState.LiteLoader @@ -793,7 +797,7 @@ ExitDataLoad: End If Case McVersionState.Old Info = "远古版本" - Case McVersionState.Original, McVersionState.Forge, McVersionState.NeoForge, McVersionState.Fabric, McVersionState.OptiFine, McVersionState.LiteLoader + Case McVersionState.Original, McVersionState.Forge, McVersionState.NeoForge, McVersionState.Fabric, McVersionState.Quilt, McVersionState.OptiFine, McVersionState.LiteLoader Info = Version.ToString Case McVersionState.Fool Info = GetMcFoolName(Version.McName) @@ -823,6 +827,7 @@ ExitDataLoad: If State <> McVersionState.Error Then WriteIni(Path & "PCL\Setup.ini", "ReleaseTime", ReleaseTime.ToString("yyyy'-'MM'-'dd HH':'mm")) WriteIni(Path & "PCL\Setup.ini", "VersionFabric", Version.FabricVersion) + WriteIni(Path & "PCL\Setup.ini", "VersionQuilt", Version.QuiltVersion) WriteIni(Path & "PCL\Setup.ini", "VersionOptiFine", Version.OptiFineVersion) WriteIni(Path & "PCL\Setup.ini", "VersionLiteLoader", Version.HasLiteLoader) WriteIni(Path & "PCL\Setup.ini", "VersionForge", Version.ForgeVersion) @@ -869,6 +874,7 @@ ExitDataLoad: NeoForge LiteLoader Fabric + Quilt End Enum ''' @@ -940,6 +946,17 @@ ExitDataLoad: ''' Public FabricVersion As String = "" + 'Quilt + + ''' + ''' 该版本是否安装了 Quilt。 + ''' + Public HasQuilt As Boolean = False + ''' + ''' Quilt 版本号,如 0.26.1-beta.1、0.26.0。 + ''' + Public QuiltVersion As String = "" + 'LiteLoader ''' @@ -957,6 +974,7 @@ ExitDataLoad: If HasForge Then ToString += ", Forge" & If(ForgeVersion = "未知版本", "", " " & ForgeVersion) If HasNeoForge Then ToString += ", NeoForge" & If(NeoForgeVersion = "未知版本", "", " " & NeoForgeVersion) If HasFabric Then ToString += ", Fabric" & If(FabricVersion = "未知版本", "", " " & FabricVersion) + If HasQuilt Then ToString += ", Quilt" & If(QuiltVersion = "未知版本", "", " " & QuiltVersion) If HasOptiFine Then ToString += ", OptiFine" & If(OptiFineVersion = "未知版本", "", " " & OptiFineVersion) If HasLiteLoader Then ToString += ", LiteLoader" If ToString = "" Then @@ -980,7 +998,15 @@ ExitDataLoad: If SubVersions.Length >= 3 Then _SortCode = Val(SubVersions(0)) * 10000 + Val(SubVersions(1)) * 100 + Val(SubVersions(2)) Else - Throw New Exception("无效的 Fabric 版本:" & ForgeVersion) + Throw New Exception("无效的 Fabric 版本:" & FabricVersion) + End If + ElseIf HasQuilt Then + If QuiltVersion = "未知版本" Then Return 0 + Dim SubVersions = QuiltVersion.Split(".") + If SubVersions.Length >= 3 Then + _SortCode = Val(SubVersions(0)) * 10000 + Val(SubVersions(1)) * 100 + Val(SubVersions(2)) + Else + Throw New Exception("无效的 Quilt 版本:" & QuiltVersion) End If ElseIf HasForge OrElse HasNeoForge Then If ForgeVersion = "未知版本" AndAlso NeoForgeVersion = "未知版本" Then Return 0 @@ -1201,6 +1227,7 @@ OnLoaded: ReadIni(Version.Path & "PCL\Setup.ini", "VersionOriginal", "Unknown") <> "Unknown" Then '旧版本可能没有这一项,导致 Version 不加载(#643) Dim VersionInfo As New McVersionInfo With { .FabricVersion = ReadIni(Version.Path & "PCL\Setup.ini", "VersionFabric", ""), + .QuiltVersion = ReadIni(Version.Path & "PCL\Setup.ini", "VersionQuilt", ""), .ForgeVersion = ReadIni(Version.Path & "PCL\Setup.ini", "VersionForge", ""), .NeoForgeVersion = ReadIni(Version.Path & "PCL\Setup.ini", "VersionNeoForge", ""), .OptiFineVersion = ReadIni(Version.Path & "PCL\Setup.ini", "VersionOptiFine", ""), @@ -1212,6 +1239,7 @@ OnLoaded: .IsApiLoaded = True } VersionInfo.HasFabric = VersionInfo.FabricVersion.Any() + VersionInfo.HasQuilt = VersionInfo.QuiltVersion.Any() VersionInfo.HasForge = VersionInfo.ForgeVersion.Any() VersionInfo.HasNeoForge = VersionInfo.NeoForgeVersion.Any() VersionInfo.HasOptiFine = VersionInfo.OptiFineVersion.Any() @@ -1304,7 +1332,7 @@ OnLoaded: McVersionFilter(VersionList, VersionListOriginal, {McVersionState.Fool}, McVersionCardType.Fool) '筛选 API 版本 - McVersionFilter(VersionList, VersionListOriginal, {McVersionState.Forge, McVersionState.NeoForge, McVersionState.LiteLoader, McVersionState.Fabric}, McVersionCardType.API) + McVersionFilter(VersionList, VersionListOriginal, {McVersionState.Forge, McVersionState.NeoForge, McVersionState.LiteLoader, McVersionState.Fabric, McVersionState.Quilt}, McVersionCardType.API) '将老版本预先分类入不常用,只剩余原版、快照、OptiFine Dim VersionUseful As New List(Of McVersion) @@ -1438,7 +1466,7 @@ OnLoaded: End Function) End If - 'API 版本:优先按版本排序,此后【先放 Fabric,再放 Neo/Forge(按版本号从高到低排序),最后放 LiteLoader(按名称排序)】 + 'API 版本:优先按版本排序,此后【先放 Fabric / Quilt,再放 Neo/Forge(按版本号从高到低排序),最后放 LiteLoader(按名称排序)】 If ResultVersionList.ContainsKey(McVersionCardType.API) Then ResultVersionList(McVersionCardType.API) = Sort(ResultVersionList(McVersionCardType.API), Function(Left As McVersion, Right As McVersion) @@ -1448,6 +1476,8 @@ OnLoaded: Else If Left.Version.HasFabric Xor Right.Version.HasFabric Then Return Left.Version.HasFabric + ElseIf Left.Version.HasQuilt Xor Right.Version.HasQuilt Then + Return Left.Version.HasQuilt ElseIf Left.Version.HasNeoForge Xor Right.Version.HasNeoForge Then Return Left.Version.HasNeoForge ElseIf Left.Version.HasForge Xor Right.Version.HasForge Then @@ -1655,7 +1685,7 @@ OnLoaded: #End Region -#Region "支持库文件(Library)" +#Region "支持库文件(Libraries)" Public Class McLibToken ''' @@ -1797,7 +1827,7 @@ OnLoaded: '不能调用 RealVersion.Check(),可能会莫名其妙地触发 CheckPermission 正被另一进程使用,导致误判前置不存在 If Not File.Exists(RealVersion.Path & RealVersion.Name & ".json") Then RealVersion = Version - Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 json 文件", LogLevel.Debug) + Log("[Minecraft] 可能缺少前置版本 " & RealVersion.Name & ",找不到对应的 Json 文件", LogLevel.Debug) End If '获取详细下载信息 If RealVersion.JsonObject("downloads") IsNot Nothing AndAlso RealVersion.JsonObject("downloads")("client") IsNot Nothing Then @@ -1948,7 +1978,7 @@ OnLoaded: Dim MainJar As NetFile = DlClientJarGet(Version, True) If MainJar IsNot Nothing Then Result.Add(MainJar) Catch ex As Exception - Log(ex, "版本缺失主 jar 文件所必须的信息", LogLevel.Developer) + Log(ex, "版本缺失主 Jar 文件所必须的信息", LogLevel.Developer) End Try 'Library 文件 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb index f1cb1699..c8c671ce 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb @@ -304,7 +304,7 @@ Public Module ModMod End Sub ''' - ''' 从 jar 文件中获取 Mod 信息。 + ''' 从 Jar 文件中获取 Mod 信息。 ''' Private Sub LookupMetadata(Jar As ZipArchive) diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb index 487e0e6f..2d9bc586 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb @@ -183,6 +183,7 @@ Retry: Dim ForgeVersion As String = Nothing Dim NeoForgeVersion As String = Nothing Dim FabricVersion As String = Nothing + Dim QuiltVersion As String = Nothing For Each Entry In If(Json("minecraft")("modLoaders"), {}) Dim Id As String = If(Entry("id"), "").ToString.ToLower If Id.StartsWithF("forge-") Then @@ -196,10 +197,22 @@ Retry: NeoForgeVersion = Id.Replace("neoforge-", "") ElseIf Id.StartsWithF("fabric-") Then 'Fabric 指定 - Log("[ModPack] 整合包 Fabric 版本:" & Id) - FabricVersion = Id.Replace("fabric-", "") - Else - Log("[ModPack] 未知 Mod 加载器:" & Id) + Try + Log("[ModPack] 整合包 Fabric 版本:" & Id) + FabricVersion = Id.Replace("fabric-", "") + Exit For + Catch ex As Exception + Log(ex, "读取整合包 Fabric 版本失败:" & Id) + End Try + ElseIf Id.StartsWithF("quilt-") Then + 'Quilt 指定 + Try + Log("[ModPack] 整合包 Quilt 版本:" & Id) + QuiltVersion = Id.Replace("quilt-", "") + Exit For + Catch ex As Exception + Log(ex, "读取整合包 Quilt 版本失败:" & Id) + End Try End If Next '解压与配置文件 @@ -217,7 +230,7 @@ Retry: CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35) Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}") Else - Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}") + Log($"[ModPack] 整合包中未找到 overrides 文件夹:{OverridePath}") End If Task.Progress = 0.95 '开启版本隔离 @@ -300,7 +313,8 @@ Retry: .MinecraftName = Json("minecraft")("version").ToString, .ForgeVersion = ForgeVersion, .NeoForgeVersion = NeoForgeVersion, - .FabricVersion = FabricVersion + .FabricVersion = FabricVersion, + .QuiltVersion = QuiltVersion } Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True) '构造 Libraries 加载器 @@ -363,6 +377,7 @@ Retry: Dim ForgeVersion As String = Nothing Dim NeoForgeVersion As String = Nothing Dim FabricVersion As String = Nothing + Dim QuiltVersion As String = Nothing For Each Entry As JProperty In If(Json("dependencies"), {}) Select Case Entry.Name.ToLower Case "minecraft" @@ -376,12 +391,11 @@ Retry: Case "fabric-loader" 'eg. 0.14.14 FabricVersion = Entry.Value.ToString Log("[ModPack] 整合包 Fabric 版本:" & FabricVersion) - Case "quilt-loader" 'eg. 1.0.0 - Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical) - Throw New CancelledException + Case "quilt-loader" 'eg. 0.26.0 + QuiltVersion = Entry.Value.ToString + Log("[ModPack] 整合包 Quilt 版本:" & QuiltVersion) Case Else - Hint($"无法安装整合包,其中出现了未知的 Mod 加载器 {Entry.Value}!", HintType.Critical) - Throw New CancelledException + Hint($"无法安装整合包,其中出现了未知的 Mod 加载器 {Entry.Name}(版本为 {Entry.Value.ToString})!", HintType.Critical) End Select Next '获取版本名 @@ -448,7 +462,8 @@ Retry: .MinecraftName = MinecraftVersion, .ForgeVersion = ForgeVersion, .NeoForgeVersion = NeoForgeVersion, - .FabricVersion = FabricVersion + .FabricVersion = FabricVersion, + .QuiltVersion = QuiltVersion } Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True) '构造 Libraries 加载器 @@ -521,7 +536,7 @@ Retry: If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then CopyDirectory(InstallTemp & ArchiveBaseFolder & "minecraft", PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35) Else - Log("[ModPack] 整合包中未找到 minecraft override 目录,已跳过") + Log("[ModPack] 整合包中未找到 minecraft overrides 目录,已跳过") End If Task.Progress = 0.95 '开启版本隔离 @@ -605,7 +620,7 @@ Retry: If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then CopyDirectory(InstallTemp & ArchiveBaseFolder & ".minecraft", PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35) Else - Log("[ModPack] 整合包中未找到 override .minecraft 目录,已跳过") + Log("[ModPack] 整合包中未找到 overrides .minecraft 目录,已跳过") End If Task.Progress = 0.95 '开启版本隔离 @@ -664,9 +679,8 @@ Retry: Request.NeoForgeVersion = Component("version") Case "net.fabricmc.fabric-loader" Request.FabricVersion = Component("version") - Case "org.quiltmc.quilt-loader" 'eg. 1.0.0 - Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical) - Throw New CancelledException + Case "org.quiltmc.quilt-loader" + Request.QuiltVersion = Component("version") End Select Next '构造加载器 @@ -740,10 +754,9 @@ Retry: For Each Entry In Json("addons") Addons.Add(Entry("id"), Entry("version")) Next - If Not Addons.ContainsKey("game") Then Throw New Exception("该 MCBBS 整合包未提供游戏版本信息,无法安装!") - If Addons.ContainsKey("quilt") Then - Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical) - Throw New CancelledException + If Not Addons.ContainsKey("game") Then + Hint("该整合包未提供游戏版本信息,无法安装!", HintType.Critical) + Return Nothing End If Dim Request As New McInstallRequest With { .TargetVersionName = VersionName, @@ -752,7 +765,8 @@ Retry: .OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing), .ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing), .NeoForgeVersion = If(Addons.ContainsKey("neoforge"), Addons("neoforge"), Nothing), - .FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing) + .FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing), + .QuiltVersion = If(Addons.ContainsKey("quilt"), Addons("quilt"), Nothing) } Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True) '构造 Libraries 加载器 diff --git a/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb b/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb index d3bcaf43..289c8111 100644 --- a/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb +++ b/Plain Craft Launcher 2/Modules/Minecraft/MyCompItem.xaml.vb @@ -108,6 +108,9 @@ If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then TargetVersion = FrmMain.PageCurrent.Additional(2) TargetLoader = FrmMain.PageCurrent.Additional(3) + ElseIf FrmMain.PageCurrent.Page = FormMain.PageType.Download AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.DownloadCompFavorites Then + TargetVersion = "" + TargetLoader = CompModLoaderType.Any Else Select Case CType(sender.Tag, CompProject).Type Case CompType.Mod @@ -115,6 +118,8 @@ TargetLoader = PageDownloadMod.Loader.Input.ModLoader Case CompType.ModPack TargetVersion = If(PageDownloadPack.Loader.Input.GameVersion, "") + Case CompType.Shader + TargetVersion = If(PageDownloadShader.Loader.Input.GameVersion, "") Case Else 'CompType.ResourcePack 'FUTURE: Res TargetVersion = "" 'If(PageDownloadResource.Loader.Input.GameVersion, "") diff --git a/Plain Craft Launcher 2/Modules/ModMain.vb b/Plain Craft Launcher 2/Modules/ModMain.vb index 5c80d2a6..b5b6bbe0 100644 --- a/Plain Craft Launcher 2/Modules/ModMain.vb +++ b/Plain Craft Launcher 2/Modules/ModMain.vb @@ -181,6 +181,9 @@ EndHint: ''' 登录模式:登录步骤 1 中返回的 JSON。 ''' Public Content As Object + + '设置轮询 Url + Public AuthUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token" ''' ''' 输入模式:输入验证规则。 ''' @@ -403,8 +406,12 @@ EndHint: Public FrmDownloadForge As PageDownloadForge Public FrmDownloadNeoForge As PageDownloadNeoForge Public FrmDownloadFabric As PageDownloadFabric + Public FrmDownloadQuilt As PageDownloadQuilt Public FrmDownloadMod As PageDownloadMod Public FrmDownloadPack As PageDownloadPack + Public FrmDownloadResourcePack As PageDownloadResourcePack + Public FrmDownloadShader As PageDownloadShader + Public FrmDownloadCompFavorites As PageDownloadCompFavorites '设置页面声明 Public FrmSetupLeft As PageSetupLeft @@ -418,6 +425,8 @@ EndHint: Public FrmOtherHelp As PageOtherHelp Public FrmOtherAbout As PageOtherAbout Public FrmOtherTest As PageOtherTest + Public FrmOtherFeedback As PageOtherFeedback + Public FrmOtherVote As PageOtherVote '登录页面声明 Public FrmLoginLegacy As PageLoginLegacy @@ -620,7 +629,7 @@ EndHint: Log("[Help] 已扫描 PCL 文件夹下的帮助文件,目前总计 " & FileList.Count & " 条") '读取自带文件 For Each File In EnumerateFiles(PathTemp & "Help") - '跳过非 json 文件与以 . 开头的文件夹 + '跳过非 Json 文件与以 . 开头的文件夹 If File.Extension.ToLower <> ".json" OrElse File.Directory.FullName.Replace(PathTemp & "Help", "").Contains("\.") Then Continue For '检查忽略列表 Dim RealPath As String = File.FullName.Replace(PathTemp & "Help\", "") diff --git a/Plain Craft Launcher 2/Modules/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb index 52fd4d23..f4e80892 100644 --- a/Plain Craft Launcher 2/Modules/ModSecret.vb +++ b/Plain Craft Launcher 2/Modules/ModSecret.vb @@ -1,19 +1,23 @@ '由于包含加解密等安全信息,本文件中的部分代码已被删除 +Imports System.ComponentModel Imports System.Net Imports System.Reflection Imports System.Security.Cryptography +Imports NAudio.Midi Friend Module ModSecret #Region "杂项" '在开源版的注册表与常规版的注册表隔离,以防数据冲突 - Public Const RegFolder As String = "PCLDebug" + Public Const RegFolder As String = "PCLCE" '用于微软登录的 ClientId Public Const OAuthClientId As String = "" 'CurseForge API Key Public Const CurseForgeAPIKey As String = "" + ' LittleSkin OAuth ClientId + Public Const LittleSkinClientId As String = "" Friend Sub SecretOnApplicationStart() '提升 UI 线程优先级 @@ -45,12 +49,13 @@ Friend Module ModSecret Environment.[Exit](Result.Cancel) End If '开源版本提示 - MyMsgBox($"该版本中无法使用以下特性: -- CurseForge API 调用:需要你自行申请 API Key,然后添加到 ModSecret.vb 的开头 -- 正版登录:需要你自行申请 Client ID,然后添加到 ModSecret.vb 的开头 -- 更新与联网通知:避免滥用隐患 -- 主题切换:这是需要赞助解锁的纪念性质的功能,别让赞助者太伤心啦…… -- 百宝箱:开发早期往里面塞了些开发工具,整理起来太麻烦了", "开源版本说明") + MyMsgBox($"你正在使用来自 PCL-Community 的 PCL2 社区版本,遇到问题请不要向官方仓库反馈! +PCL-Community 及其成员与龙腾猫跃无从属关系,且均不会为您的使用做担保。 + +该版本中暂时无法使用以下特性: +- 更新与联网通知:在做了在做了.jpg +- 主题切换:这是需要赞助解锁的纪念性质的功能,社区版不会制作 +- 百宝箱:主线分支没有提供相关内容", "社区版本说明", "我知道了") End Sub ''' @@ -268,13 +273,120 @@ Friend Module ModSecret Public IsUpdateStarted As Boolean = False Public IsUpdateWaitingRestart As Boolean = False Public Sub UpdateCheckByButton() - Hint("该版本中不包含更新功能……") + Hint("正在获取更新信息...") + If IsUpdateStarted Then + Exit Sub + End If + Dim LatestReleaseInfoJson As JObject = Nothing + Dim LatestVersion As String = Nothing + RunInNewThread(Sub() + Try + LatestReleaseInfoJson = GetJson(NetRequestRetry("https://api.github.com/repos/PCL-Community/PCL2-CE/releases/latest", "GET", "", "application/x-www-form-urlencoded")) + LatestVersion = LatestReleaseInfoJson("tag_name").ToString + If Not LatestVersion = VersionBaseName Then + If MyMsgBox("发现了启动器更新,是否更新?", "启动器更新", "更新", "取消") = 1 Then + UpdateStart(LatestVersion, False) + End If + Else + Hint("启动器已是最新版 " + VersionBaseName + ",无须更新啦!", HintType.Finish) + End If + Catch ex As Exception + Log(ex, "[Update] 获取启动器更新信息失败", LogLevel.Hint) + Hint("获取启动器更新信息失败,请检查网络连接", HintType.Critical) + End Try + End Sub) End Sub - Public Sub UpdateStart(BaseUrl As String, Slient As Boolean, Optional ReceivedKey As String = Nothing, Optional ForceValidated As Boolean = False) + Public Sub UpdateStart(VersionStr As String, Slient As Boolean, Optional ReceivedKey As String = Nothing, Optional ForceValidated As Boolean = False) + Dim DlLink As String = "https://github.com/PCL-Community/PCL2-CE/releases/download/" + VersionStr + "/PCL2_CE.exe" + Dim DlTargetPath As String = Path + "PCL\Plain Craft Launcher 2.exe" + RunInNewThread(Sub() + Try + '构造步骤加载器 + Dim Loaders As New List(Of LoaderBase) + '下载 + Dim Address As New List(Of String) + Address.Add(DlLink) + Loaders.Add(New LoaderDownload("下载更新文件", New List(Of NetFile) From {New NetFile(Address.ToArray, DlTargetPath, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15}) + Loaders.Add(New LoaderTask(Of Integer, Integer)("安装更新", Sub() UpdateRestart(True))) + '启动 + Dim Loader As New LoaderCombo(Of JObject)("启动器更新", Loaders) + Loader.Start() + LoaderTaskbarAdd(Loader) + FrmMain.BtnExtraDownload.ShowRefresh() + FrmMain.BtnExtraDownload.Ribble() + Catch ex As Exception + Log(ex, "[Update] 下载启动器更新文件失败", LogLevel.Hint) + Hint("下载启动器更新文件失败,请检查网络连接", HintType.Critical) + End Try + End Sub) End Sub Public Sub UpdateRestart(TriggerRestartAndByEnd As Boolean) + IsUpdateWaitingRestart = True + Try + Dim fileName As String = Path + "PCL\Plain Craft Launcher 2.exe" + Dim text As String = String.Concat(New String() {"--update ", Process.GetCurrentProcess().Id, " """, AppDomain.CurrentDomain.SetupInformation.ApplicationName, """ """, AppDomain.CurrentDomain.SetupInformation.ApplicationName, """ ", TriggerRestartAndByEnd}) + Log("[System] 更新程序启动,参数:" + text, LogLevel.Normal, "出现错误") + Process.Start(New ProcessStartInfo(fileName) With {.WindowStyle = ProcessWindowStyle.Hidden, .CreateNoWindow = True, .Arguments = text}) + If TriggerRestartAndByEnd Then + FrmMain.EndProgram(False) + Log("[System] 已由于更新强制结束程序", LogLevel.Normal, "出现错误") + End If + Catch ex As Win32Exception + Log(ex, "自动更新时触发 Win32 错误,疑似被拦截", LogLevel.Debug, "出现错误") + If MyMsgBox(String.Format("由于被 Windows 安全中心拦截,或者存在权限问题,导致 PCL 无法更新。{0}请将 PCL 所在文件夹加入白名单,或者手动用 {1}PCL\Plain Craft Launcher 2.exe 替换当前文件!", vbCrLf, ModBase.Path), "更新失败", "查看帮助", "确定", "", True, True, False, Nothing, Nothing, Nothing) = 1 Then + TryStartEvent("打开帮助", "启动器/Microsoft Defender 添加排除项.json") + End If + End Try End Sub Public Sub UpdateReplace(ProcessId As Integer, OldFileName As String, NewFileName As String, TriggerRestart As Boolean) + Try + Process.GetProcessById(ProcessId).Kill() + Catch ex As Exception + End Try + Dim OriginalPath As String = Strings.Mid(Path, 1, Path.Length - 4) + GetFileNameFromPath(OldFileName) + Dim TempPath As String = Strings.Mid(Path, 1, Path.Length - 4) + GetFileNameFromPath(NewFileName) + Dim ex2 As Exception = Nothing + Dim num As Integer = 0 + Do + Try + If File.Exists(OriginalPath) Then + File.Delete(OriginalPath) + End If + If File.Exists(TempPath) Then + File.Delete(TempPath) + End If + If Not File.Exists(OriginalPath) AndAlso Not File.Exists(TempPath) Then + Exit Try + End If + Thread.Sleep(2000) + Catch ex3 As Exception + ex2 = ex3 + End Try + num += 1 + Loop While num <= 4 + If Not File.Exists(OriginalPath) AndAlso Not File.Exists(TempPath) Then + Try + CopyFile(OriginalPath, TempPath) + Catch ex4 As UnauthorizedAccessException + MsgBox("PCL 更新失败:权限不足。请手动复制 PCL 文件夹下的新版本程序。" & vbCrLf & "若 PCL 位于桌面或 C 盘,你可以尝试将其挪到其他文件夹,这可能可以解决权限问题。" & vbCrLf + GetExceptionSummary(ex4), MsgBoxStyle.Critical, "更新失败") + Catch ex5 As Exception + MsgBox("PCL 更新失败:无法复制新文件。请手动复制 PCL 文件夹下的新版本程序。" & vbCrLf + GetExceptionSummary(ex5), MsgBoxStyle.Critical, "更新失败") + Return + End Try + If TriggerRestart Then + Try + Process.Start(TempPath) + Catch ex6 As Exception + MsgBox("PCL 更新失败:无法重新启动。" & vbCrLf + GetExceptionSummary(ex6), MsgBoxStyle.Critical, "更新失败") + End Try + End If + Return + End If + If TypeOf ex2 Is UnauthorizedAccessException Then + MsgBox(String.Concat(New String() {"由于权限不足,PCL 无法完成更新。请尝试:" & vbCrLf, If((TempPath.StartsWithF(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), False) OrElse TempPath.StartsWithF(Environment.GetFolderPath(Environment.SpecialFolder.Personal), False)), " - 将 PCL 文件移动到桌面、文档以外的文件夹(这或许可以一劳永逸地解决权限问题)" & vbCrLf, ""), If(TempPath.StartsWithF("C", True), " - 将 PCL 文件移动到 C 盘以外的文件夹(这或许可以一劳永逸地解决权限问题)" & vbCrLf, ""), " - 右键以管理员身份运行 PCL" & vbCrLf & " - 手动复制已下载到 PCL 文件夹下的新版本程序,覆盖原程序" & vbCrLf & vbCrLf, GetExceptionSummary(ex2)}), MsgBoxStyle.Critical, "更新失败") + Return + End If + MsgBox("PCL 更新失败:无法删除原文件。请手动复制已下载到 PCL 文件夹下的新版本程序覆盖原程序。" & vbCrLf + GetExceptionSummary(ex2), MsgBoxStyle.Critical, "更新失败") End Sub #End Region diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb index 5f7dcd99..c5cbc7b0 100644 --- a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb +++ b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb @@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 ' 方法是按如下所示使用“*” - - + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb index ff7ec51f..078fb785 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb @@ -39,7 +39,7 @@ Public Module ModDownloadLib '已有版本检查 If Behaviour <> NetPreDownloadBehaviour.IgnoreCheck AndAlso File.Exists(VersionFolder & Id & ".json") AndAlso File.Exists(VersionFolder & Id & ".jar") Then If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return Nothing - If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 与 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 与 Jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & Id & ".jar") File.Delete(VersionFolder & Id & ".json") Else @@ -60,6 +60,47 @@ Public Module ModDownloadLib Return Nothing End Try End Function + ''' + ''' 保存某个 Minecraft 版本的核心文件(仅 Json 与核心 Jar)。 + ''' + ''' 所下载的 Minecraft 的版本名。 + ''' Json 文件的 Mojang 官方地址。 + Public Sub McDownloadClientCore(Id As String, JsonUrl As String, Behaviour As NetPreDownloadBehaviour) + Try + Dim VersionFolder As String = SelectFolder() + If Not VersionFolder.Contains("\") Then Exit Sub + VersionFolder = VersionFolder & Id & "\" + + '重复任务检查 + For Each OngoingLoader In LoaderTaskbar + If OngoingLoader.Name <> $"Minecraft {Id} 下载" Then Continue For + If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Exit Sub + Hint("该版本正在下载中!", HintType.Critical) + Exit Sub + Next + + Dim Loaders As New List(Of LoaderBase) + '下载版本 Json 文件 + Loaders.Add(New LoaderDownload("下载版本 Json 文件", New List(Of NetFile) From { + New NetFile(DlSourceLauncherOrMetaGet(JsonUrl), VersionFolder & Id & ".json", New FileChecker(CanUseExistsFile:=False, IsJson:=True)) + }) With {.ProgressWeight = 2}) + '获取支持库文件地址 + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析核心 Jar 文件下载地址", + Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 0.5, .Show = False}) + '下载支持库文件 + Loaders.Add(New LoaderDownload("下载核心 Jar 文件", New List(Of NetFile)) With {.ProgressWeight = 5}) + + '启动 + Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave} + Loader.Start(Id) + LoaderTaskbarAdd(Loader) + FrmMain.BtnExtraDownload.ShowRefresh() + FrmMain.BtnExtraDownload.Ribble() + + Catch ex As Exception + Log(ex, "开始 Minecraft 下载失败", LogLevel.Feedback) + End Try + End Sub ''' ''' 获取下载某个 Minecraft 版本的加载器列表。 @@ -73,7 +114,7 @@ Public Module ModDownloadLib '下载版本 Json 文件 If JsonUrl Is Nothing Then - Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取原版 json 文件下载地址", + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取原版 Json 文件下载地址", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Dim JsonAddress As String = DlClientListGet(Id) Task.Output = New List(Of NetFile) From {New NetFile(DlSourceLauncherOrMetaGet(JsonAddress), VersionFolder & VersionName & ".json")} @@ -124,7 +165,7 @@ Public Module ModDownloadLib End Function Private Const McDownloadClientLibName As String = "下载原版支持库文件" - Private Const McDownloadClientJsonName As String = "下载原版 json 文件" + Private Const McDownloadClientJsonName As String = "下载原版 Json 文件" #End Region @@ -391,6 +432,44 @@ pause" #Region "OptiFine 下载" + Public Sub McDownloadOptiFine(DownloadInfo As DlOptiFineListEntry) + Try + Dim Id As String = DownloadInfo.NameVersion + Dim VersionFolder As String = PathMcFolder & "versions\" & Id & "\" + Dim IsNewVersion As Boolean = Val(DownloadInfo.Inherit.Split(".")(1)) >= 14 + Dim Target As String = If(IsNewVersion, + PathTemp & "Cache\Code\" & DownloadInfo.NameVersion & "_" & GetUuid(), + PathMcFolder & "libraries\optifine\OptiFine\" & DownloadInfo.NameFile.Replace("OptiFine_", "").Replace(".jar", "").Replace("preview_", "") & "\" & DownloadInfo.NameFile.Replace("OptiFine_", "OptiFine-").Replace("preview_", "")) + + '重复任务检查 + For Each OngoingLoader In LoaderTaskbar + If OngoingLoader.Name <> $"OptiFine {DownloadInfo.NameDisplay} 下载" Then Continue For + Hint("该版本正在下载中!", HintType.Critical) + Exit Sub + Next + + '已有版本检查 + If File.Exists(VersionFolder & Id & ".json") Then + If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 和 Jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + File.Delete(VersionFolder & Id & ".jar") + File.Delete(VersionFolder & Id & ".json") + Else + Exit Sub + End If + End If + + '启动 + Dim Loader As New LoaderCombo(Of String)("OptiFine " & DownloadInfo.NameDisplay & " 下载", McDownloadOptiFineLoader(DownloadInfo)) With {.OnStateChanged = AddressOf McInstallState} + Loader.Start(VersionFolder) + LoaderTaskbarAdd(Loader) + FrmMain.BtnExtraDownload.ShowRefresh() + FrmMain.BtnExtraDownload.Ribble() + + Catch ex As Exception + Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback) + End Try + End Sub + Private Sub McDownloadOptiFineSave(DownloadInfo As DlOptiFineListEntry) Try Dim Id As String = DownloadInfo.NameVersion @@ -835,7 +914,7 @@ Retry: '已有版本检查 If File.Exists(VersionFolder & VersionName & ".json") Then - If MyMsgBox("版本 " & VersionName & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then + If MyMsgBox("版本 " & VersionName & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 Json 和 Jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then File.Delete(VersionFolder & VersionName & ".jar") File.Delete(VersionFolder & VersionName & ".json") Else @@ -1453,10 +1532,10 @@ Retry: End If If DeltaList.Count = 1 Then '如果没有新增文件夹,那么预测的文件夹名就是正确的 - '如果只新增 1 个文件夹,那么拷贝 json 文件 + '如果只新增 1 个文件夹,那么拷贝 Json 文件 Dim JsonFile As FileInfo = DeltaList(0).EnumerateFiles.First() WriteFile(VersionFolder & TargetVersion & ".json", ReadFile(JsonFile.FullName)) - Log($"[Download] 已拷贝新增的版本 JSON 文件:{JsonFile.FullName} -> {VersionFolder}{TargetVersion}.json") + Log($"[Download] 已拷贝新增的版本 Json 文件:{JsonFile.FullName} -> {VersionFolder}{TargetVersion}.json") ElseIf DeltaList.Count > 1 Then '新增了多个文件夹 Log($"[Download] 有多个疑似的新增版本,无法确定:{DeltaList.Select(Function(d) d.Name).Join(";")}") @@ -1897,6 +1976,111 @@ Retry: #End Region +#Region "Quilt 下载" + + Public Sub McDownloadQuiltLoaderSave(DownloadInfo As JObject) + Try + Dim Url As String = DownloadInfo("url").ToString + Dim FileName As String = GetFileNameFromPath(Url) + Dim Version As String = GetFileNameFromPath(DownloadInfo("version").ToString) + Dim Target As String = SelectAs("选择保存位置", FileName, "Quilt 安装器 (*.jar)|*.jar") + If Not Target.Contains("\") Then Exit Sub + + '重复任务检查 + For Each OngoingLoader In LoaderTaskbar + If OngoingLoader.Name <> $"Quilt {Version} 安装器下载" Then Continue For + Hint("该版本正在下载中!", HintType.Critical) + Exit Sub + Next + + '构造步骤加载器 + Dim Loaders As New List(Of LoaderBase) + '下载 + 'TODO: BMCLAPI 不支持 Quilt Installer 下载 + Dim Address As New List(Of String) + Address.Add(Url) + Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15}) + '启动 + Dim Loader As New LoaderCombo(Of JObject)("Quilt " & Version & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave} + Loader.Start(DownloadInfo) + LoaderTaskbarAdd(Loader) + FrmMain.BtnExtraDownload.ShowRefresh() + FrmMain.BtnExtraDownload.Ribble() + + Catch ex As Exception + Log(ex, "开始 Quilt 安装器下载失败", LogLevel.Feedback) + End Try + End Sub + + ''' + ''' 获取下载某个 Quilt 版本的加载器列表。 + ''' + Private Function McDownloadQuiltLoader(QuiltVersion As String, MinecraftName As String, Optional McFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase) + + '参数初始化 + McFolder = If(McFolder, PathMcFolder) + Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder + Dim Id As String = "quilt-loader-" & QuiltVersion & "-" & MinecraftName + Dim VersionFolder As String = McFolder & "versions\" & Id & "\" + Dim Loaders As New List(Of LoaderBase) + + '下载 Json + MinecraftName = MinecraftName.Replace("∞", "infinite") '放在 ID 后面避免影响版本文件夹名称 + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 Quilt 主文件下载地址", + Sub(Task As LoaderTask(Of String, List(Of NetFile))) + '启动依赖版本的下载 + If FixLibrary Then + McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, MinecraftName) + End If + Task.Progress = 0.5 + '构造文件请求 + Task.Output = New List(Of NetFile) From {New NetFile({ + "https://meta.quiltmc.org/v3/versions/loader/" & MinecraftName & "/" & QuiltVersion & "/profile/json" + }, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))} + '新建 mods 文件夹 + Directory.CreateDirectory(New McVersion(VersionFolder).GetPathIndie(True) & "mods\") + End Sub) With {.ProgressWeight = 0.5}) + Loaders.Add(New LoaderDownload("下载 Quilt 主文件", New List(Of NetFile)) With {.ProgressWeight = 2.5}) + + '下载支持库 + If FixLibrary Then + Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 Quilt 支持库文件", + Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False}) + Loaders.Add(New LoaderDownload("下载 Quilt 支持库文件", New List(Of NetFile)) With {.ProgressWeight = 8}) + End If + + Return Loaders + End Function + +#End Region + +#Region "Quilt 下载菜单" + + Public Function QuiltDownloadListItem(Entry As JObject, OnClick As MyListItem.ClickEventHandler) As MyListItem + '建立控件 + Dim NewItem As New MyListItem With { + .Title = Entry("version").ToString, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, + .Info = If(Entry("maven").ToString.Contains("installer"), "安装器", If(Entry("version").ToString.Contains("beta") OrElse Entry("version").ToString.Contains("pre"), "测试版", "稳定版")), + .Logo = PathImage & "Blocks/Quilt.png" + } + AddHandler NewItem.Click, OnClick + '结束 + Return NewItem + End Function + Public Function QSLDownloadListItem(Entry As CompFile, OnClick As MyListItem.ClickEventHandler) As MyListItem + '建立控件 + Dim NewItem As New MyListItem With { + .Title = Entry.DisplayName.Split("]")(1).Replace(" build ", ".").Split("+")(0).Trim, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, + .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy'/'MM'/'dd HH':'mm"), + .Logo = PathImage & "Blocks/Quilt.png" + } + AddHandler NewItem.Click, OnClick + '结束 + Return NewItem + End Function + +#End Region + #Region "合并安装" ''' @@ -1962,6 +2146,16 @@ Retry: ''' Public FabricApi As CompFile = Nothing + ''' + ''' 欲下载的 Quilt Loader 版本名。 + ''' + Public QuiltVersion As String = Nothing + + ''' + ''' 欲下载的 Quilted Fabric API (QFAPI) / Quilt Standard Libraries (QSL) 信息。 + ''' + Public QSL As CompFile = Nothing + ''' ''' 欲下载的 OptiFabric 信息。 ''' @@ -2095,6 +2289,8 @@ Retry: If Request.NeoForgeVersion IsNot Nothing Then NeoForgeFolder = TempMcFolder & "versions\neoforge-" & Request.NeoForgeVersion Dim FabricFolder As String = Nothing If Request.FabricVersion IsNot Nothing Then FabricFolder = TempMcFolder & "versions\fabric-loader-" & Request.FabricVersion & "-" & Request.MinecraftName + Dim QuiltFolder As String = Nothing + If Request.QuiltVersion IsNot Nothing Then QuiltFolder = TempMcFolder & "versions\quilt-loader-" & Request.QuiltVersion & "-" & Request.MinecraftName Dim LiteLoaderFolder As String = Nothing If Request.LiteLoaderEntry IsNot Nothing Then LiteLoaderFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-LiteLoader" @@ -2115,6 +2311,7 @@ Retry: If ForgeFolder IsNot Nothing Then Log("[Download] Forge 缓存:" & ForgeFolder) If NeoForgeFolder IsNot Nothing Then Log("[Download] NeoForge 缓存:" & NeoForgeFolder) If FabricFolder IsNot Nothing Then Log("[Download] Fabric 缓存:" & FabricFolder) + If QuiltFolder IsNot Nothing Then Log("[Download] Quilt 缓存:" & QuiltFolder) If LiteLoaderFolder IsNot Nothing Then Log("[Download] LiteLoader 缓存:" & LiteLoaderFolder) Log("[Download] 对应的原版版本:" & Request.MinecraftName) @@ -2131,6 +2328,10 @@ Retry: If Request.FabricApi IsNot Nothing Then LoaderList.Add(New LoaderDownload("下载 Fabric API", New List(Of NetFile) From {Request.FabricApi.ToNetFile(ModsFolder)}) With {.ProgressWeight = 3, .Block = False}) End If + 'Quilted Fabric API (QFAPI) / Quilt Standard Libraries (QSL) + If Request.QSL IsNot Nothing Then + LoaderList.Add(New LoaderDownload("下载 QFAPI / QSL", New List(Of NetFile) From {Request.QSL.ToNetFile(New McVersion(OutputFolder).GetPathIndie(True) & "mods\")}) With {.ProgressWeight = 3, .Block = False}) + End If 'OptiFabric If Request.OptiFabric IsNot Nothing Then LoaderList.Add(New LoaderDownload("下载 OptiFabric", New List(Of NetFile) From {Request.OptiFabric.ToNetFile(ModsFolder)}) With {.ProgressWeight = 3, .Block = False}) @@ -2162,10 +2363,14 @@ Retry: If Request.FabricVersion IsNot Nothing Then LoaderList.Add(New LoaderCombo(Of String)("下载 Fabric " & Request.FabricVersion, McDownloadFabricLoader(Request.FabricVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2, .Block = True}) End If + 'Quilt + If Request.QuiltVersion IsNot Nothing Then + LoaderList.Add(New LoaderCombo(Of String)("下载 Quilt " & Request.QuiltVersion, McDownloadQuiltLoader(Request.QuiltVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2, .Block = True}) + End If '合并安装 LoaderList.Add(New LoaderTask(Of String, String)("安装游戏", Sub(Task As LoaderTask(Of String, String)) - InstallMerge(OutputFolder, OutputFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, NeoForgeFolder, Request.NeoForgeVersion, FabricFolder, LiteLoaderFolder) + InstallMerge(OutputFolder, OutputFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, NeoForgeFolder, Request.NeoForgeVersion, FabricFolder, QuiltFolder, LiteLoaderFolder) Task.Progress = 0.3 If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries") If Directory.Exists(TempMcFolder & "mods") Then CopyDirectory(TempMcFolder & "mods", ModsFolder) @@ -2177,7 +2382,7 @@ Retry: End Sub) With {.ProgressWeight = 2, .Block = True}) '补全文件 If Not DontFixLibraries AndAlso - (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.BeforeFirst(".") >= 20) OrElse Request.NeoForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then + (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.Split(".")(0) >= 20) OrElse Request.NeoForgeVersion IsNot Nothing OrElse Request.FabricVersion IsNot Nothing OrElse Request.QuiltVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then Dim LoadersLib As New List(Of LoaderBase) LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(OutputFolder))) With {.ProgressWeight = 1, .Show = False}) LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False}) @@ -2193,18 +2398,19 @@ Retry: ''' ''' 将多个版本 Json 进行合并,如果目标已存在则直接覆盖。失败会抛出异常。 ''' - Private Sub InstallMerge(OutputFolder As String, MinecraftFolder As String, Optional OptiFineFolder As String = Nothing, Optional OptiFineAsMod As Boolean = False, Optional ForgeFolder As String = Nothing, Optional ForgeVersion As String = Nothing, Optional NeoForgeFolder As String = Nothing, Optional NeoForgeVersion As String = Nothing, Optional FabricFolder As String = Nothing, Optional LiteLoaderFolder As String = Nothing) + Private Sub InstallMerge(OutputFolder As String, MinecraftFolder As String, Optional OptiFineFolder As String = Nothing, Optional OptiFineAsMod As Boolean = False, Optional ForgeFolder As String = Nothing, Optional ForgeVersion As String = Nothing, Optional NeoForgeFolder As String = Nothing, Optional NeoForgeVersion As String = Nothing, Optional FabricFolder As String = Nothing, Optional QuiltFolder As String = Nothing, Optional LiteLoaderFolder As String = Nothing) Log("[Download] 开始进行版本合并,输出:" & OutputFolder & ",Minecraft:" & MinecraftFolder & If(OptiFineFolder IsNot Nothing, ",OptiFine:" & OptiFineFolder, "") & If(ForgeFolder IsNot Nothing, ",Forge:" & ForgeFolder, "") & If(NeoForgeFolder IsNot Nothing, ",NeoForge:" & NeoForgeFolder, "") & If(LiteLoaderFolder IsNot Nothing, ",LiteLoader:" & LiteLoaderFolder, "") & - If(FabricFolder IsNot Nothing, ",Fabric:" & FabricFolder, "")) + If(FabricFolder IsNot Nothing, ",Fabric:" & FabricFolder, "") & + If(QuiltFolder IsNot Nothing, ",Quilt:" & QuiltFolder, "")) Directory.CreateDirectory(OutputFolder) - Dim HasOptiFine As Boolean = OptiFineFolder IsNot Nothing AndAlso Not OptiFineAsMod, HasForge As Boolean = ForgeFolder IsNot Nothing, HasNeoForge As Boolean = NeoForgeFolder IsNot Nothing, HasLiteLoader As Boolean = LiteLoaderFolder IsNot Nothing, HasFabric As Boolean = FabricFolder IsNot Nothing - Dim OutputName As String, MinecraftName As String, OptiFineName As String, ForgeName As String, NeoForgeName As String, LiteLoaderName As String, FabricName As String - Dim OutputJsonPath As String, MinecraftJsonPath As String, OptiFineJsonPath As String = Nothing, ForgeJsonPath As String = Nothing, NeoForgeJsonPath As String = Nothing, LiteLoaderJsonPath As String = Nothing, FabricJsonPath As String = Nothing + Dim HasOptiFine As Boolean = OptiFineFolder IsNot Nothing AndAlso Not OptiFineAsMod, HasForge As Boolean = ForgeFolder IsNot Nothing, HasNeoForge As Boolean = NeoForgeFolder IsNot Nothing, HasLiteLoader As Boolean = LiteLoaderFolder IsNot Nothing, HasFabric As Boolean = FabricFolder IsNot Nothing, HasQuilt As Boolean = QuiltFolder IsNot Nothing + Dim OutputName As String, MinecraftName As String, OptiFineName As String, ForgeName As String, NeoForgeName As String, LiteLoaderName As String, FabricName As String, QuiltName As String + Dim OutputJsonPath As String, MinecraftJsonPath As String, OptiFineJsonPath As String = Nothing, ForgeJsonPath As String = Nothing, NeoForgeJsonPath As String = Nothing, LiteLoaderJsonPath As String = Nothing, FabricJsonPath As String = Nothing, QuiltJsonPath As String = Nothing Dim OutputJar As String, MinecraftJar As String #Region "初始化路径信息" If Not OutputFolder.EndsWithF("\") Then OutputFolder += "\" @@ -2246,43 +2452,55 @@ Retry: FabricName = GetFolderNameFromPath(FabricFolder) FabricJsonPath = FabricFolder & FabricName & ".json" End If + + If HasQuilt Then + If Not QuiltFolder.EndsWithF("\") Then QuiltFolder += "\" + QuiltName = GetFolderNameFromPath(QuiltFolder) + QuiltJsonPath = QuiltFolder & QuiltName & ".json" + End If #End Region - Dim OutputJson As JObject, MinecraftJson As JObject, OptiFineJson As JObject = Nothing, ForgeJson As JObject = Nothing, NeoForgeJson As JObject = Nothing, LiteLoaderJson As JObject = Nothing, FabricJson As JObject = Nothing + Dim OutputJson As JObject, MinecraftJson As JObject, OptiFineJson As JObject = Nothing, ForgeJson As JObject = Nothing, NeoForgeJson As JObject = Nothing, LiteLoaderJson As JObject = Nothing, FabricJson As JObject = Nothing, QuiltJson As JObject = Nothing #Region "读取文件并检查文件是否合规" Dim MinecraftJsonText As String = ReadFile(MinecraftJsonPath) - If Not MinecraftJsonText.StartsWithF("{") Then Throw New Exception("Minecraft json 有误,地址:" & MinecraftJsonPath & ",前段内容:" & MinecraftJsonText.Substring(0, Math.Min(MinecraftJsonText.Length, 1000))) + If Not MinecraftJsonText.StartsWithF("{") Then Throw New Exception("Minecraft Json 有误,地址:" & MinecraftJsonPath & ",前段内容:" & MinecraftJsonText.Substring(0, Math.Min(MinecraftJsonText.Length, 1000))) MinecraftJson = GetJson(MinecraftJsonText) If HasOptiFine Then Dim OptiFineJsonText As String = ReadFile(OptiFineJsonPath) - If Not OptiFineJsonText.StartsWithF("{") Then Throw New Exception("OptiFine json 有误,地址:" & OptiFineJsonPath & ",前段内容:" & OptiFineJsonText.Substring(0, Math.Min(OptiFineJsonText.Length, 1000))) + If Not OptiFineJsonText.StartsWithF("{") Then Throw New Exception("OptiFine Json 有误,地址:" & OptiFineJsonPath & ",前段内容:" & OptiFineJsonText.Substring(0, Math.Min(OptiFineJsonText.Length, 1000))) OptiFineJson = GetJson(OptiFineJsonText) End If If HasForge Then Dim ForgeJsonText As String = ReadFile(ForgeJsonPath) - If Not ForgeJsonText.StartsWithF("{") Then Throw New Exception("Forge json 有误,地址:" & ForgeJsonPath & ",前段内容:" & ForgeJsonText.Substring(0, Math.Min(ForgeJsonText.Length, 1000))) + If Not ForgeJsonText.StartsWithF("{") Then Throw New Exception("Forge Json 有误,地址:" & ForgeJsonPath & ",前段内容:" & ForgeJsonText.Substring(0, Math.Min(ForgeJsonText.Length, 1000))) ForgeJson = GetJson(ForgeJsonText) End If If HasNeoForge Then Dim NeoForgeJsonText As String = ReadFile(NeoForgeJsonPath) - If Not NeoForgeJsonText.StartsWithF("{") Then Throw New Exception("NeoForge json 有误,地址:" & NeoForgeJsonPath & ",前段内容:" & NeoForgeJsonText.Substring(0, Math.Min(NeoForgeJsonText.Length, 1000))) + If Not NeoForgeJsonText.StartsWithF("{") Then Throw New Exception("NeoForge Json 有误,地址:" & NeoForgeJsonPath & ",前段内容:" & NeoForgeJsonText.Substring(0, Math.Min(NeoForgeJsonText.Length, 1000))) NeoForgeJson = GetJson(NeoForgeJsonText) End If If HasLiteLoader Then Dim LiteLoaderJsonText As String = ReadFile(LiteLoaderJsonPath) - If Not LiteLoaderJsonText.StartsWithF("{") Then Throw New Exception("LiteLoader json 有误,地址:" & LiteLoaderJsonPath & ",前段内容:" & LiteLoaderJsonText.Substring(0, Math.Min(LiteLoaderJsonText.Length, 1000))) + If Not LiteLoaderJsonText.StartsWithF("{") Then Throw New Exception("LiteLoader Json 有误,地址:" & LiteLoaderJsonPath & ",前段内容:" & LiteLoaderJsonText.Substring(0, Math.Min(LiteLoaderJsonText.Length, 1000))) LiteLoaderJson = GetJson(LiteLoaderJsonText) End If If HasFabric Then Dim FabricJsonText As String = ReadFile(FabricJsonPath) - If Not FabricJsonText.StartsWithF("{") Then Throw New Exception("Fabric json 有误,地址:" & FabricJsonPath & ",前段内容:" & FabricJsonText.Substring(0, Math.Min(FabricJsonText.Length, 1000))) + If Not FabricJsonText.StartsWithF("{") Then Throw New Exception("Fabric Json 有误,地址:" & FabricJsonPath & ",前段内容:" & FabricJsonText.Substring(0, Math.Min(FabricJsonText.Length, 1000))) FabricJson = GetJson(FabricJsonText) End If + + If HasQuilt Then + Dim QuiltJsonText As String = ReadFile(QuiltJsonPath) + If Not QuiltJsonText.StartsWithF("{") Then Throw New Exception("Quilt Json 有误,地址:" & QuiltJsonPath & ",前段内容:" & QuiltJsonText.Substring(0, Math.Min(QuiltJsonText.Length, 1000))) + QuiltJson = GetJson(QuiltJsonText) + End If #End Region #Region "处理 JSON 文件" @@ -2339,6 +2557,12 @@ Retry: FabricJson.Remove("time") OutputJson.Merge(FabricJson) End If + If HasQuilt Then + '合并 Quilt + QuiltJson.Remove("releaseTime") + QuiltJson.Remove("time") + OutputJson.Merge(QuiltJson) + End If '修改 If RealArguments IsNot Nothing AndAlso RealArguments.Replace(" ", "") <> "" Then OutputJson("minecraftArguments") = RealArguments OutputJson.Remove("_comment_") diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml index e45317ec..5c2aff08 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadClient.xaml @@ -9,7 +9,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompFavorites.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompFavorites.xaml.vb new file mode 100644 index 00000000..c2f63356 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompFavorites.xaml.vb @@ -0,0 +1,315 @@ +Public Class PageDownloadCompFavorites + +#Region "加载器信息" + '加载器信息 + Public Shared Loader As New LoaderTask(Of List(Of String), List(Of CompProject))("CompProject Favorites", AddressOf CompFavoritesGet, AddressOf LoaderInput) + + Private Sub PageDownloadCompFavorites_Inited(sender As Object, e As EventArgs) Handles Me.Initialized + PageLoaderInit(Load, PanLoad, PanContent, Nothing, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput) + End Sub + Private Sub PageDownloadCompFavorites_Loaded(sender As Object, e As EventArgs) Handles Me.Loaded + SelectedItemList.Clear() + RefreshBar() + If Loader.Input IsNot Nothing AndAlso (Not Loader.Input.Count.Equals(CompFavorites.FavoritesList.Count) OrElse Loader.Input.Except(CompFavorites.FavoritesList).Any()) Then + Loader.Start() + End If + End Sub + + Private Shared Function LoaderInput() As List(Of String) + Return CompFavorites.FavoritesList.Clone() '复制而不是直接引用! + End Function + Private Shared Sub CompFavoritesGet(Task As LoaderTask(Of List(Of String), List(Of CompProject))) + Task.Output = CompRequest.GetCompProjectsByIds(Task.Input) + End Sub +#End Region + + Private CompItemList As New List(Of MyListItem) + Private SelectedItemList As New List(Of MyListItem) + +#Region "UI 化" + Class CompListItemContainer ' 用来存储自动依据类型生成的卡片及其相关信息 + Public Property Card As MyCard + Public Property ContentList As StackPanel + Public Property Title As String + Public Property CompType As Integer + End Class + + Dim ItemList As New List(Of CompListItemContainer) + + ''' + ''' 返回适合当前工程项目的卡片记录 + ''' + ''' 工程项目类型 + ''' + Private Function GetSuitListContainer(Type As Integer) As CompListItemContainer + If ItemList.Any(Function(e) e.CompType.Equals(Type)) Then + Return ItemList.First(Function(e) e.CompType.Equals(Type)) + Else + Dim NewItem As New CompListItemContainer With { + .Card = New MyCard With { + .CanSwap = True, + .Margin = New Thickness(0, 0, 0, 15) + }, + .ContentList = New StackPanel With { + .Orientation = Orientation.Vertical, + .Margin = New Thickness(12, 38, 12, 12) + }, + .CompType = Type + } + Select Case Type + Case -1 + NewItem.Title = "搜索结果 ({0})" ' 搜索结果 + Case CompType.Mod + NewItem.Title = "Mod ({0})" + Case CompType.ModPack + NewItem.Title = "整合包 ({0})" + Case CompType.ResourcePack + NewItem.Title = "资源包 ({0})" + Case CompType.Shader + NewItem.Title = "光影包 ({0})" + Case Else + NewItem.Title = "未分类类型 ({0})" + End Select + NewItem.Card.Title = String.Format(NewItem.Title, 0) + NewItem.Card.Children.Add(NewItem.ContentList) + ItemList.Add(NewItem) + Return NewItem + End If + End Function + + '结果 UI 化 + Private Sub Load_OnFinish() + ItemList.Clear() + Try + AllowSearch = False + PanSearchBox.Text = String.Empty + AllowSearch = True + CompItemList.Clear() + HintGetFail.Visibility = If(Loader.Input.Count = Loader.Output.Count, Visibility.Collapsed, Visibility.Visible) + For Each item In Loader.Output + Dim CompItem = item.ToListItem() + + CompItem.Type = MyListItem.CheckType.CheckBox + '----添加按钮---- + '删除按钮 + Dim Btn_Delete As New MyIconButton + Btn_Delete.Logo = Logo.IconButtonLikeFill + Btn_Delete.ToolTip = "取消收藏" + ToolTipService.SetPlacement(Btn_Delete, Primitives.PlacementMode.Center) + ToolTipService.SetVerticalOffset(Btn_Delete, 30) + ToolTipService.SetHorizontalOffset(Btn_Delete, 2) + AddHandler Btn_Delete.Click, Sub(sender As Object, e As EventArgs) + Items_CancelFavorites(CompItem) + RefreshContent() + RefreshCardTitle() + RefreshBar() + End Sub + CompItem.Buttons = {Btn_Delete} + '---操作逻辑--- + '右键查看详细信息界面 + AddHandler CompItem.MouseRightButtonUp, Sub(sender As Object, e As EventArgs) + FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, + .Additional = {CompItem.Tag, New List(Of String), String.Empty, CompModLoaderType.Any}}) + End Sub + '---其它事件--- + AddHandler CompItem.Changed, AddressOf ItemCheckStatusChanged + CompItemList.Add(CompItem) + Next + If CompItemList.Any() Then '有收藏 + If Not IsSearching Then + PanSearchBox.Visibility = Visibility.Visible + PanContentList.Visibility = Visibility.Visible + CardNoContent.Visibility = Visibility.Collapsed + End If + Else '没有收藏 + PanSearchBox.Visibility = Visibility.Collapsed + PanContentList.Visibility = Visibility.Collapsed + CardNoContent.Visibility = Visibility.Visible + End If + + RefreshContent() + RefreshCardTitle() + Catch ex As Exception + Log(ex, "可视化收藏夹列表出错", LogLevel.Feedback) + End Try + End Sub + + Private Sub RefreshContent() + For Each item In ItemList ' 清除逻辑父子关系 + item.ContentList.Children.Clear() + Next + PanContentList.Children.Clear() + Dim DataSource As List(Of MyListItem) = If(IsSearching, SearchResult, CompItemList) + For Each item As MyListItem In DataSource + GetSuitListContainer(If(IsSearching, -1, CType(item.Tag, CompProject).Type)).ContentList.Children.Add(item) + Next + For Each item In ItemList + If item.ContentList.Children.Count = 0 Then Continue For + PanContentList.Children.Add(item.Card) + Next + End Sub + + Private Sub RefreshCardTitle() + For Each item In ItemList + item.Card.Title = String.Format(item.Title, CompItemList.Where(Function(e) CType(e.Tag, CompProject).Type = item.CompType).Count()) + Next + If Not ItemList.Any(Function(e) e.CompType.Equals(-1)) Then Return + Dim SearchItem = ItemList.First(Function(e) e.CompType.Equals(-1)) + If SearchItem IsNot Nothing Then + SearchItem.Card.Title = String.Format(SearchItem.Title, SearchResult.Count) + End If + End Sub + + Private BottomBarShownCount As Integer = 0 + + Private Sub RefreshBar() + Dim NewCount As Integer = SelectedItemList.Count + Dim Selected = NewCount > 0 + If Selected Then LabSelect.Text = $"已选择 {NewCount} 个收藏项目" '取消所有选择时不更新数字 + '更新显示状态 + If AniControlEnabled = 0 Then + PanContentList.Margin = New Thickness(0, 0, 0, If(Selected, 80, 0)) + If Selected Then + '仅在数量增加时播放出现/跳跃动画 + If BottomBarShownCount >= NewCount Then + BottomBarShownCount = NewCount + Return + Else + BottomBarShownCount = NewCount + End If + '出现/跳跃动画 + CardSelect.Visibility = Visibility.Visible + AniStart({ + AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60), + AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)), + AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)), + AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)) + }, "CompFavorites Sidebar") + Else + '不重复播放隐藏动画 + If BottomBarShownCount = 0 Then Return + BottomBarShownCount = 0 + '隐藏动画 + AniStart({ + AaOpacity(CardSelect, -CardSelect.Opacity, 90), + AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)), + AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True) + }, "CompFavorites Sidebar") + End If + Else + AniStop("CompFavorites Sidebar") + BottomBarShownCount = NewCount + If Selected Then + CardSelect.Visibility = Visibility.Visible + CardSelect.Opacity = 1 + TransSelect.Y = -25 + Else + CardSelect.Visibility = Visibility.Collapsed + CardSelect.Opacity = 0 + TransSelect.Y = -10 + End If + End If + End Sub + +#End Region + + '选中状态改变 + Private Sub ItemCheckStatusChanged(sender As Object, e As RouteEventArgs) + Dim SenderItem As MyListItem = sender + If SelectedItemList.Contains(SenderItem) Then SelectedItemList.Remove(SenderItem) + If SenderItem.Checked Then SelectedItemList.Add(SenderItem) + RefreshBar() + End Sub + + '自动重试 + Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged + Select Case Loader.State + Case LoadState.Failed + Dim ErrorMessage As String = "" + If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message + If ErrorMessage.Contains("不是有效的 json 文件") Then + Log("[Download] 下载的工程列表 JSON 文件损坏,已自动重试", LogLevel.Debug) + PageLoaderRestart() + End If + End Select + End Sub + + Private Sub Btn_FavoritesCancel_Clicked(sender As Object, e As RouteEventArgs) Handles Btn_FavoritesCancel.Click + For Each Items In SelectedItemList.Clone() + Items_CancelFavorites(Items) + Next + If CompItemList.Any Then + RefreshContent() + RefreshCardTitle() + Else + Loader.Start() + End If + RefreshBar() + End Sub + + Private Sub Btn_SelectCancel_Clicked(sender As Object, e As RouteEventArgs) Handles Btn_SelectCancel.Click + SelectedItemList.Clear() + Items_SetSelectAll(False) + End Sub + + Private Sub Items_SetSelectAll(TargetStatus As Boolean) + If IsSearching Then + For Each Item As MyListItem In SearchResult + Item.Checked = TargetStatus + Next + Else + For Each Item As MyListItem In CompItemList + Item.Checked = TargetStatus + Next + End If + SelectedItemList = CompItemList.Where(Function(e) e.Checked).ToList() + End Sub + + Private Sub Items_CancelFavorites(Item As MyListItem) + CompItemList.Remove(Item) + If SelectedItemList.Contains(Item) Then SelectedItemList.Remove(Item) + If SearchResult.Contains(Item) Then SearchResult.Remove(Item) + CompFavorites.FavoritesList.Remove(Item.Tag.Id) + CompFavorites.Save() + End Sub + + Private Sub Page_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown + If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then Items_SetSelectAll(True) + End Sub + +#Region "搜索" + + Private ReadOnly Property IsSearching As Boolean + Get + Return Not String.IsNullOrWhiteSpace(PanSearchBox.Text) + End Get + End Property + + Private AllowSearch As Boolean = True + Private SearchResult As New List(Of MyListItem) + Public Sub SearchRun() Handles PanSearchBox.TextChanged + If Not AllowSearch Then Exit Sub + If IsSearching Then + '构造请求 + Dim QueryList As New List(Of SearchEntry(Of MyListItem)) + For Each Item As MyListItem In CompItemList + Dim Entry As CompProject = Item.Tag + Dim SearchSource As New List(Of KeyValuePair(Of String, Double)) + SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.RawName, 1)) + If Entry.Description IsNot Nothing AndAlso Entry.Description <> "" Then + SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Description, 0.4)) + End If + If Entry.TranslatedName <> Entry.RawName Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.TranslatedName, 1)) + SearchSource.Add(New KeyValuePair(Of String, Double)(String.Join("", Entry.Tags), 0.2)) + QueryList.Add(New SearchEntry(Of MyListItem) With {.Item = Item, .SearchSource = SearchSource}) + Next + '进行搜索 + SearchResult = Search(QueryList, PanSearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35).Select(Function(r) r.Item).ToList + End If + RefreshContent() + RefreshCardTitle() + End Sub + +#End Region + +End Class diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml index 6c7a8009..cf22f4d5 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml @@ -19,6 +19,8 @@ + + @@ -134,6 +136,42 @@ Data="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -201,6 +239,8 @@ + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb index 9b5a245c..8adda0a8 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb @@ -1,4 +1,4 @@ -Public Class PageDownloadInstall +Public Class PageDownloadInstall Private Sub LoaderInit() Handles Me.Initialized PageLoaderInit(LoadMinecraft, PanLoad, PanBack, Nothing, DlClientListLoader, AddressOf LoadMinecraft_OnFinish) @@ -10,6 +10,7 @@ DlOptiFineListLoader.Start() DlLiteLoaderListLoader.Start() DlFabricListLoader.Start() + DlQuiltListLoader.Start() DlNeoForgeListLoader.Start() '重载预览 @@ -27,6 +28,8 @@ LoadLiteLoader.State = DlLiteLoaderListLoader LoadFabric.State = DlFabricListLoader LoadFabricApi.State = DlFabricApiLoader + LoadQuilt.State = DlQuiltListLoader + LoadQSL.State = DlQSLLoader LoadNeoForge.State = DlNeoForgeListLoader LoadOptiFabric.State = DlOptiFabricLoader End Sub @@ -41,6 +44,7 @@ IsInSelectPage = True AutoSelectedFabricApi = False + AutoSelectedQSL = False AutoSelectedOptiFabric = False IsSelectNameEdited = False PanSelect.Visibility = Visibility.Visible @@ -56,6 +60,8 @@ CardNeoForge.IsSwaped = True CardFabric.IsSwaped = True CardFabricApi.IsSwaped = True + CardQuilt.IsSwaped = True + CardQSL.IsSwaped = True CardOptiFabric.IsSwaped = True If Not Setup.Get("HintInstallBack") Then @@ -76,8 +82,9 @@ ForgeLoader.Start(SelectedMinecraftId) End If - '启动 Fabric API、OptiFabric 加载 + '启动 Fabric API、QSL、OptiFabric 加载 DlFabricApiLoader.Start() + DlQSLLoader.Start() DlOptiFabricLoader.Start() AniStart({ @@ -93,6 +100,8 @@ NeoForge_Loaded() Fabric_Loaded() FabricApi_Loaded() + Quilt_Loaded() + QSL_Loaded() OptiFabric_Loaded() SelectReload() End Sub, After:=True), @@ -111,6 +120,8 @@ BtnNeoForgeClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardNeoForge.MainTextBlock, .Mode = BindingMode.OneWay}) BtnFabricClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardFabric.MainTextBlock, .Mode = BindingMode.OneWay}) BtnFabricApiClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardFabricApi.MainTextBlock, .Mode = BindingMode.OneWay}) + BtnQuiltClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardQuilt.MainTextBlock, .Mode = BindingMode.OneWay}) + BtnQSLClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardQSL.MainTextBlock, .Mode = BindingMode.OneWay}) BtnOptiFabricClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardOptiFabric.MainTextBlock, .Mode = BindingMode.OneWay}) End Sub,, True) }, "FrmDownloadInstall SelectPageSwitch", True) @@ -180,6 +191,12 @@ End If End Sub + 'Mod Loader 统一判断,内容应为 Forge / NeoForge / Fabric / Quilt + Private SelectedLoaderName As String = Nothing + + 'Mod Loader API 统一判断,内容应为 Fabric API 或 QFAPI / QSL + Private SelectedAPIName As String = Nothing + 'LiteLoader Private SelectedLiteLoader As DlLiteLoaderListEntry = Nothing Private Sub SetLiteLoaderInfoShow(IsShow As String) @@ -280,6 +297,46 @@ End If End Sub + 'Quilt + Private SelectedQuilt As String = Nothing + Private Sub SetQuiltInfoShow(IsShow As String) + If PanQuiltInfo.Tag = IsShow Then Exit Sub + PanQuiltInfo.Tag = IsShow + If IsShow = "True" Then + '显示信息栏 + AniStart({ + AaTranslateY(PanQuiltInfo, -CType(PanQuiltInfo.RenderTransform, TranslateTransform).Y, 270, 100, Ease:=New AniEaseOutBack), + AaOpacity(PanQuiltInfo, 1 - PanQuiltInfo.Opacity, 100, 90) + }, "SetQuiltInfoShow") + Else + '隐藏信息栏 + AniStart({ + AaTranslateY(PanQuiltInfo, 6 - CType(PanQuiltInfo.RenderTransform, TranslateTransform).Y, 200), + AaOpacity(PanQuiltInfo, -PanQuiltInfo.Opacity, 100) + }, "SetQuiltInfoShow") + End If + End Sub + + 'QSL + Private SelectedQSL As CompFile = Nothing + Private Sub SetQSLInfoShow(IsShow As String) + If PanQSLInfo.Tag = IsShow Then Exit Sub + PanQSLInfo.Tag = IsShow + If IsShow = "True" Then + '显示信息栏 + AniStart({ + AaTranslateY(PanQSLInfo, -CType(PanQSLInfo.RenderTransform, TranslateTransform).Y, 270, 100, Ease:=New AniEaseOutBack), + AaOpacity(PanQSLInfo, 1 - PanQSLInfo.Opacity, 100, 90) + }, "SetQSLInfoShow") + Else + '隐藏信息栏 + AniStart({ + AaTranslateY(PanQSLInfo, 6 - CType(PanQSLInfo.RenderTransform, TranslateTransform).Y, 200), + AaOpacity(PanQSLInfo, -PanQSLInfo.Opacity, 100) + }, "SetQSLInfoShow") + End If + End Sub + 'OptiFabric Private SelectedOptiFabric As CompFile = Nothing Private Sub SetOptiFabricInfoShow(IsShow As String) @@ -411,13 +468,13 @@ End If End If 'FabricApi - If SelectedFabric Is Nothing Then + If SelectedFabric Is Nothing AndAlso SelectedQuilt Is Nothing Then CardFabricApi.Visibility = Visibility.Collapsed Else CardFabricApi.Visibility = Visibility.Visible Dim FabricApiError As String = LoadFabricApiGetError() CardFabricApi.MainSwap.Visibility = If(FabricApiError Is Nothing, Visibility.Visible, Visibility.Collapsed) - If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwaped = True + If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing AndAlso SelectedQuilt Is Nothing Then CardFabricApi.IsSwaped = True SetFabricApiInfoShow(CardFabricApi.IsSwaped) If SelectedFabricApi Is Nothing Then BtnFabricApiClear.Visibility = Visibility.Collapsed @@ -431,6 +488,48 @@ LabFabricApi.Foreground = ColorGray1 End If End If + 'Quilt + If SelectedMinecraftId.Contains("1.") AndAlso Val(SelectedMinecraftId.Split(".")(1)) <= 14 AndAlso Not SelectedMinecraftId.Contains("1.14.4") Then + CardQuilt.Visibility = Visibility.Collapsed + Else + CardQuilt.Visibility = Visibility.Visible + Dim QuiltError As String = LoadQuiltGetError() + CardQuilt.MainSwap.Visibility = If(QuiltError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If QuiltError IsNot Nothing Then CardQuilt.IsSwaped = True + SetQuiltInfoShow(CardQuilt.IsSwaped) + If SelectedQuilt Is Nothing Then + BtnQuiltClear.Visibility = Visibility.Collapsed + ImgQuilt.Visibility = Visibility.Collapsed + LabQuilt.Text = If(QuiltError, "点击选择") + LabQuilt.Foreground = ColorGray4 + Else + BtnQuiltClear.Visibility = Visibility.Visible + ImgQuilt.Visibility = Visibility.Visible + LabQuilt.Text = SelectedQuilt.Replace("+build", "") + LabQuilt.Foreground = ColorGray1 + End If + End If + 'QSL + If SelectedQuilt Is Nothing Then + CardQSL.Visibility = Visibility.Collapsed + Else + CardQSL.Visibility = Visibility.Visible + Dim QSLError As String = LoadQSLGetError() + CardQSL.MainSwap.Visibility = If(QSLError Is Nothing, Visibility.Visible, Visibility.Collapsed) + If QSLError IsNot Nothing OrElse SelectedQuilt Is Nothing Then CardQSL.IsSwaped = True + SetQSLInfoShow(CardQSL.IsSwaped) + If SelectedQSL Is Nothing Then + BtnQSLClear.Visibility = Visibility.Collapsed + ImgQSL.Visibility = Visibility.Collapsed + LabQSL.Text = If(QSLError, "点击选择") + LabQSL.Foreground = ColorGray4 + Else + BtnQSLClear.Visibility = Visibility.Visible + ImgQSL.Visibility = Visibility.Visible + LabQSL.Text = SelectedQSL.DisplayName.Split("]")(1).Trim + LabQSL.Foreground = ColorGray1 + End If + End If 'OptiFabric If SelectedFabric Is Nothing OrElse SelectedOptiFine Is Nothing Then CardOptiFabric.Visibility = Visibility.Collapsed @@ -458,6 +557,23 @@ Else HintFabricAPI.Visibility = Visibility.Collapsed End If + If SelectedQuilt IsNot Nothing AndAlso SelectedQSL Is Nothing AndAlso SelectedFabricApi Is Nothing Then + HintQSL.Visibility = Visibility.Visible + Else + HintQSL.Visibility = Visibility.Collapsed + End If + If SelectedQuilt IsNot Nothing AndAlso SelectedFabricApi IsNot Nothing AndAlso DlQSLLoader.Output IsNot Nothing Then + For Each Version In DlQSLLoader.Output + If IsSuitableQSL(Version.GameVersions, SelectedMinecraftId) Then + HintQuiltFabricAPI.Visibility = Visibility.Visible + Exit For + Else + HintQuiltFabricAPI.Visibility = Visibility.Collapsed + End If + Next + Else + HintQuiltFabricAPI.Visibility = Visibility.Collapsed + End If If SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then If SelectedMinecraftId.StartsWith("1.14") OrElse SelectedMinecraftId.StartsWith("1.15") Then HintOptiFabric.Visibility = Visibility.Collapsed @@ -488,10 +604,14 @@ SelectedMinecraftIcon = Nothing SelectedOptiFine = Nothing SelectedLiteLoader = Nothing + SelectedLoaderName = Nothing + SelectedAPIName = Nothing SelectedForge = Nothing SelectedNeoForge = Nothing SelectedFabric = Nothing SelectedFabricApi = Nothing + SelectedQuilt = Nothing + SelectedQSL = Nothing SelectedOptiFabric = Nothing End Sub @@ -502,7 +622,10 @@ Private Function GetSelectName() As String Dim Name As String = SelectedMinecraftId If SelectedFabric IsNot Nothing Then - Name += "-Fabric " & SelectedFabric.Replace("+build", "") + Name += "-Fabric_" & SelectedFabric.Replace("+build", "") + End If + If SelectedQuilt IsNot Nothing Then + Name += "-Quilt_" & SelectedQuilt End If If SelectedForge IsNot Nothing Then Name += "-Forge_" & SelectedForge.VersionName @@ -526,6 +649,9 @@ If SelectedFabric IsNot Nothing Then Info += ", Fabric " & SelectedFabric.Replace("+build", "") End If + If SelectedQuilt IsNot Nothing Then + Info += ", Quilt " & SelectedQuilt + End If If SelectedForge IsNot Nothing Then Info += ", Forge " & SelectedForge.VersionName End If @@ -547,6 +673,8 @@ Private Function GetSelectLogo() As String If SelectedFabric IsNot Nothing Then Return "pack://application:,,,/images/Blocks/Fabric.png" + ElseIf SelectedQuilt IsNot Nothing Then + Return "pack://application:,,,/images/Blocks/Quilt.png" ElseIf SelectedForge IsNot Nothing Then Return "pack://application:,,,/images/Blocks/Anvil.png" ElseIf SelectedNeoForge IsNot Nothing Then @@ -694,11 +822,11 @@ ''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。 ''' Private Function LoadOptiFineGetError() As String - If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" + If SelectedLoaderName = "NeoForge" OrElse SelectedLoaderName = "Quilt" Then Return $"与 {SelectedLoaderName} 不兼容" 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 '检查 Forge 1.13 - 1.14.3:全部不兼容 - If SelectedForge IsNot Nothing AndAlso + If SelectedLoaderName = "Forge" AndAlso VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then Return "与 Forge 不兼容" End If @@ -876,8 +1004,7 @@ Dim NotSuitForOptiFine As Boolean = False For Each Version In Loader.Output If Version.Category = "universal" OrElse Version.Category = "client" Then Continue For '跳过无法自动安装的版本 - If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" - If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容" + If SelectedLoaderName IsNot Nothing AndAlso SelectedLoaderName IsNot "Forge" Then Return $"与 {SelectedLoaderName} 不兼容" If SelectedOptiFine IsNot Nothing AndAlso VersionSortInteger(SelectedMinecraftId, "1.13") >= 0 AndAlso VersionSortInteger("1.14.3", SelectedMinecraftId) >= 0 Then Return "与 OptiFine 不兼容" '1.13 ~ 1.14.3 OptiFine 检查 @@ -927,6 +1054,7 @@ '选择与清除 Private Sub Forge_Selected(sender As MyListItem, e As EventArgs) SelectedForge = sender.Tag + SelectedLoaderName = "Forge" CardForge.IsSwaped = True If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, SelectedForge) Then SelectedOptiFine = Nothing OptiFine_Loaded() @@ -934,6 +1062,7 @@ End Sub Private Sub Forge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnForgeClear.MouseLeftButtonUp SelectedForge = Nothing + SelectedLoaderName = Nothing CardForge.IsSwaped = True e.Handled = True OptiFine_Loaded() @@ -950,8 +1079,7 @@ Private Function LoadNeoForgeGetError() As String If Not SelectedMinecraftId.StartsWith("1.") Then Return "没有可用版本" If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容" - If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" - If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容" + If SelectedLoaderName IsNot Nothing AndAlso SelectedLoaderName IsNot "NeoForge" Then Return $"与 {SelectedLoaderName} 不兼容" If LoadNeoForge Is Nothing OrElse LoadNeoForge.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" If LoadNeoForge.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadNeoForge.State, Object).Error.Message If DlNeoForgeListLoader.Output.Value.Any(Function(v) v.Inherit = SelectedMinecraftId) Then @@ -989,12 +1117,14 @@ '选择与清除 Private Sub NeoForge_Selected(sender As MyListItem, e As EventArgs) SelectedNeoForge = sender.Tag + SelectedLoaderName = "NeoForge" CardNeoForge.IsSwaped = True OptiFine_Loaded() SelectReload() End Sub Private Sub NeoForge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnNeoForgeClear.MouseLeftButtonUp SelectedNeoForge = Nothing + SelectedLoaderName = Nothing CardNeoForge.IsSwaped = True e.Handled = True OptiFine_Loaded() @@ -1013,8 +1143,7 @@ If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message 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 SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" - If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容" + If SelectedLoaderName IsNot Nothing AndAlso SelectedLoaderName IsNot "Fabric" Then Return $"与 {SelectedLoaderName} 不兼容" Return Nothing End If Next @@ -1048,6 +1177,7 @@ '选择与清除 Public Sub Fabric_Selected(sender As MyListItem, e As EventArgs) SelectedFabric = sender.Tag("version").ToString + SelectedLoaderName = "Fabric" FabricApi_Loaded() OptiFabric_Loaded() CardFabric.IsSwaped = True @@ -1057,6 +1187,8 @@ SelectedFabric = Nothing SelectedFabricApi = Nothing SelectedOptiFabric = Nothing + SelectedLoaderName = Nothing + SelectedAPIName = Nothing CardFabric.IsSwaped = True e.Handled = True SelectReload() @@ -1111,13 +1243,14 @@ Private Function LoadFabricApiGetError() As String If LoadFabricApi Is Nothing OrElse LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" If LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabricApi.State, Object).Error.Message + If SelectedAPIName IsNot Nothing AndAlso SelectedAPIName IsNot "Fabric API" Then Return $"与 {SelectedAPIName} 不兼容" If DlFabricApiLoader.Output Is Nothing Then - If SelectedFabric Is Nothing Then Return "需要安装 Fabric" + If SelectedFabric Is Nothing AndAlso SelectedQuilt Is Nothing Then Return "需要安装 Fabric / Quilt" Return "正在获取版本列表……" End If For Each Version In DlFabricApiLoader.Output If Not IsSuitableFabricApi(Version.DisplayName, SelectedMinecraftId) Then Continue For - If SelectedFabric Is Nothing Then Return "需要安装 Fabric" + If SelectedFabric Is Nothing AndAlso SelectedQuilt Is Nothing Then Return "需要安装 Fabric / Quilt" Return Nothing Next Return "没有可用版本" @@ -1135,7 +1268,7 @@ Private Sub FabricApi_Loaded() Handles LoadFabricApi.StateChanged Try If DlFabricApiLoader.State <> LoadState.Finished Then Exit Sub - If SelectedMinecraftId Is Nothing OrElse SelectedFabric Is Nothing Then Exit Sub + If SelectedMinecraftId Is Nothing OrElse (SelectedFabric Is Nothing AndAlso SelectedQuilt Is Nothing) Then Exit Sub '获取版本列表 Dim Versions As New List(Of CompFile) For Each Version In DlFabricApiLoader.Output @@ -1156,7 +1289,7 @@ PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected)) Next '自动选择 Fabric API - If Not AutoSelectedFabricApi Then + If (Not AutoSelectedFabricApi AndAlso SelectedQuilt Is Nothing) OrElse (SelectedQuilt IsNot Nothing AndAlso LoadQSLGetError() Is "没有可用版本") Then AutoSelectedFabricApi = True Log($"[Download] 已自动选择 Fabric API:{CType(PanFabricApi.Children(0), MyListItem).Title}") FabricApi_Selected(PanFabricApi.Children(0), Nothing) @@ -1169,11 +1302,13 @@ '选择与清除 Private Sub FabricApi_Selected(sender As MyListItem, e As EventArgs) SelectedFabricApi = sender.Tag + SelectedAPIName = "Fabric API" CardFabricApi.IsSwaped = True SelectReload() End Sub Private Sub FabricApi_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricApiClear.MouseLeftButtonUp SelectedFabricApi = Nothing + SelectedAPIName = Nothing CardFabricApi.IsSwaped = True e.Handled = True SelectReload() @@ -1181,6 +1316,166 @@ #End Region +#Region "Quilt 列表" + + ''' + ''' 获取 Quilt 的加载异常信息。若正常则返回 Nothing。 + ''' + Private Function LoadQuiltGetError() As String + If LoadQuilt Is Nothing OrElse LoadQuilt.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" + If LoadQuilt.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadQuilt.State, Object).Error.Message + For Each Version As JObject In DlQuiltListLoader.Output.Value("game") + If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then + If SelectedLoaderName IsNot Nothing AndAlso SelectedLoaderName IsNot "Quilt" Then Return $"与 {SelectedLoaderName} 不兼容" + Return Nothing + End If + Next + Return "没有可用版本" + End Function + + '限制展开 + Private Sub CardQuilt_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardQuilt.PreviewSwap + If LoadQuiltGetError() IsNot Nothing Then e.Handled = True + End Sub + + ''' + ''' 尝试重新可视化 Quilt 版本列表。 + ''' + Private Sub Quilt_Loaded() Handles LoadQuilt.StateChanged + Try + If DlQuiltListLoader.State <> LoadState.Finished Then Exit Sub + '获取版本列表 + Dim Versions As JArray = DlQuiltListLoader.Output.Value("loader") + If Not Versions.Any() Then Exit Sub + '可视化 + PanQuilt.Children.Clear() + PanQuilt.Tag = Versions + CardQuilt.SwapControl = PanQuilt + CardQuilt.SwapType = 14 + Catch ex As Exception + Log(ex, "可视化 Quilt 安装版本列表出错", LogLevel.Feedback) + End Try + End Sub + + '选择与清除 + Public Sub Quilt_Selected(sender As MyListItem, e As EventArgs) + SelectedQuilt = sender.Tag("version").ToString + SelectedLoaderName = "Quilt" + FabricApi_Loaded() + QSL_Loaded() + CardQuilt.IsSwaped = True + SelectReload() + End Sub + Private Sub Quilt_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnQuiltClear.MouseLeftButtonUp + SelectedQuilt = Nothing + SelectedQSL = Nothing + SelectedFabricApi = Nothing + SelectedLoaderName = Nothing + SelectedAPIName = Nothing + CardQuilt.IsSwaped = True + e.Handled = True + SelectReload() + End Sub + +#End Region + +#Region "QSL 列表" + + ''' + ''' 从显示名判断该 API 是否与某版本适配。 + ''' + Public Shared Function IsSuitableQSL(SupportVersions As List(Of String), MinecraftVersion As String) As Boolean + Try + If SupportVersions.Contains(MinecraftVersion) Then + Return True + Else + Return False + End If + Catch ex As Exception + Log(ex, "判断 QSL 版本适配性出错(" & SupportVersions.ToString & ", " & MinecraftVersion & ")") + Return False + End Try + End Function + + ''' + ''' 获取 QSL 的加载异常信息。若正常则返回 Nothing。 + ''' + Private Function LoadQSLGetError() As String + If LoadQSL Is Nothing OrElse LoadQSL.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" + If LoadQSL.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadQSL.State, Object).Error.Message + If SelectedAPIName IsNot Nothing AndAlso SelectedAPIName IsNot "QFAPI / QSL" Then Return $"与 {SelectedAPIName} 不兼容" + If DlQSLLoader.Output Is Nothing Then + If SelectedQuilt Is Nothing Then Return "需要安装 Quilt" + Return "正在获取版本列表……" + End If + For Each Version In DlQSLLoader.Output + If Not IsSuitableQSL(Version.GameVersions, SelectedMinecraftId) Then Continue For + If SelectedQuilt Is Nothing Then Return "需要安装 Quilt" + Return Nothing + Next + Return "没有可用版本" + End Function + + '限制展开 + Private Sub CardQSL_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardQSL.PreviewSwap + If LoadQSLGetError() IsNot Nothing Then e.Handled = True + End Sub + + Private AutoSelectedQSL As Boolean = False + ''' + ''' 尝试重新可视化 QSL 版本列表。 + ''' + Private Sub QSL_Loaded() Handles LoadQSL.StateChanged + Try + If DlQSLLoader.State <> LoadState.Finished Then Exit Sub + If SelectedMinecraftId Is Nothing OrElse SelectedQuilt Is Nothing Then Exit Sub + '获取版本列表 + Dim Versions As New List(Of CompFile) + For Each Version In DlQSLLoader.Output + If IsSuitableQSL(Version.GameVersions, SelectedMinecraftId) Then + If Not Version.DisplayName.StartsWith("[") Then + Log("[Download] 已特判修改 QSL 显示名:" & Version.DisplayName, LogLevel.Debug) + Version.DisplayName = "[" & SelectedMinecraftId & "] " & Version.DisplayName + End If + Versions.Add(Version) + End If + Next + If Not Versions.Any() Then Exit Sub + Versions = Sort(Versions, Function(a, b) a.ReleaseDate > b.ReleaseDate) + '可视化 + PanQSL.Children.Clear() + For Each Version In Versions + If Not IsSuitableQSL(Version.GameVersions, SelectedMinecraftId) Then Continue For + PanQSL.Children.Add(QSLDownloadListItem(Version, AddressOf QSL_Selected)) + Next + '自动选择 QSL + If Not AutoSelectedQSL Then + AutoSelectedQSL = True + Log($"[Download] 已自动选择 QSL:{CType(PanQSL.Children(0), MyListItem).Title}") + QSL_Selected(PanQSL.Children(0), Nothing) + End If + Catch ex As Exception + Log(ex, "可视化 QSL 安装版本列表出错", LogLevel.Feedback) + End Try + End Sub + + '选择与清除 + Private Sub QSL_Selected(sender As MyListItem, e As EventArgs) + SelectedQSL = sender.Tag + SelectedAPIName = "QFAPI / QSL" + CardQSL.IsSwaped = True + SelectReload() + End Sub + Private Sub QSL_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnQSLClear.MouseLeftButtonUp + SelectedQSL = Nothing + SelectedAPIName = Nothing + CardQSL.IsSwaped = True + e.Handled = True + SelectReload() + End Sub + +#End Region + #Region "OptiFabric 列表" ''' @@ -1299,6 +1594,8 @@ .NeoForgeEntry = SelectedNeoForge, .FabricVersion = SelectedFabric, .FabricApi = SelectedFabricApi, + .QuiltVersion = SelectedQuilt, + .QSL = SelectedQSL, .OptiFabric = SelectedOptiFabric, .LiteLoaderEntry = SelectedLiteLoader } diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml index bb644b1e..5db75e85 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml @@ -54,13 +54,21 @@ - - - - - - - + + + + + + + + + + + + + @@ -68,7 +76,9 @@ + + @@ -77,6 +87,7 @@ + @@ -86,6 +97,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb index f2eb250e..f226814f 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb @@ -11,7 +11,7 @@ ''' ''' 勾选事件改变页面。 ''' - Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemClient.Check, ItemOptiFine.Check, ItemForge.Check, ItemNeoForge.Check, ItemLiteLoader.Check, ItemMod.Check, ItemFabric.Check, ItemPack.Check + Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemInstall.Check, ItemClient.Check, ItemOptiFine.Check, ItemForge.Check, ItemNeoForge.Check, ItemLiteLoader.Check, ItemMod.Check, ItemFabric.Check, ItemQuilt.Check, ItemPack.Check, ItemResourcePack.Check, ItemShader.Check, ItemFavorites.Check '尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0 '若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False) If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag)) @@ -41,12 +41,24 @@ Case FormMain.PageSubType.DownloadFabric If FrmDownloadFabric Is Nothing Then FrmDownloadFabric = New PageDownloadFabric Return FrmDownloadFabric + Case FormMain.PageSubType.DownloadQuilt + If FrmDownloadQuilt Is Nothing Then FrmDownloadQuilt = New PageDownloadQuilt + Return FrmDownloadQuilt Case FormMain.PageSubType.DownloadMod If FrmDownloadMod Is Nothing Then FrmDownloadMod = New PageDownloadMod Return FrmDownloadMod Case FormMain.PageSubType.DownloadPack If FrmDownloadPack Is Nothing Then FrmDownloadPack = New PageDownloadPack Return FrmDownloadPack + Case FormMain.PageSubType.DownloadResourcePack + If FrmDownloadResourcePack Is Nothing Then FrmDownloadResourcePack = New PageDownloadResourcePack + Return FrmDownloadResourcePack + Case FormMain.PageSubType.DownloadShader + If FrmDownloadShader Is Nothing Then FrmDownloadShader = New PageDownloadShader + Return FrmDownloadShader + Case FormMain.PageSubType.DownloadCompFavorites + If FrmDownloadCompFavorites Is Nothing Then FrmDownloadCompFavorites = New PageDownloadCompFavorites + Return FrmDownloadCompFavorites Case Else Throw New Exception("未知的下载子页面种类:" & ID) End Select @@ -107,6 +119,8 @@ DlLiteLoaderListLoader.Start(IsForceRestart:=True) DlFabricListLoader.Start(IsForceRestart:=True) DlFabricApiLoader.Start(IsForceRestart:=True) + DlQuiltListLoader.Start(IsForceRestart:=True) + DlQSLLoader.Start(IsForceRestart:=True) DlOptiFabricLoader.Start(IsForceRestart:=True) ItemInstall.Checked = True Case FormMain.PageSubType.DownloadMod @@ -123,6 +137,18 @@ CompFilesCache.Clear() If FrmDownloadPack IsNot Nothing Then FrmDownloadPack.PageLoaderRestart() ItemPack.Checked = True + Case FormMain.PageSubType.DownloadResourcePack + PageDownloadResourcePack.Storage = New CompProjectStorage + PageDownloadResourcePack.Page = 0 + CompProjectCache.Clear() + If FrmDownloadResourcePack IsNot Nothing Then FrmDownloadResourcePack.PageLoaderRestart() + ItemResourcePack.Checked = True + Case FormMain.PageSubType.DownloadShader + PageDownloadShader.Storage = New CompProjectStorage + PageDownloadShader.Page = 0 + CompProjectCache.Clear() + If FrmDownloadShader IsNot Nothing Then FrmDownloadShader.PageLoaderRestart() + ItemShader.Checked = True Case FormMain.PageSubType.DownloadClient DlClientListLoader.Start(IsForceRestart:=True) ItemClient.Checked = True @@ -141,6 +167,12 @@ Case FormMain.PageSubType.DownloadFabric DlFabricListLoader.Start(IsForceRestart:=True) ItemFabric.Checked = True + Case FormMain.PageSubType.DownloadQuilt + DlQuiltListLoader.Start(IsForceRestart:=True) + ItemQuilt.Checked = True + Case FormMain.PageSubType.DownloadCompFavorites + If FrmDownloadCompFavorites IsNot Nothing Then FrmDownloadCompFavorites.PageLoaderRestart() + ItemFavorites.Checked = True End Select Hint("正在刷新……", Log:=False) End Sub @@ -171,6 +203,7 @@ ItemClient.Visibility = Visibility.Visible ItemOptiFine.Visibility = Visibility.Visible ItemFabric.Visibility = Visibility.Visible + ItemQuilt.Visibility = Visibility.Visible ItemForge.Visibility = Visibility.Visible ItemNeoForge.Visibility = Visibility.Visible ItemLiteLoader.Visibility = Visibility.Visible @@ -192,6 +225,7 @@ ItemOptiFine.Visibility = Visibility.Collapsed ItemNeoForge.Visibility = Visibility.Collapsed ItemFabric.Visibility = Visibility.Collapsed + ItemQuilt.Visibility = Visibility.Collapsed ItemForge.Visibility = Visibility.Collapsed ItemLiteLoader.Visibility = Visibility.Collapsed RunInThread( diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb index 113ff253..e9d83970 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadMod.xaml.vb @@ -106,8 +106,8 @@ Case LoadState.Failed Dim ErrorMessage As String = "" If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message - If ErrorMessage.Contains("不是有效的 json 文件") Then - Log("[Download] 下载的 Mod 列表 json 文件损坏,已自动重试", LogLevel.Debug) + If ErrorMessage.Contains("不是有效的 Json 文件") Then + Log("[Download] 下载的 Mod 列表 Json 文件损坏,已自动重试", LogLevel.Debug) PageLoaderRestart() End If End Select diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb index 6ec8a317..5f452689 100644 --- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadPack.xaml.vb @@ -67,8 +67,8 @@ Case LoadState.Failed Dim ErrorMessage As String = "" If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message - If ErrorMessage.Contains("不是有效的 json 文件") Then - Log("[Download] 下载的整合包列表 json 文件损坏,已自动重试", LogLevel.Debug) + If ErrorMessage.Contains("不是有效的 Json 文件") Then + Log("[Download] 下载的整合包列表 Json 文件损坏,已自动重试", LogLevel.Debug) PageLoaderRestart() End If End Select diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml new file mode 100644 index 00000000..92cc5ca9 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml.vb new file mode 100644 index 00000000..09d58230 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadQuilt.xaml.vb @@ -0,0 +1,32 @@ +Public Class PageDownloadQuilt + + Private Sub LoaderInit() Handles Me.Initialized + PageLoaderInit(Load, PanLoad, CardVersions, CardTip, DlQuiltListLoader, AddressOf Load_OnFinish) + End Sub + Private Sub Init() Handles Me.Loaded + PanBack.ScrollToHome() + End Sub + + Private Sub Load_OnFinish() + '结果数据化 + Try + Dim Versions As JArray = DlQuiltListLoader.Output.Value("installer") + PanVersions.Children.Clear() + For Each Version In Versions + PanVersions.Children.Add(QuiltDownloadListItem(Version, AddressOf Quilt_Selected)) + Next + CardVersions.Title = "版本列表 (" & Versions.Count & ")" + Catch ex As Exception + Log(ex, "可视化 Quilt 版本列表出错", LogLevel.Feedback) + End Try + End Sub + + Private Sub Quilt_Selected(sender As MyListItem, e As EventArgs) + McDownloadQuiltLoaderSave(sender.Tag) + End Sub + + Private Sub BtnWeb_Click(sender As Object, e As EventArgs) Handles BtnWeb.Click + OpenWebsite("https://quiltmc.org") + End Sub + +End Class diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml new file mode 100644 index 00000000..6ce439f2 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml.vb new file mode 100644 index 00000000..41c5c7bc --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadResourcePack.xaml.vb @@ -0,0 +1,124 @@ +Public Class PageDownloadResourcePack + + Public Const PageSize = 40 + + '加载器信息 + Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject ResourcePack", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000} + Public Shared Storage As New CompProjectStorage + Public Shared Page As Integer = 0 + Private Sub PageDownloadResourcePack_Inited(sender As Object, e As EventArgs) Handles Me.Initialized + PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput) + If McVersionHighest = -1 Then McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1))) + End Sub + Private Shared Function LoaderInput() As CompProjectRequest + Dim Request As New CompProjectRequest(CompType.ResourcePack, Storage, (Page + 1) * PageSize) + If FrmDownloadResourcePack IsNot Nothing Then + With Request + .SearchText = FrmDownloadResourcePack.TextSearchName.Text + .GameVersion = If(FrmDownloadResourcePack.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing, + If(FrmDownloadResourcePack.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadResourcePack.TextSearchVersion.Text.Contains("w"), FrmDownloadResourcePack.TextSearchVersion.Text, Nothing)) + .Tag = FrmDownloadResourcePack.ComboSearchTag.SelectedItem.Tag + .Source = CType(Val(FrmDownloadResourcePack.ComboSearchSource.SelectedItem.Tag), CompSourceType) + End With + End If + Return Request + End Function + + '结果 UI 化 + Private Sub Load_OnFinish() + Try + Log($"[Comp] 开始可视化资源包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页") + '列表项 + PanProjects.Children.Clear() + For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1) + PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, False)) + Next + '页码 + CardPages.Visibility = If(Storage.Results.Count > 40 OrElse + Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal, + Visibility.Visible, Visibility.Collapsed) + LabPage.Text = Page + 1 + BtnPageFirst.IsEnabled = Page > 1 + BtnPageFirst.Opacity = If(Page > 1, 1, 0.2) + BtnPageLeft.IsEnabled = Page > 0 + BtnPageLeft.Opacity = If(Page > 0, 1, 0.2) + Dim IsRightEnabled As Boolean = '由于 WPF 的未知 bug,读取到的 IsEnabled 可能是错误的值(#3319) + Storage.Results.Count > PageSize * (Page + 1) OrElse + Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal + BtnPageRight.IsEnabled = IsRightEnabled + BtnPageRight.Opacity = If(IsRightEnabled, 1, 0.2) + '错误信息 + If Storage.ErrorMessage Is Nothing Then + HintError.Visibility = Visibility.Collapsed + Else + HintError.Visibility = Visibility.Visible + HintError.Text = Storage.ErrorMessage + End If + '强制返回顶部 + PanBack.ScrollToTop() + Catch ex As Exception + Log(ex, "可视化资源包列表出错", LogLevel.Feedback) + End Try + End Sub + + '自动重试 + Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged + Select Case Loader.State + Case LoadState.Failed + Dim ErrorMessage As String = "" + If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message + If ErrorMessage.Contains("不是有效的 json 文件") Then + Log("[Download] 下载的资源包列表 json 文件损坏,已自动重试", LogLevel.Debug) + PageLoaderRestart() + End If + End Select + End Sub + + '切换页码 + + Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click + ChangePage(0) + End Sub + Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click + ChangePage(Page - 1) + End Sub + Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click + ChangePage(Page + 1) + End Sub + Private Sub ChangePage(NewPage As Integer) + CardPages.IsEnabled = False + Page = NewPage + FrmMain.BackToTop() + Log($"[Download] 资源包切换到第 {Page + 1} 页") + RunInThread(Sub() + Thread.Sleep(100) '等待向上滚的动画结束 + RunInUi(Sub() CardPages.IsEnabled = True) + Loader.Start() + End Sub) + End Sub + +#Region "搜索" + + '搜索按钮 + Private Sub StartNewSearch() Handles BtnSearchRun.Click + Page = 0 + If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311) + Loader.Start() + End Sub + Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown + If e.Key = Key.Enter Then StartNewSearch() + End Sub + + '重置按钮 + Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click + TextSearchName.Text = "" + TextSearchVersion.Text = "全部 (也可自行输入)" + TextSearchVersion.SelectedIndex = 0 + ComboSearchSource.SelectedIndex = 0 + ComboSearchTag.SelectedIndex = 0 + Loader.LastFinishedTime = 0 '要求强制重新开始 + End Sub + +#End Region + +End Class diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml new file mode 100644 index 00000000..67b3a07a --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml.vb new file mode 100644 index 00000000..13a58c9f --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadShader.xaml.vb @@ -0,0 +1,122 @@ +Public Class PageDownloadShader + + Public Const PageSize = 40 + + '加载器信息 + Public Shared Loader As New LoaderTask(Of CompProjectRequest, Integer)("CompProject Shader", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000} + Public Shared Storage As New CompProjectStorage + Public Shared Page As Integer = 0 + Private Sub PageDownloadShader_Inited(sender As Object, e As EventArgs) Handles Me.Initialized + PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput) + If McVersionHighest = -1 Then McVersionHighest = Math.Max(McVersionHighest, Integer.Parse(CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString.Split(".")(1))) + End Sub + Private Shared Function LoaderInput() As CompProjectRequest + Dim Request As New CompProjectRequest(CompType.Shader, Storage, (Page + 1) * PageSize) + If FrmDownloadShader IsNot Nothing Then + With Request + .SearchText = FrmDownloadShader.TextSearchName.Text + .GameVersion = If(FrmDownloadShader.TextSearchVersion.Text = "全部 (也可自行输入)", Nothing, + If(FrmDownloadShader.TextSearchVersion.Text.Contains(".") OrElse FrmDownloadShader.TextSearchVersion.Text.Contains("w"), FrmDownloadShader.TextSearchVersion.Text, Nothing)) + .Tag = FrmDownloadShader.ComboSearchTag.SelectedItem.Tag + .Source = CType(Val(FrmDownloadShader.ComboSearchSource.SelectedItem.Tag), CompSourceType) + End With + End If + Return Request + End Function + + '结果 UI 化 + Private Sub Load_OnFinish() + Try + Log($"[Comp] 开始可视化光影包列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页") + '列表项 + PanProjects.Children.Clear() + For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1) + PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, False)) + Next + '页码 + CardPages.Visibility = If(Storage.Results.Count > 40 OrElse + Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal, + Visibility.Visible, Visibility.Collapsed) + LabPage.Text = Page + 1 + BtnPageFirst.IsEnabled = Page > 1 + BtnPageFirst.Opacity = If(BtnPageFirst.IsEnabled, 1, 0.2) + BtnPageLeft.IsEnabled = Page > 0 + BtnPageLeft.Opacity = If(BtnPageLeft.IsEnabled, 1, 0.2) + BtnPageRight.IsEnabled = Storage.Results.Count > PageSize * (Page + 1) OrElse + Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal + BtnPageRight.Opacity = If(BtnPageRight.IsEnabled, 1, 0.2) + '错误信息 + If Storage.ErrorMessage Is Nothing Then + HintError.Visibility = Visibility.Collapsed + Else + HintError.Visibility = Visibility.Visible + HintError.Text = Storage.ErrorMessage + End If + '强制返回顶部 + PanBack.ScrollToTop() + Catch ex As Exception + Log(ex, "可视化光影包列表出错", LogLevel.Feedback) + End Try + End Sub + + '自动重试 + Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged + Select Case Loader.State + Case LoadState.Failed + Dim ErrorMessage As String = "" + If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message + If ErrorMessage.Contains("不是有效的 JSON 文件") Then + Log("[Download] 下载的光影包列表 JSON 文件损坏,已自动重试", LogLevel.Debug) + PageLoaderRestart() + End If + End Select + End Sub + + '切换页码 + + Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click + ChangePage(0) + End Sub + Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click + ChangePage(Page - 1) + End Sub + Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click + ChangePage(Page + 1) + End Sub + Private Sub ChangePage(NewPage As Integer) + CardPages.IsEnabled = False + Page = NewPage + FrmMain.BackToTop() + Log($"[Download] 光影包切换到第 {Page + 1} 页") + RunInThread(Sub() + Thread.Sleep(100) '等待向上滚的动画结束 + RunInUi(Sub() CardPages.IsEnabled = True) + Loader.Start() + End Sub) + End Sub + +#Region "搜索" + + '搜索按钮 + Private Sub StartNewSearch() Handles BtnSearchRun.Click + Page = 0 + If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311) + Loader.Start() + End Sub + Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown + If e.Key = Key.Enter Then StartNewSearch() + End Sub + + '重置按钮 + Private Sub BtnSearchReset_Click(sender As Object, e As EventArgs) Handles BtnSearchReset.Click + TextSearchName.Text = "" + TextSearchVersion.Text = "全部 (也可自行输入)" + TextSearchVersion.SelectedIndex = 0 + ComboSearchSource.SelectedIndex = 0 + ComboSearchTag.SelectedIndex = 0 + Loader.LastFinishedTime = 0 '要求强制重新开始 + End Sub + +#End Region + +End Class diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb index 174f4bee..313db37c 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/MyMsgLogin.xaml.vb @@ -3,6 +3,8 @@ Private UserCode As String '需要用户在网页上输入的设备代码 Private DeviceCode As String '用于轮询的设备代码 Private Website As String '验证网页的网址 + Private OAuthUrl As String = "" 'OAuth 轮询验证地址 + #Region "弹窗" @@ -18,6 +20,7 @@ MyConverter = Converter ShapeLine.StrokeThickness = GetWPFSize(1) Data = Converter.Content + OAuthUrl = Converter.AuthUrl Init() Catch ex As Exception Log(ex, "登录弹窗初始化失败", LogLevel.Hint) @@ -82,13 +85,20 @@ Private Sub Init() UserCode = Data("user_code") DeviceCode = Data("device_code") - Website = Data("verification_uri") + If Data("verification_uri_complete") IsNot Nothing Then + Website = Data("verification_uri_complete") + LabCaption.Text = $"登录网页将自动开启,授权码将自动填充。" & vbCrLf & vbCrLf & + $"如果网络环境不佳,网页可能一直加载不出来,届时请使用 VPN 并重试。" & vbCrLf & + $"如果没有自动填充,请在页面内粘贴此授权码 {UserCode} (将自动复制)" & vbCrLf & + $"你也可以用其他设备打开 {Website} 并输入授权码。" + Else + Website = Website = Data("verification_uri") + LabCaption.Text = $"登录网页将自动开启,请在网页中输入授权码 {UserCode}(将自动复制)。" & vbCrLf & vbCrLf & + $"如果网络环境不佳,网页可能一直加载不出来,届时请使用 VPN 并重试。" & vbCrLf & + $"你也可以用其他设备打开 {Website} 并输入上述授权码。" + End If '设置 UI LabTitle.Text = "登录 Minecraft" - LabCaption.Text = - $"登录网页将自动开启,请在网页中输入 {UserCode}(已自动复制)。" & vbCrLf & vbCrLf & - $"如果网络环境不佳,网页可能一直加载不出来,届时请使用 VPN 并重试。" & vbCrLf & - $"你也可以用其他设备打开 {Website} 并输入上述代码。" Btn1.EventData = Website Btn2.EventData = UserCode '启动工作线程 @@ -105,21 +115,29 @@ Dim UnknownFailureCount As Integer = 0 Do While Not MyConverter.IsExited Try + Dim Scope As String = "" + Dim ClientId As String = "" + If OAuthUrl.ToLower().Contains("microsoftonline.com") Then + ClientId = OAuthClientId + Scope = "scope=XboxLive.signin%20offline_access" + Else + ClientId = LittleSkinClientId + End If Dim Result = NetRequestOnce( - "https://login.microsoftonline.com/consumers/oauth2/v2.0/token", "POST", + OAuthUrl, "POST", "grant_type=urn:ietf:params:oauth:grant-type:device_code" & "&" & - "client_id=" & OAuthClientId & "&" & + "client_id=" & ClientId & "&" & "device_code=" & DeviceCode & "&" & - "scope=XboxLive.signin%20offline_access", + Scope, "application/x-www-form-urlencoded", 5000 + UnknownFailureCount * 5000, MakeLog:=False) '获取结果 Dim ResultJson As JObject = GetJson(Result) McLaunchLog($"令牌过期时间:{ResultJson("expires_in")} 秒") Hint("网页登录成功!", HintType.Finish) - Finished({ResultJson("access_token").ToString, ResultJson("refresh_token").ToString}) + Finished(ResultJson) Return Catch ex As Exception - If ex.Message.Contains("authorization_declined") Then + If ex.Message.Contains("authorization_declined") Or ex.Message.Contains("access_denied") Then Finished(New Exception("$你拒绝了 PCL 申请的权限……")) Return ElseIf ex.Message.Contains("expired_token") Then diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml index 1cc6a708..ae8d7f5e 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml @@ -8,7 +8,7 @@ - + @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb index 595aecb2..296f74ac 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb @@ -5,14 +5,10 @@ PanBack.ScrollToHome() PanScroll = PanBack '不知道为啥不能在 XAML 设置 PanLog.Visibility = If(ModeDebug, Visibility.Visible, Visibility.Collapsed) - '快照版提示 -#If BETA Then - PanHint.Visibility = Visibility.Collapsed -#Else + '社区版提示 PanHint.Visibility = If(ThemeCheckGold(), Visibility.Collapsed, Visibility.Visible) - LabHint1.Text = "快照版包含尚未正式发布的测试功能,仅用于赞助者本人尝鲜。请不要发给其他人或者用来制作整合包哦!" - LabHint2.Text = $"若已累积赞助¥23.33,在爱发电私信发送 {vbLQ}解锁码{vbRQ} 即可永久隐藏此提示。" -#End If + LabHint1.Text = "社区版包含尚未在官方主线版本发布的测试功能,仅用于尝鲜。请不要向官方仓库反馈社区版的问题哦!" + LabHint2.Text = $"若要永久隐藏此提示,请自行 Clone 代码并删除此提示相关内容。" End Sub '暂时关闭快照版提示 diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb index e9ae125f..fa10925b 100644 --- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginAuth.xaml.vb @@ -38,6 +38,7 @@ ''' 当前页面的登录信息是否有效。 ''' Public Shared Function IsVaild(LoginData As McLoginServer) As String + If Not LittleSkinClientId = "" Then Return "" If LoginData.UserName = "" Then Return "账号不能为空!" If LoginData.Password = "" Then Return "密码不能为空!" Return "" diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml index 81421633..f0f925c9 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml @@ -18,7 +18,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml.vb index 237bd2ad..482cd7bf 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml.vb @@ -10,7 +10,7 @@ If IsLoaded Then Exit Sub IsLoaded = True - ItemAboutPcl.Info = ItemAboutPcl.Info.Replace("%VERSION%", VersionDisplayName).Replace("%VERSIONCODE%", VersionCode).Replace("%BRANCH%", VersionBranchCode) + ItemAboutPcl.Info = ItemAboutPcl.Info.Replace("%VERSION%", VersionBaseName).Replace("%VERSIONCODE%", VersionCode).Replace("%BRANCH%", VersionBranchName).Replace("%COMMIT_HASH%", CommitHash).Replace("%UPSTREAM_VERSION%", UpstreamVersion) #If DEBUG Then BtnDonateDonate.Visibility = Visibility.Collapsed BtnDonateOutput.Visibility = Visibility.Visible diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml new file mode 100644 index 00000000..64c845a0 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml.vb new file mode 100644 index 00000000..feacc68c --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherFeedback.xaml.vb @@ -0,0 +1,137 @@ +Public Class PageOtherFeedback + + Public Class Feedback + Public Property User As String + Public Property Title As String + Public Property Time As Date + Public Property Content As String + Public Property Url As String + Public Property ID As String + Public Property Tags As New List(Of String) + End Class + + Enum TagID As Int64 + NewIssue = 4365827012 + Bug = 4365944566 + Improve = 4365949262 + Processing = 4365819896 + WaitingResponse = 4365816377 + Completed = 4365809832 + Decline = 4365654603 + NewFeture = 4365949953 + Ignored = 4365654601 + Duplicate = 4365654597 + End Enum + + Private Shadows IsLoaded As Boolean = False + Private Sub PageOtherFeedback_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded + PageLoaderInit(Load, PanLoad, PanContent, PanInfo, Loader, AddressOf RefreshList, AddressOf LoaderInput) + '重复加载部分 + PanBack.ScrollToHome() + '非重复加载部分 + If IsLoaded Then Exit Sub + IsLoaded = True + + End Sub + + Public Shared Loader As New LoaderTask(Of String, List(Of Feedback))("FeedbackList", AddressOf FeedbackListGet, AddressOf LoaderInput) + + Private Shared Function LoaderInput() As String + Return "" ' awa? + End Function + + Public Shared Sub FeedbackListGet(Task As LoaderTask(Of String, List(Of Feedback))) + Dim list As JArray + list = NetGetCodeByRequestRetry("https://api.github.com/repos/Hex-Dragon/PCL2/issues?state=all&sort=created&per_page=200", BackupUrl:="https://api.kkgithub.com/repos/Hex-Dragon/PCL2/issues?state=all&sort=created&per_page=200", IsJson:=True, UseBrowserUserAgent:=True) ' 获取近期 200 条数据就够了 + If list Is Nothing Then Throw New Exception("无法获取到内容") + Dim res As List(Of Feedback) = New List(Of Feedback) + For Each i As JObject In list + Dim item As Feedback = New Feedback With {.Title = i("title").ToString(), + .Url = i("html_url").ToString(), + .Content = i("body").ToString(), + .Time = Date.Parse(i("created_at").ToString()), + .User = i("user")("login").ToString(), + .ID = i("number")} + Dim thisTags As JArray = i("labels") + For Each thisTag As JObject In thisTags + item.Tags.Add(thisTag("id")) + Next + res.Add(item) + Next + Task.Output = res + End Sub + + Public Sub RefreshList() + PanListCompleted.Children.Clear() + PanListProcessing.Children.Clear() + PanListWaitingResponse.Children.Clear() + PanListDecline.Children.Clear() + For Each item In Loader.Output + Dim ele As New MyListItem With {.Title = item.Title, .Type = MyListItem.CheckType.Clickable} + Dim StatusDesc As String = "???" + If item.Tags.Contains(TagID.Duplicate) Then Continue For + If item.Tags.Contains(TagID.NewIssue) Then + ele.Logo = PathImage & "Blocks/Grass.png" + StatusDesc = "未查看" + End If + If item.Tags.Contains(TagID.Processing) Then + ele.Logo = PathImage & "Blocks/CommandBlock.png" + StatusDesc = "处理中" + End If + If item.Tags.Contains(TagID.Bug) Then + ele.Logo = PathImage & "Blocks/RedstoneBlock.png" + StatusDesc = "处理中-Bug" + End If + If item.Tags.Contains(TagID.Improve) Then + ele.Logo = PathImage & "Blocks/Anvil.png" + StatusDesc = "处理中-优化" + End If + If item.Tags.Contains(TagID.Completed) Then + ele.Logo = PathImage & "Blocks/GrassPath.png" + StatusDesc = "已完成" + End If + If item.Tags.Contains(TagID.WaitingResponse) Then + ele.Logo = PathImage & "Blocks/RedstoneLampOff.png" + StatusDesc = "等待提交者" + End If + If item.Tags.Contains(TagID.NewFeture) Then + ele.Logo = PathImage & "Blocks/Egg.png" + StatusDesc = "处理中-新功能" + End If + If item.Tags.Contains(TagID.Decline) Then + ele.Logo = PathImage & "Blocks/CobbleStone.png" + StatusDesc = "已拒绝" + End If + If item.Tags.Contains(TagID.Ignored) Then + ele.Logo = PathImage & "Blocks/CobbleStone.png" + StatusDesc = "已忽略" + End If + ele.Info = StatusDesc & " | " & item.User & " | " & item.Time + AddHandler ele.Click, Sub() + MyMsgBox($"提交者:{item.User}({GetTimeSpanString(item.Time - DateTime.Now, False)}){vbCrLf}状态:{StatusDesc}{vbCrLf}{vbCrLf}{item.Content}", "#" & item.ID & " " & item.Title, Button2:="查看详情", Button2Action:=Sub() + OpenWebsite(item.Url) + End Sub) + End Sub + If StatusDesc.StartsWithF("处理中") Then + PanListProcessing.Children.Add(ele) + ElseIf StatusDesc.Equals("等待提交者") Then + PanListWaitingResponse.Children.Add(ele) + ElseIf StatusDesc.Equals("已完成") Then + PanListCompleted.Children.Add(ele) + ElseIf StatusDesc.Equals("未查看") Then + PanListNewIssue.Children.Add(ele) + ElseIf StatusDesc.Equals("已拒绝") Then + PanListDecline.Children.Add(ele) + End If + PanContentDecline.Visibility = If(PanListDecline.Children.Count.Equals(0), Visibility.Collapsed, Visibility.Visible) + PanContentCompleted.Visibility = If(PanListCompleted.Children.Count.Equals(0), Visibility.Collapsed, Visibility.Visible) + PanContentNewIssue.Visibility = If(PanListNewIssue.Children.Count.Equals(0), Visibility.Collapsed, Visibility.Visible) + PanContentWaitingResponse.Visibility = If(PanListWaitingResponse.Children.Count.Equals(0), Visibility.Collapsed, Visibility.Visible) + PanContentProcessing.Visibility = If(PanListProcessing.Children.Count.Equals(0), Visibility.Collapsed, Visibility.Visible) + Next + End Sub + + Private Sub Feedback_Click(sender As Object, e As MouseButtonEventArgs) + PageOtherLeft.TryFeedback() + End Sub +End Class diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb index 59df7428..e6c53af9 100644 --- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherLeft.xaml.vb @@ -49,7 +49,7 @@ ''' ''' 勾选事件改变页面。 ''' - Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemAbout.Check, ItemHelp.Check, ItemTest.Check + Private Sub PageCheck(sender As MyListItem, e As RouteEventArgs) Handles ItemAbout.Check, ItemHelp.Check, ItemTest.Check, ItemFeedback.Check, ItemVote.Check '尚未初始化控件属性时,sender.Tag 为 Nothing,会导致切换到页面 0 '若使用 IsLoaded,则会导致模拟点击不被执行(模拟点击切换页面时,控件的 IsLoaded 为 False) If sender.Tag IsNot Nothing Then PageChange(Val(sender.Tag)) @@ -67,6 +67,12 @@ Case FormMain.PageSubType.OtherTest If FrmOtherTest Is Nothing Then FrmOtherTest = New PageOtherTest Return FrmOtherTest + Case FormMain.PageSubType.OtherFeedback + If FrmOtherFeedback Is Nothing Then FrmOtherFeedback = New PageOtherFeedback + Return FrmOtherFeedback + Case FormMain.PageSubType.OtherVote + If FrmOtherVote Is Nothing Then FrmOtherVote = New PageOtherVote + Return FrmOtherVote Case Else Throw New Exception("未知的更多子页面种类:" & ID) End Select @@ -125,12 +131,7 @@ End Sub '打开网页 - Private Sub TryFeedback(sender As Object, e As RouteEventArgs) Handles ItemFeedback.Changed - If Not ItemFeedback.Checked Then Exit Sub - TryFeedback() - e.Handled = True - End Sub - Public Shared Sub TryFeedback() + Public Shared Sub TryFeedback() 'Handles ItemFeedback.Click If Not CanFeedback(True) Then Exit Sub Select Case MyMsgBox("在提交新反馈前,建议先搜索反馈列表,以避免重复提交。" & vbCrLf & "如果无法打开该网页,请尝试使用加速器或 VPN。", "反馈", "提交新反馈", "查看反馈列表", "取消") @@ -140,12 +141,7 @@ OpenWebsite("https://github.com/Hex-Dragon/PCL2/issues/") End Select End Sub - Private Sub TryVote(sender As Object, e As RouteEventArgs) Handles ItemVote.Changed - If Not ItemVote.Checked Then Exit Sub - TryVote() - e.Handled = True - End Sub - Public Shared Sub TryVote() + Public Shared Sub TryVote() 'Handles ItemVote.Click If MyMsgBox("是否要打开新功能投票网页?" & vbCrLf & "如果无法打开该网页,请尝试使用加速器或 VPN。", "新功能投票", "打开", "取消") = 2 Then Exit Sub OpenWebsite("https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Adate_created") diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml new file mode 100644 index 00000000..5822b038 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml.vb b/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml.vb new file mode 100644 index 00000000..aa86b9e5 --- /dev/null +++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherVote.xaml.vb @@ -0,0 +1,93 @@ +Imports System.Net.Http +Imports System.Text.RegularExpressions +Imports System.Windows.Forms +Imports System.Windows.Forms.LinkLabel +Imports System.Xml +Imports NAudio.Gui + +Public Class PageOtherVote + Public Class Vote + Public Property Title As String + Public Property Url As String + Public Property Time As Date + Public Property Vote As String + End Class + + Private Shadows IsLoaded As Boolean = False + Private Sub PageOtherFeedback_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded + PageLoaderInit(Load, PanLoad, PanContent, PanInfo, Loader, AddressOf LoadList, AddressOf LoaderInput) + '重复加载部分 + PanBack.ScrollToHome() + + '非重复加载部分 + If IsLoaded Then Exit Sub + IsLoaded = True + + End Sub + Public Shared Loader As New LoaderTask(Of String, List(Of Vote))("VoteList", AddressOf VoteListGet, AddressOf LoaderInput) + + Private Shared Function LoaderInput() As String + Return "" ' awa? + End Function + + Public Shared Sub VoteListGet(Task As LoaderTask(Of String, List(Of Vote))) + Dim content = NetGetCodeByRequestRetry("https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=is%3Aopen+category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Atop", BackupUrl:="https://kkgithub.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8?discussions_q=is%3Aopen+category%3A%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8+sort%3Atop", UseBrowserUserAgent:=True) + If content Is Nothing Then Throw New Exception("空内容") + + Dim pattern As String = "
(.*?)" + Dim matches As MatchCollection = Regex.Matches(content, pattern, RegexOptions.Singleline) + Dim contentList As New List(Of String) + + For Each match As Match In matches + If match.Success Then + contentList.Add(match.Groups(1).Value) + End If + Next + + Dim res As List(Of Vote) = New List(Of Vote) + + For Each c In contentList + Dim item As Vote = New Vote() + ' 抓取标题和网址 + pattern = "]*?\bdata-hovercard-type=""discussion""[^>]*?href=""(.*?)""[^>]*?>(.*?)" + Dim SingleMatch As Match = Regex.Match(c, pattern, RegexOptions.Singleline) + ' 使用正则表达式匹配 + If SingleMatch.Success Then + ' 输出匹配到的href和文本内容 + item.Title = SingleMatch.Groups(2).Value.Trim() + item.Url = "https://github.com/" & SingleMatch.Groups(1).Value + End If + ' 抓取时间 + pattern = " 1 Then + If If(IsLiteExists, 1, 0) + If(IsForgeExists, 1, 0) + If(IsFabricExists, 1, 0) + If(IsNeoForgeExists, 1, 0) + If(IsQuiltExists, 1, 0) > 1 Then CardName = "可安装 Mod" ElseIf IsForgeExists Then CardName = "Forge 版本" @@ -55,6 +57,8 @@ CardName = "NeoForge 版本" ElseIf IsLiteExists Then CardName = "LiteLoader 版本" + ElseIf IsQuiltExists Then + CardName = "Quilt 版本" Else CardName = "Fabric 版本" End If diff --git a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb index 6dbe50c4..dcb95f8e 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb @@ -66,8 +66,12 @@ {"CacheAuthServerServer", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, {"CacheAuthServerName", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, {"CacheAuthServerRegister", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, + {"CacheAuthRefresh", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, + {"CacheAuthIDToken", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, + {"CacheAuthAPIToken", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, {"CacheDownloadFolder", New SetupEntry("", Source:=SetupSource.Registry)}, {"CacheJavaListVersion", New SetupEntry(0, Source:=SetupSource.Registry)}, + {"CompFavorites", New SetupEntry("[]", Source:=SetupSource.Registry)}, {"LoginRemember", New SetupEntry(True, Source:=SetupSource.Registry, Encoded:=True)}, {"LoginLegacyName", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)}, {"LoginMsJson", New SetupEntry("{}", Source:=SetupSource.Registry, Encoded:=True)}, '{UserName: OAuthToken, ...} @@ -183,9 +187,7 @@ {"VersionServerNide", New SetupEntry("", Source:=SetupSource.Version)}, {"VersionServerAuthRegister", New SetupEntry("", Source:=SetupSource.Version)}, {"VersionServerAuthName", New SetupEntry("", Source:=SetupSource.Version)}, - {"VersionServerAuthServer", New SetupEntry("", Source:=SetupSource.Version)} - } - + {"VersionServerAuthServer", New SetupEntry("", Source:=SetupSource.Version)}} #Region "基础" Private Enum SetupSource diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml index 8b692a43..7d66af79 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml @@ -251,7 +251,7 @@ + ToolTip="警告:强烈推荐在版本设置中关闭特定版本的校验,而不是在这里关闭所有版本的校验! 完全不更改 assets;不校验 libraries、第三方登录库与版本主 Jar 文件是否被修改。 如果你没有修改相关文件,请勿勾选此项。" /> diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb index fc00a3d7..44edb287 100644 --- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb @@ -7,7 +7,7 @@ '重复加载部分 PanBack.ScrollToHome() -#If BETA Then +#If RELEASE Then PanDonate.Visibility = Visibility.Collapsed #Else PanDonate.Visibility = Visibility.Visible @@ -177,7 +177,7 @@ Dim ServerContent As String = ReadFile(PathTemp & "Cache\Notice.cfg") If ServerContent.Split("|").Count < 3 Then Return Nothing '确认是否为最新 -#If BETA Then +#If RELEASE Then Dim NewVersionCode As Integer = ServerContent.Split("|")(2) #Else Dim NewVersionCode As Integer = ServerContent.Split("|")(1) diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml index d461c047..d11a6cab 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml @@ -37,6 +37,7 @@ + diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb index 133138ab..6487b34e 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb @@ -121,7 +121,7 @@ Try JsonObject = GetJson(ReadFile(PageVersionLeft.Version.Path & PageVersionLeft.Version.Name & ".json")) Catch ex As Exception - Log(ex, "重命名读取 json 时失败") + Log(ex, "重命名读取 Json 时失败") JsonObject = PageVersionLeft.Version.JsonObject End Try '重命名主文件夹 @@ -164,7 +164,7 @@ JsonObject("id") = NewName WriteFile(NewPath & NewName & ".json", JsonObject.ToString) Catch ex As Exception - Log(ex, "重命名版本 json 失败") + Log(ex, "重命名版本 Json 失败") End Try End If '刷新与提示 diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml index 378e63e4..44686fc9 100644 --- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml +++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml @@ -220,7 +220,7 @@ + ToolTip="完全不更改 Assets;不校验 Libraries、第三方登录库与版本主 Jar 文件是否被修改。 如果你没有修改相关文件,请勿勾选此项。" /> diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj index 414d9f22..f217819b 100644 --- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj +++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj @@ -192,9 +192,6 @@ MyIconTextButton.xaml - - - @@ -239,6 +236,15 @@ MyCompItem.xaml + + PageDownloadResourcePack.xaml + + + PageDownloadQuilt.xaml + + + PageDownloadCompFavorites.xaml + PageDownloadMod.xaml @@ -257,6 +263,9 @@ PageDownloadPack.xaml + + PageDownloadShader.xaml + MyMsgLogin.xaml @@ -281,6 +290,9 @@ PageLinkHiper.xaml + + PageOtherVote.xaml + PageOtherHelpDetail.xaml @@ -290,6 +302,9 @@ PageLinkLeft.xaml + + PageOtherFeedback.xaml + PageVersionLeft.xaml @@ -491,7 +506,6 @@ FormMain.xaml Code - MSBuild:Compile Designer @@ -500,6 +514,18 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -524,6 +550,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -561,6 +591,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -589,6 +623,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -942,7 +980,7 @@ - + diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml index 21ca4593..7f81da63 100644 --- a/Plain Craft Launcher 2/Resources/Custom.xaml +++ b/Plain Craft Launcher 2/Resources/Custom.xaml @@ -206,6 +206,8 @@ + + diff --git a/README.md b/README.md index 99e80765..6fec4b31 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ -# Plain Craft Launcher +# PCL Community Edition -[![Stars](https://img.shields.io/github/stars/Hex-Dragon/PCL2?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=Stars&labelColor=444444&color=eac54f)](https://github.com/Hex-Dragon/PCL2/) -[![Issues](https://img.shields.io/github/issues/Hex-Dragon/PCL2?style=flat&label=Issues&labelColor=444444&color=1F883D)](https://github.com/Hex-Dragon/PCL2/issues) +[![Stars](https://img.shields.io/github/stars/PCL-Community/PCL2-CE?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=Stars&labelColor=444444&color=eac54f)](https://github.com/PCL-Community/PCL2-CE/) +[![Issues](https://img.shields.io/github/issues/PCL-Community/PCL2-CE?style=flat&label=Issues&labelColor=444444&color=1F883D)](https://github.com/PCL-Community/PCL2-CE/issues) [![哔哩哔哩](https://img.shields.io/badge/动态-BiliBili-00A4DB?style=flat&labelColor=444444&logoSize=auto)](https://space.bilibili.com/11343203/dynamic) [![爱发电](https://img.shields.io/badge/赞助-爱发电-946ce6?style=flat&labelColor=444444&logoSize=auto)](https://afdian.com/@LTCat) -包含 PCL 的大部分源代码,包括 UI 库、动画模块、下载模块、Minecraft 启动模块等!
-代码并不是实时更新的,它会在每次 PCL 发布更新时同步一次。
-非常感谢社区的大力支持! +基于 PCL2 开源代码二次开发的社区版本,包括了主线暂未制作的功能和改进!
+代码会在主线发布更新时与主线同步一次,但来自社区的功能会保持自己的更新节奏。
+同时需注意,社区版的版本号与主线并非严格对应关系。
+欢迎大家来用用看!
### 相关资源 -- [PCL 下载](https://afdian.com/p/0164034c016c11ebafcb52540025c377):下载可免费使用的正式版 PCL! -- [PCL 功能投票](https://github.com/Hex-Dragon/PCL2/discussions/2):来参加投票吧,开发者会优先处理票数较高的项目! +- [PCL 社区版下载](https://github.com/PCL-Community/PCL2-CE/releases):下载来自本存储库的 PCL2 社区版! +- [PCL 官方存储库](https://github.com/Hex-Dragon/PCL2):PCL 的官方开源代码存储库 - [帮助文档库](https://github.com/LTCatt/PCL2Help):PCL 内置帮助文档的存储库 diff --git "a/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" "b/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" deleted file mode 100644 index 29be464e..00000000 Binary files "a/\346\234\200\346\226\260\346\255\243\345\274\217\347\211\210.zip" and /dev/null differ