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 = "(.*?)