Преглед на файлове

Merge pull request #32 from Hex-Dragon/main

u
薄奚梦灵 преди 1 година
родител
ревизия
effdf5a3a9

+ 12 - 1
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -927,6 +927,9 @@ Public Class FormMain
         DownloadLiteLoader = 9
         DownloadMod = 11
         DownloadPack = 12
+        DownloadResourcePack = 13
+        DownloadShader = 14
+        DownloadDataPack = 15
         SetupLaunch = 0
         SetupUI = 1
         SetupSystem = 2
@@ -963,8 +966,16 @@ Public Class FormMain
                         Return "Mod 下载 - " & Project.TranslatedName
                     Case CompType.ModPack
                         Return "整合包下载 - " & Project.TranslatedName
-                    Case Else 'CompType.ResourcePack
+                    Case CompType.DataPack
+                        If Project.IsMix Then
+                            Return "Mod / 数据包下载 - " & Project.TranslatedName
+                        Else
+                            Return "数据包下载 - " & Project.TranslatedName
+                        End If
+                    Case CompType.ResourcePack
                         Return "资源包下载 - " & Project.TranslatedName
+                    Case Else 'CompType.Shader
+                        Return "光影包下载 - " & Project.TranslatedName
                 End Select
             Case PageType.HelpDetail
                 Dim Entry As HelpEntry = Stack.Additional(0)

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

@@ -13,6 +13,14 @@
         ''' 资源包。
         ''' </summary>
         ResourcePack = 2
+        ''' <summary>
+        ''' 光影包。
+        ''' </summary>
+        Shader = 3
+        ''' <summary>
+        ''' 数据包。
+        ''' </summary>
+        DataPack = 4
     End Enum
     Public Enum CompModLoaderType
         'https://docs.curseforge.com/?http#tocS_ModLoaderType
@@ -114,6 +122,10 @@
         ''' </summary>
         Public ReadOnly Type As CompType
         ''' <summary>
+        ''' 是否为 Mod / 数据包融合工程。
+        ''' </summary>
+        Public ReadOnly IsMix As Boolean
+        ''' <summary>
         ''' 工程的短名。例如 technical-enchant。
         ''' </summary>
         Public ReadOnly Slug As String
@@ -279,8 +291,14 @@
                         Type = CompType.Mod
                     ElseIf Website.Contains("/modpacks/") Then
                         Type = CompType.ModPack
-                    Else
+                    ElseIf Website.Contains("/resourcepacks/") Then
+                        Type = CompType.ResourcePack
+                    ElseIf Website.Contains("/texture-packs/") Then
                         Type = CompType.ResourcePack
+                    ElseIf Website.Contains("/shaders/")
+                        Type = CompType.Shader
+                    Else
+                        Type = CompType.DataPack
                     End If
                     'Tags
                     Tags = New List(Of String)
@@ -328,7 +346,36 @@
                             Case 4480 : Tags.Add("基于地图")
                             Case 4481 : Tags.Add("轻量")
                             Case 4482 : Tags.Add("大型")
-                                'FUTURE: Res
+                        '数据包
+                            Case 6946 : Tags.Add("Mod 支持")
+                            Case 6947 : Tags.Add("杂项")
+                            Case 6948 : Tags.Add("冒险")
+                            Case 6949 : Tags.Add("幻想")
+                            Case 6950 : Tags.Add("支持库")
+                            Case 6951 : Tags.Add("科技")
+                            Case 6952 : Tags.Add("魔法")
+                            Case 6953 : Tags.Add("实用工具")
+                        '光影包
+                            Case 6553 : Tags.Add("写实")
+                            Case 6554 : Tags.Add("幻想")
+                            Case 6555 : Tags.Add("原版风")
+                        '资源包
+                            Case 5244 : Tags.Add("字体包")
+                            Case 5193 : Tags.Add("数据包")
+                            Case 399 : Tags.Add("蒸汽朋克")
+                            Case 396 : Tags.Add("128x")
+                            Case 398 : Tags.Add("512x 或更高")
+                            Case 397 : Tags.Add("256x")
+                            Case 405 : Tags.Add("其他")
+                            Case 395 : Tags.Add("64x")
+                            Case 400 : Tags.Add("仿真")
+                            Case 393 : Tags.Add("16x")
+                            Case 403 : Tags.Add("传统")
+                            Case 394 : Tags.Add("32x")
+                            Case 404 : Tags.Add("动态效果")
+                            Case 4465 : Tags.Add("模组支持")
+                            Case 402 : Tags.Add("中世纪")
+                            Case 401 : Tags.Add("现代")
                         End Select
                     Next
                     If Not Tags.Any() Then Tags.Add("杂项")
@@ -356,7 +403,13 @@
                         Case "mod" : Type = CompType.Mod
                         Case "modpack" : Type = CompType.ModPack
                         Case "resourcepack" : Type = CompType.ResourcePack
+                        Case "datapack" : Type = CompType.DataPack
+                        Case "shader" : Type = CompType.Shader
                     End Select
+                    If Data("categories").ToArray.Contains("datapack") Then 'Modrinth 上的数据包由于未知原因,返回的 project_type 为 mod,这里做兜底处理
+                        Type = CompType.DataPack
+                        IsMix = Data("categories").ToArray.Contains("forge") OrElse Data("categories").ToArray.Contains("fabric") OrElse Data("categories").ToArray.Contains("neoforge") OrElse Data("categories").ToArray.Contains("quilt")
+                    End If
                     'Tags & ModLoaders
                     Tags = New List(Of String)
                     ModLoaders = New List(Of CompModLoaderType)
@@ -404,7 +457,83 @@
                             Case "adventure" : Tags.Add("冒险")
                             Case "kitchen-sink" : Tags.Add("水槽包/大杂烩")
                             Case "lightweight" : Tags.Add("轻量")
-                                'FUTURE: Res
+                            '数据包
+                            Case "adventure" : Tags.Add("冒险")
+                            Case "cursed" : Tags.Add("Cursed")
+                            Case "decoration" : Tags.Add("装饰")
+                            Case "economy" : Tags.Add("经济")
+                            Case "equipment" : Tags.Add("装备")
+                            Case "food" : Tags.Add("食物")
+                            Case "game-mechanics" : Tags.Add("游戏机制")
+                            Case "library" : Tags.Add("支持库")
+                            Case "magic" : Tags.Add("魔法")
+                            Case "management" : Tags.Add("管理")
+                            Case "minigame" : Tags.Add("小游戏")
+                            Case "mobs" : Tags.Add("生物")
+                            Case "optimization" : Tags.Add("优化")
+                            Case "social" : Tags.Add("社交")
+                            Case "storage" : Tags.Add("存储")
+                            Case "technology" : Tags.Add("科技")
+                            Case "transportation" : Tags.Add("交通")
+                            Case "utility" : Tags.Add("实用工具")
+                            Case "worldgen" : Tags.Add("世界生成")
+                            '光影包
+                            Case "cartoon" : Tags.Add("卡通")
+                            Case "cursed" : Tags.Add("Cursed")
+                            Case "fantasy" : Tags.Add("幻想")
+                            Case "realistic" : Tags.Add("写实")
+                            Case "semi-realistic" : Tags.Add("半写实")
+                            Case "vanilla-like" : Tags.Add("原版风")
+
+                            Case "atmosphere" : Tags.Add("大气环境")
+                            Case "bloom" : Tags.Add("植被")
+                            Case "colored-lighting" : Tags.Add("光源着色")
+                            Case "foliage" : Tags.Add("树叶")
+                            Case "path-tracing" : Tags.Add("路径追踪")
+                            Case "pbr" : Tags.Add("PBR")
+                            Case "reflections" : Tags.Add("反射")
+                            Case "shadows" : Tags.Add("阴影")
+
+                            Case "potato" : Tags.Add("土豆画质")
+                            Case "low" : Tags.Add("低性能影响")
+                            Case "medium" : Tags.Add("中性能影响")
+                            Case "high" : Tags.Add("高性能影响")
+                            Case "screenshot" : Tags.Add("极致画质")
+
+                            Case "canvas" : Tags.Add("Canvas")
+                            Case "iris" : Tags.Add("Iris")
+                            Case "optifine" : Tags.Add("OptiFine")
+                            Case "vanilla" : Tags.Add("原版光影")
+                            '资源包
+                            Case "8x-" : Tags.Add("8x-")
+                            Case "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("杂项")
@@ -775,8 +904,12 @@ NoSubtitle:
                     Address += "&classId=6"
                 Case CompType.ModPack
                     Address += "&classId=4471"
+                Case CompType.DataPack
+                    Address += "&classId=6945"
+                Case CompType.Shader
+                    Address += "&classId=6552"
                 Case CompType.ResourcePack
-                    'FUTURE: Res
+                    Address += "&classId=12"
             End Select
             Address += "&categoryId=" & If(Tag = "", "0", Tag.BeforeFirst("/"))
             If ModLoader <> CompModLoaderType.Any Then Address += "&modLoaderType=" & CType(ModLoader, Integer)

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

@@ -115,9 +115,12 @@
                     TargetLoader = PageDownloadMod.Loader.Input.ModLoader
                 Case CompType.ModPack
                     TargetVersion = If(PageDownloadPack.Loader.Input.GameVersion, "")
+                Case CompType.DataPack
+                    TargetVersion = If(PageDownloadDataPack.Loader.Input.GameVersion, "")
+                Case CompType.Shader
+                    TargetVersion = If(PageDownloadShader.Loader.Input.GameVersion, "")
                 Case Else 'CompType.ResourcePack
-                    'FUTURE: Res
-                    TargetVersion = "" 'If(PageDownloadResource.Loader.Input.GameVersion, "")
+                    TargetVersion = If(PageDownloadResourcePack.Loader.Input.GameVersion, "")
             End Select
         End If
         If CType(sender.Tag, CompProject).Type <> CompType.Mod Then TargetLoader = CompModLoaderType.Any

+ 3 - 0
Plain Craft Launcher 2/Modules/ModMain.vb

@@ -405,6 +405,9 @@ EndHint:
     Public FrmDownloadFabric As PageDownloadFabric
     Public FrmDownloadMod As PageDownloadMod
     Public FrmDownloadPack As PageDownloadPack
+    Public FrmDownloadDataPack As PageDownloadDataPack
+    Public FrmDownloadShader As PageDownloadShader
+    Public FrmDownloadResourcePack As PageDownloadResourcePack
 
     '设置页面声明
     Public FrmSetupLeft As PageSetupLeft

+ 8 - 1
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb

@@ -289,7 +289,14 @@
         RunInNewThread(
         Sub()
             Try
-                Dim Desc As String = If(Project.Type = CompType.ModPack, "整合包", If(Project.Type = CompType.Mod, "Mod ", "资源包"))
+                Dim Desc As String = "资源"
+                Select Case Project.Type
+                    Case CompType.ModPack : Desc = "整合包"
+                    Case CompType.Mod : Desc = "Mod "
+                    Case CompType.ResourcePack : Desc = "资源包"
+                    Case CompType.DataPack : Desc = If(File.FileName.EndsWith(".jar"), "Mod ", "数据包") '有些项目同时包含 Mod 版本和数据包版本
+                    Case CompType.Shader : Desc = "光影包"
+                End Select
                 '确认默认保存位置
                 Dim DefaultFolder As String = Nothing
                 If Project.Type = CompType.Mod Then

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

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

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

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

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

@@ -71,21 +71,44 @@
             <TextBlock Text="社区资源" Margin="13,30,5,4" Opacity="0.6" FontSize="12" />
             <local:MyListItem x:Name="ItemMod" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="11" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="Mod"
                           LogoScale="0.97" Logo="M789.504 912.896h-195.072a35.328 35.328 0 0 1-34.816-35.328v-77.312a39.936 39.936 0 0 0-40.448-39.936H481.28a40.448 40.448 0 0 0-40.448 39.936v77.312a34.816 34.816 0 0 1-34.816 35.328H204.8a41.984 41.984 0 0 1-40.448-42.496v-200.192a35.328 35.328 0 0 1 35.328-35.328h72.704a39.936 39.936 0 0 0 39.936-39.936v-37.888a39.936 39.936 0 0 0-39.936-40.448H199.68a35.328 35.328 0 0 1-35.328-35.328V287.744A41.984 41.984 0 0 1 204.8 245.76h176.64v-32.768a102.4 102.4 0 0 1 102.4-102.4h33.792a102.4 102.4 0 0 1 102.4 102.4v32.768h170.496a41.984 41.984 0 0 1 41.984 41.984V460.8h28.672a102.4 102.4 0 0 1 102.4 102.4v33.792a102.4 102.4 0 0 1-102.4 102.4h-28.672V870.4a41.984 41.984 0 0 1-43.008 42.496z m-159.744-70.144h131.584V665.6a34.304 34.304 0 0 1 34.816-34.816h64a31.744 31.744 0 0 0 31.744-31.744V563.2a31.744 31.744 0 0 0-31.744-31.744h-64a34.816 34.816 0 0 1-34.816-35.328V316.416h-177.152a35.328 35.328 0 0 1-35.328-35.328V212.992a31.744 31.744 0 0 0-31.744-31.744h-33.792a31.744 31.744 0 0 0-31.744 31.744v68.096a35.328 35.328 0 0 1-34.816 35.328H234.496v130.048h37.888a110.592 110.592 0 0 1 110.08 110.592v37.888a110.592 110.592 0 0 1-110.08 110.592h-37.888v137.216h136.192v-42.496a110.592 110.592 0 0 1 110.592-110.08h37.888a110.592 110.592 0 0 1 110.592 110.08z">
+            <local:MyListItem.Buttons>
+                <x:Array Type="{x:Type local:MyIconButton}">
+                    <local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                </x:Array>
+            </local:MyListItem.Buttons>
+        </local:MyListItem>
+        <local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="12" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
+                          LogoScale="0.98" Logo="M511.488 995.328a128.654222 128.654222 0 0 1-57.116444-13.112889L70.769778 791.808a126.833778 126.833778 0 0 1-70.769778-113.777778V311.608889a126.179556 126.179556 0 0 1 15.36-60.103111V248.604444c1.479111-2.901333 3.356444-5.603556 5.518222-8.021333a127.630222 127.630222 0 0 1 49.891556-42.325333L454.371556 13.368889a128.739556 128.739556 0 0 1 112.981333 0l383.601778 190.407111a126.862222 126.862222 0 0 1 72.049777 113.379556v360.874666a126.805333 126.805333 0 0 1-70.769777 115.939556L568.604444 984.32c-17.92 7.964444-37.461333 11.747556-57.116444 11.008z m42.638222-470.897778v370.204445l360.192-178.545778c14.449778-7.253333 23.552-21.987556 23.438222-38.087111v-335.928889L554.097778 524.430222zM85.248 330.666667v347.335111a42.268444 42.268444 0 0 0 23.438222 38.087111l360.192 178.545778V523.576889L85.248 330.666667zM135.537778 260.835556l375.950222 189.952 137.671111-65.564445L286.435556 188.074667 135.537778 260.864z m245.105778-118.471112l363.576888 197.973334 150.897778-71.480889-365.283555-180.224a42.922667 42.922667 0 0 0-37.518223 0l-111.672888 53.731555z">
+            <local:MyListItem.Buttons>
+                <x:Array Type="{x:Type local:MyIconButton}">
+                    <local:MyIconButton Tag="12" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                </x:Array>
+            </local:MyListItem.Buttons>
+        </local:MyListItem>
+        <local:MyListItem x:Name="ItemResourcePack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="13" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="资源包"
+                  LogoScale="0.98" Logo="M913.066667 264.533333l-371.2-209.066666c-25.6-12.8-59.733333-12.8-85.333334 0L89.6 264.533333C34.133333 298.666667 34.133333 379.733333 89.6 413.866667l371.2 209.066666c25.6 12.8 59.733333 12.8 85.333333 0l371.2-209.066666c55.466667-34.133333 55.466667-119.466667-4.266666-149.333334z m-413.866667 281.6L132.266667 337.066667 499.2 128l371.2 209.066667-371.2 209.066666z M46.933333 516.266667c12.8-21.333333 38.4-25.6 59.733334-17.066667l384 221.866667c12.8 8.533333 29.866667 8.533333 42.666666 0l388.266667-217.6c21.333333-12.8 46.933333-4.266667 59.733333 17.066666 12.8 21.333333 4.266667 46.933333-17.066666 59.733334l-388.266667 217.6c-38.4 21.333333-89.6 21.333333-128 0l-384-221.866667c-21.333333-12.8-25.6-38.4-17.066667-59.733333z M106.666667 669.866667c-21.333333-12.8-46.933333-4.266667-59.733334 17.066666-12.8 21.333333-4.266667 46.933333 17.066667 59.733334l388.266667 217.6c38.4 21.333333 85.333333 21.333333 128 0l379.733333-217.6c21.333333-12.8 25.6-38.4 17.066667-59.733334-12.8-21.333333-38.4-25.6-59.733334-17.066666l-379.733333 217.6c-12.8 8.533333-29.866667 8.533333-42.666667 0l-388.266666-217.6z">
+            <local:MyListItem.Buttons>
+                <x:Array Type="{x:Type local:MyIconButton}">
+                    <local:MyIconButton Tag="13" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                </x:Array>
+            </local:MyListItem.Buttons>
+        </local:MyListItem>
+        <local:MyListItem x:Name="ItemShader" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="14" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="光影包"
+              LogoScale="0.98" Logo="M640 810.666667 384 810.666667c-25.6 0-42.666667-17.066667-42.666667-42.666667l0-21.333333c0-59.733333-25.6-119.466667-72.533333-166.4C200.533333 512 166.4 422.4 170.666667 328.533333 179.2 149.333333 328.533333 4.266667 507.733333 0L512 0c187.733333 0 341.333333 153.6 341.333333 341.333333 0 89.6-34.133333 179.2-102.4 243.2-46.933333 42.666667-68.266667 102.4-68.266667 162.133333L682.666667 768C682.666667 793.6 665.6 810.666667 640 810.666667zM426.666667 725.333333l170.666667 0c4.266667-76.8 38.4-145.066667 93.866667-204.8C742.4 473.6 768 409.6 768 341.333333c0-140.8-115.2-256-256-256l-4.266667 0C375.466667 89.6 260.266667 196.266667 256 332.8 251.733333 401.066667 281.6 469.333333 328.533333 520.533333 388.266667 580.266667 422.4 652.8 426.666667 725.333333z M512 1024 512 1024c-93.866667 0-170.666667-76.8-170.666667-170.666667l0-85.333333c0-25.6 17.066667-42.666667 42.666667-42.666667l256 0c25.6 0 42.666667 17.066667 42.666667 42.666667l0 85.333333C682.666667 947.2 605.866667 1024 512 1024zM426.666667 810.666667l0 42.666667c0 46.933333 38.4 85.333333 85.333333 85.333333L512 938.666667c46.933333 0 85.333333-38.4 85.333333-85.333333l0-42.666667L426.666667 810.666667z M384 384C358.4 384 341.333333 366.933333 341.333333 341.333333c0-93.866667 76.8-170.666667 170.666667-170.666667 25.6 0 42.666667 17.066667 42.666667 42.666667s-17.066667 42.666667-42.666667 42.666667c-46.933333 0-85.333333 38.4-85.333333 85.333333C426.666667 366.933333 409.6 384 384 384z">
+            <local:MyListItem.Buttons>
+                <x:Array Type="{x:Type local:MyIconButton}">
+                    <local:MyIconButton Tag="14" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                </x:Array>
+            </local:MyListItem.Buttons>
+        </local:MyListItem>
+            <local:MyListItem x:Name="ItemDataPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="15" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="数据包"
+              LogoScale="0.98" Logo="M511.488 995.328a128.654222 128.654222 0 0 1-57.116444-13.112889L70.769778 791.808a126.833778 126.833778 0 0 1-70.769778-113.777778V311.608889a126.179556 126.179556 0 0 1 15.36-60.103111V248.604444c1.479111-2.901333 3.356444-5.603556 5.518222-8.021333a127.630222 127.630222 0 0 1 49.891556-42.325333L454.371556 13.368889a128.739556 128.739556 0 0 1 112.981333 0l383.601778 190.407111a126.862222 126.862222 0 0 1 72.049777 113.379556v360.874666a126.805333 126.805333 0 0 1-70.769777 115.939556L568.604444 984.32c-17.92 7.964444-37.461333 11.747556-57.116444 11.008z m42.638222-470.897778v370.204445l360.192-178.545778c14.449778-7.253333 23.552-21.987556 23.438222-38.087111v-335.928889L554.097778 524.430222zM85.248 330.666667v347.335111a42.268444 42.268444 0 0 0 23.438222 38.087111l360.192 178.545778V523.576889L85.248 330.666667zM135.537778 260.835556l375.950222 189.952 137.671111-65.564445L286.435556 188.074667 135.537778 260.864z m245.105778-118.471112l363.576888 197.973334 150.897778-71.480889-365.283555-180.224a42.922667 42.922667 0 0 0-37.518223 0l-111.672888 53.731555z">
                 <local:MyListItem.Buttons>
                     <x:Array Type="{x:Type local:MyIconButton}">
-                        <local:MyIconButton Tag="11" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
+                        <local:MyIconButton Tag="15" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
                     </x:Array>
                 </local:MyListItem.Buttons>
             </local:MyListItem>
-            <local:MyListItem x:Name="ItemPack" Type="RadioBox" IsScaleAnimationEnabled="False" Tag="12" MinPaddingRight="35" Height="36" VerticalAlignment="Top" Title="整合包"
-                          LogoScale="0.98" Logo="M511 995a128 128 0 0 1-57-13L70 791a126 126 0 0 1-70-113V311a126 126 0 0 1 15-60V248c1-2 3-5 5-8a127 127 0 0 1 49-42L454 13a128 128 0 0 1 112 0l383 190a126 126 0 0 1 72 113v360a126 126 0 0 1-70 115L568 984c-17 7-37 11-57 11z m42-470v370l360-178c14-7 23-21 23-38v-335L554 524zM85 330v347a42 42 0 0 0 23 38l360 178V523L85 330zM135 260l375 189 137-65L286 188 135 260z m245-118l363 197 150-71-365-180a42 42 0 0 0-37 0l-111 53z">
-                <local:MyListItem.Buttons>
-                    <x:Array Type="{x:Type local:MyIconButton}">
-                        <local:MyIconButton Tag="12" ToolTip="刷新" ToolTipService.Placement="Right" ToolTipService.InitialShowDelay="200" ToolTipService.VerticalOffset="-1" LogoScale="0.85" Click="Refresh" Logo="M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z" />
-                    </x:Array>
-                </local:MyListItem.Buttons>
-            </local:MyListItem>
-
         </StackPanel>
     </local:MyScrollViewer>
 </local:MyPageLeft>

+ 28 - 1
Plain Craft Launcher 2/Pages/PageDownload/PageDownloadLeft.xaml.vb

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

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

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

+ 124 - 0
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

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

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

+ 122 - 0
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

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

@@ -195,9 +195,6 @@
     <Compile Include="Controls\MyIconTextButton.xaml.vb">
       <DependentUpon>MyIconTextButton.xaml</DependentUpon>
     </Compile>
-
-
-
     <Compile Include="Modules\Minecraft\ModComp.vb" />
     <Compile Include="Modules\Minecraft\ModJava.vb" />
     <Compile Include="Modules\Minecraft\ModMod.vb" />
@@ -242,6 +239,12 @@
     <Compile Include="Modules\Minecraft\MyCompItem.xaml.vb">
       <DependentUpon>MyCompItem.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Pages\PageDownload\PageDownloadDataPack.xaml.vb">
+      <DependentUpon>PageDownloadDataPack.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Pages\PageDownload\PageDownloadResourcePack.xaml.vb">
+      <DependentUpon>PageDownloadResourcePack.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Pages\PageDownload\PageDownloadMod.xaml.vb">
       <DependentUpon>PageDownloadMod.xaml</DependentUpon>
     </Compile>
@@ -260,6 +263,9 @@
     <Compile Include="Pages\PageDownload\PageDownloadPack.xaml.vb">
       <DependentUpon>PageDownloadPack.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Pages\PageDownload\PageDownloadShader.xaml.vb">
+      <DependentUpon>PageDownloadShader.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Pages\PageLaunch\MyMsgLogin.xaml.vb">
       <DependentUpon>MyMsgLogin.xaml</DependentUpon>
     </Compile>
@@ -489,7 +495,6 @@
       <DependentUpon>FormMain.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
-
     <Page Include="Modules\Minecraft\MyLocalModItem.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
@@ -498,6 +503,14 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Pages\PageDownload\PageDownloadDataPack.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="Pages\PageDownload\PageDownloadResourcePack.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Pages\PageDownload\PageDownloadMod.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
@@ -522,6 +535,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Pages\PageDownload\PageDownloadShader.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Pages\PageLaunch\MyMsgLogin.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
@@ -929,7 +946,6 @@
     <Resource Include="Images\Heads\MCBBS.png" />
   </ItemGroup>
   <ItemGroup>
-
   </ItemGroup>
   <ItemGroup>
     <Resource Include="Images\Blocks\NeoForge.png" />
@@ -942,4 +958,4 @@
     <None Include="Resources\libwebp64.dll" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
-</Project>
+</Project>