This commit is contained in:
LTCat
2026-10-17 03:39:13 +08:00
parent 9dcb799fb0
commit ade6e04c6e
65 changed files with 6989 additions and 2643 deletions
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
<local:MyPageRight
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PCL" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PCL" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="PageDownloadInstall"
PanScroll="PanBack">
<Grid>
@@ -14,19 +14,96 @@
</StackPanel.RenderTransform>
</StackPanel>
<StackPanel Grid.Row="1" x:Name="PanSelect" Visibility="Collapsed" Opacity="0" IsHitTestVisible="False" Margin="0,0,0,45">
<StackPanel.Resources>
<Style x:Key="ModifyCompareIcon" TargetType="{x:Type local:MyImage}">
<Setter Property="Width" Value="18" />
<Setter Property="Height" Value="18" />
<Setter Property="RenderOptions.BitmapScalingMode" Value="Linear" />
</Style>
<Style x:Key="ModifyCompareText" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource BasedOnTextBlock}">
<Setter Property="Margin" Value="7,0,0,0" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="ModifyCompareArrow" TargetType="{x:Type Path}">
<Setter Property="Data" Value="M3,3 L7,7 3,11 M9,3 L13,7 9,11" />
<Setter Property="Margin" Value="10,0,18,0" />
<Setter Property="Height" Value="14" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Stroke" Value="{DynamicResource ColorBrushGray2}" />
<Setter Property="StrokeThickness" Value="1.4" />
<Setter Property="StrokeStartLineCap" Value="Round" />
<Setter Property="StrokeEndLineCap" Value="Round" />
<Setter Property="StrokeLineJoin" Value="Round" />
</Style>
<sys:String x:Key="ModifyRestoreLogo">M9.83,3.17 A1.18,1.18 0 0 1 9.83,4.83 L6.85,7.82 H14.5 A6.68,6.68 0 0 1 14.5,21.18 H11 A1.18,1.18 0 0 1 11,18.82 H14.5 A4.32,4.32 0 0 0 14.5,10.18 H6.85 L9.83,13.17 A1.18,1.18 0 0 1 8.17,14.83 L3.17,9.83 A1.18,1.18 0 0 1 3.17,8.17 L8.17,3.17 A1.18,1.18 0 0 1 9.83,3.17 Z</sys:String>
<sys:String x:Key="ModifyClearLogo">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</sys:String>
<Style x:Key="ModifyRestoreButton" TargetType="{x:Type local:MyIconButton}">
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="30" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Margin" Value="0,5,32,0" />
<Setter Property="ToolTip" Value="撤销更改" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<Style x:Key="ModifyClearButton" TargetType="{x:Type local:MyIconButton}">
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="30" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="Margin" Value="0,5,32,0" />
<Setter Property="ToolTip" Value="取消安装" />
</Style>
</StackPanel.Resources>
<StackPanel.RenderTransform>
<TranslateTransform X="40" />
</StackPanel.RenderTransform>
<local:MyCard Margin="0,15,0,15" UseAnimation="False" Height="62">
<local:MyCard x:Name="CardSelectName" Margin="0,15,0,15" MinHeight="62">
<local:MyIconButton HorizontalAlignment="Left" Theme="Black" Opacity="0.6" x:Name="BtnBack" Margin="13,0,0,0" Height="26" Width="26"
LogoScale="0.87" Logo="M1097 584 250 584 562 896C591 925 591 972 562 1001 533 1030 487 1030 458 1001L21 565C6 550-0 531 0 511L0 511 0 511C-0 492 6 472 21 457L458 21C487-7 533-7 562 21 591 50 591 97 562 126L250 438 1097 438C1137 438 1170 471 1170 511 1170 551 1137 584 1097 584L1097 584Z" />
<local:MyImage x:Name="ImgLogo" Margin="52,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Height="32" RenderOptions.BitmapScalingMode="HighQuality" />
<local:MyTextBox Margin="93,0,16,0" Padding="-1,0" MinHeight="30" FontSize="15" VerticalAlignment="Center" x:Name="TextSelectName" MaxLength="70" HasBackground="False" />
<local:MyImage x:Name="ImgLogo" Margin="52,15,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Height="32" RenderOptions.BitmapScalingMode="HighQuality" />
<local:MyTextBox Margin="93,16,16,0" Padding="-1,0" MinHeight="30" FontSize="15" VerticalAlignment="Top" x:Name="TextSelectName" MaxLength="70" HasBackground="False" ShowValidateResult="False" />
<TextBlock x:Name="LabSelectName" Visibility="Collapsed" Height="21" Margin="56,47,42,11" Padding="0,4,0,0" VerticalAlignment="Top" FontSize="12.5" Foreground="{DynamicResource ColorBrushGray3}" TextTrimming="CharacterEllipsis" />
<local:MyIconButton x:Name="BtnOriginalNameRestore" Style="{StaticResource ModifyRestoreButton}" Margin="0,16,6,0"
Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyHint x:Name="HintSelectName" Theme="Red" Visibility="Collapsed" />
<local:MyHint Text="如果不安装 Fabric API,大多数 Mod 都会无法使用!" Margin="0,10,0,0" x:Name="HintFabricAPI" Theme="Red" />
<local:MyHint Text="必须安装 OptiFabric 才能正常使用 OptiFine!" Margin="0,10,0,0" x:Name="HintOptiFabric" Theme="Red" />
<local:MyHint Text="安装结束后,请在 Mod 下载中搜索 OptiFabric Origins 并下载,否则 OptiFine 会无法使用!" Margin="0,10,0,0" x:Name="HintOptiFabricOld" Theme="Yellow" />
<local:MyHint Text="OptiFine 与一部分 Mod 的兼容性不佳,请谨慎安装。" Margin="0,10,0,0" x:Name="HintModOptiFine" Theme="Yellow" />
<local:MyCard Title="Minecraft" Height="40" Margin="0,12,0,0" x:Name="CardMinecraft" Visibility="Collapsed">
<Grid x:Name="PanMinecraftInfo" Height="18" Margin="132,11,35,0" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /><ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<local:MyImage x:Name="ImgMinecraft" Height="18" Width="18" Margin="0,0,7,0" RenderOptions.BitmapScalingMode="Linear" />
<TextBlock x:Name="LabMinecraftVersion" Grid.Column="1" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
</Grid>
<Grid x:Name="PanMinecraftCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage x:Name="ImgMinecraftOld" Style="{StaticResource ModifyCompareIcon}" DockPanel.Dock="Left" />
<TextBlock x:Name="LabMinecraftOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage x:Name="ImgMinecraftNew" Style="{StaticResource ModifyCompareIcon}" DockPanel.Dock="Left" />
<TextBlock x:Name="LabMinecraftNew" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<Grid Height="40" VerticalAlignment="Top" Background="Transparent" Cursor="Hand">
<Path HorizontalAlignment="Right" Margin="0,0,16,0" Stretch="Uniform" Height="6" Width="10" Fill="{StaticResource ColorBrush1}" RenderTransformOrigin="0.5,0.5">
<Path.Data>M2,4 l-2,2 10,10 10,-10 -2,-2 -8,8 -8,-8 z</Path.Data>
<Path.RenderTransform><RotateTransform Angle="270" /></Path.RenderTransform>
</Path>
</Grid>
<local:MyIconButton x:Name="BtnMinecraftRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="Forge" Height="40" Margin="0,12,0,0" x:Name="CardForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanForge" />
<Grid x:Name="PanForgeInfo" Height="18" Margin="132,11,15,0" VerticalAlignment="Top" Tag="True">
@@ -37,13 +114,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Anvil.png" />
<local:MyImage x:Name="ImgForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Anvil.png" />
<TextBlock x:Name="LabForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanForgeCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Anvil.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabForgeOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Anvil.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnForgeClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnForgeRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="NeoForge" Height="40" Margin="0,12,0,0" x:Name="CardNeoForge" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanNeoForge" />
@@ -55,13 +144,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgNeoForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/NeoForge.png" />
<local:MyImage x:Name="ImgNeoForge" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/NeoForge.png" />
<TextBlock x:Name="LabNeoForge" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnNeoForgeClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnNeoForgeClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanNeoForgeCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/NeoForge.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabNeoForgeOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/NeoForge.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnNeoForgeClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnNeoForgeRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="Fabric" Height="40" Margin="0,12,0,0" x:Name="CardFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabric" />
@@ -73,13 +174,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<local:MyImage x:Name="ImgFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<TextBlock x:Name="LabFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanFabricCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabFabricOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnFabricClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnFabricRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="Fabric API" Height="40" Margin="0,12,0,0" x:Name="CardFabricApi" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanFabricApi" />
@@ -91,13 +204,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgFabricApi" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<local:MyImage x:Name="ImgFabricApi" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Fabric.png" />
<TextBlock x:Name="LabFabricApi" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnFabricApiClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnFabricApiClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanFabricApiCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabFabricApiOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Fabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnFabricApiClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnFabricApiRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="OptiFine" Height="40" Margin="0,12,0,0" x:Name="CardOptiFine" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top">
@@ -111,13 +236,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgOptiFine" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/GrassPath.png" />
<local:MyImage x:Name="ImgOptiFine" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/GrassPath.png" />
<TextBlock x:Name="LabOptiFine" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnOptiFineClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnOptiFineClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanOptiFineCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/GrassPath.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabOptiFineOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/GrassPath.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnOptiFineClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnOptiFineRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="OptiFabric" Height="40" Margin="0,12,0,0" x:Name="CardOptiFabric" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanOptiFabric" />
@@ -129,13 +266,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgOptiFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/images/Blocks/OptiFabric.png" />
<local:MyImage x:Name="ImgOptiFabric" Margin="0,0,7,0" SnapsToDevicePixels="True" Height="18" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/images/Blocks/OptiFabric.png" />
<TextBlock x:Name="LabOptiFabric" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnOptiFabricClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnOptiFabricClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanOptiFabricCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/OptiFabric.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabOptiFabricOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/OptiFabric.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnOptiFabricClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnOptiFabricRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
<local:MyCard Title="LiteLoader" Height="40" Margin="0,12,0,0" x:Name="CardLiteLoader" IsSwapped="True" CanSwap="True" SwapLogoRight="True">
<StackPanel Margin="20,40,18,15" VerticalAlignment="Top" Name="PanLiteLoader" />
@@ -147,13 +296,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Image x:Name="ImgLiteLoader" Margin="-1,0,6,0" SnapsToDevicePixels="True" Height="20" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Egg.png" />
<local:MyImage x:Name="ImgLiteLoader" Margin="-1,0,6,0" SnapsToDevicePixels="True" Height="20" RenderOptions.BitmapScalingMode="Linear" Source="pack://application:,,,/images/Blocks/Egg.png" />
<TextBlock x:Name="LabLiteLoader" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Grid.Column="1" />
</Grid>
<Grid x:Name="BtnLiteLoaderClear" Height="30" Width="30" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5,32,0" Background="{StaticResource ColorBrushSemiTransparent}">
<Path x:Name="BtnLiteLoaderClearInner" Height="10" Width="10" Stretch="Uniform" Fill="{StaticResource ColorBrushGray1}" HorizontalAlignment="Center" VerticalAlignment="Center"
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" />
<Grid x:Name="PanLiteLoaderCompare" Height="18" Margin="132,11,80,0" VerticalAlignment="Top" Visibility="Collapsed" IsHitTestVisible="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<DockPanel>
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Egg.png" DockPanel.Dock="Left" />
<TextBlock x:Name="LabLiteLoaderOld" Style="{StaticResource ModifyCompareText}" />
</DockPanel>
<Path Grid.Column="1" Style="{StaticResource ModifyCompareArrow}" />
<DockPanel Grid.Column="2">
<local:MyImage Style="{StaticResource ModifyCompareIcon}" Source="pack://application:,,,/images/Blocks/Egg.png" DockPanel.Dock="Left" />
<TextBlock Style="{StaticResource ModifyCompareText}" />
</DockPanel>
</Grid>
<local:MyIconButton x:Name="BtnLiteLoaderClear" Style="{StaticResource ModifyClearButton}" Theme="Black" LogoScale="0.5" Logo="{StaticResource ModifyClearLogo}" />
<local:MyIconButton x:Name="BtnLiteLoaderRestore" Style="{StaticResource ModifyRestoreButton}" Theme="Black" LogoScale="0.55" Logo="{StaticResource ModifyRestoreLogo}" />
</local:MyCard>
</StackPanel>
</Grid>
@@ -163,7 +324,7 @@
LogoScale="0.95" Logo="M955 610h-59c-15 0-29 13-29 29v196c0 15-13 29-29 29h-649c-15 0-29-13-29-29v-196c0-15-13-29-29-29h-59c-15 0-29 13-29 29V905c0 43 35 78 78 78h787c43 0 78-35 78-78V640c0-15-13-29-29-29zM492 740c11 11 29 11 41 0l265-265c11-11 11-29 0-41l-41-41c-11-11-29-11-41 0l-110 110c-11 11-33 3-33-13V68C571 53 555 39 541 39h-59c-15 0-29 13-29 29v417c0 17-21 25-33 13l-110-110c-11-11-29-11-41 0L226 433c-11 11-11 29 0 41L492 740z" />
</Grid>
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
<local:MyLoading Text="正在获取版本列表" Margin="20,20,20,17" x:Name="LoadMinecraft" HorizontalAlignment="Center" VerticalAlignment="Center" />
<local:MyLoading Text="正在加载" Margin="20,20,20,17" x:Name="LoadMinecraft" HorizontalAlignment="Center" VerticalAlignment="Center" ShowProgress="True" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadOptiFine" HasAnimation="False" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadForge" HasAnimation="False" />
<local:MyLoading Visibility="Collapsed" x:Name="LoadNeoForge" HasAnimation="False" />
@@ -196,4 +357,4 @@
</StackPanel>
</Border>
</Grid>
-->
-->
@@ -1,12 +1,30 @@
Public Class PageDownloadInstall
Private Sub LoaderInit() Handles Me.Initialized
Public Sub ControlInit()
DisabledPageAnimControls.Add(BtnStart)
LoadOptiFine.State = DlOptiFineListLoader
LoadLiteLoader.State = DlLiteLoaderListLoader
LoadFabric.State = DlFabricListLoader
LoadFabricApi.State = DlFabricApiLoader
LoadNeoForge.State = DlNeoForgeListLoader
LoadOptiFabric.State = DlOptiFabricLoader
McDownloadForgeRecommendedRefresh()
End Sub
Public Overridable Sub LoaderInit() Handles Me.Initialized
ControlInit()
PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, DlClientListLoader, AddressOf LoadMinecraft_OnFinish)
End Sub
Private IsLoad As Boolean = False
Private Sub Init() Handles Me.Loaded
Public Overridable Sub Init() Handles Me.Loaded
If IsLoad Then
IsSelectNameEdited = False
ExitSelectPage()
If DlClientListLoader.State = LoadState.Finished Then TrySelectWaitingVersion()
End If
IsLoad = True
PanBack.ScrollToHome()
DlOptiFineListLoader.Start()
DlLiteLoaderListLoader.Start()
@@ -17,35 +35,19 @@ Public Class PageDownloadInstall
TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFolderName(McFolderSelected & "versions")}
TextSelectName.Validate()
ReloadSelected()
'非重复加载部分
If IsLoad Then Return
IsLoad = True
McDownloadForgeRecommendedRefresh()
LoadOptiFine.State = DlOptiFineListLoader
LoadLiteLoader.State = DlLiteLoaderListLoader
LoadFabric.State = DlFabricListLoader
LoadFabricApi.State = DlFabricApiLoader
LoadNeoForge.State = DlNeoForgeListLoader
LoadOptiFabric.State = DlOptiFabricLoader
End Sub
#Region "页面切换"
'页面切换动画
Public IsInSelectPage As Boolean = False
Private IsFirstLoaded As Boolean = False
Private Sub EnterSelectPage()
If IsInSelectPage Then Return
''' <summary>
''' 选择页面 UI 初始化。
''' </summary>
Public Sub PrepareSelectPage()
IsInSelectPage = True
PanInner.Margin = New Thickness(25, 10, 25, 40)
AutoSelectedFabricApi = False
AutoSelectedOptiFabric = False
IsSelectNameEdited = False
PanSelect.Visibility = Visibility.Visible
PanSelect.IsHitTestVisible = True
PanMinecraft.IsHitTestVisible = False
@@ -54,13 +56,9 @@ Public Class PageDownloadInstall
DisabledPageAnimControls.Remove(BtnStart)
BtnStart.Show = True
CardOptiFine.IsSwapped = True
CardLiteLoader.IsSwapped = True
CardForge.IsSwapped = True
CardNeoForge.IsSwapped = True
CardFabric.IsSwapped = True
CardFabricApi.IsSwapped = True
CardOptiFabric.IsSwapped = True
For Each Card In {CardOptiFine, CardLiteLoader, CardForge, CardNeoForge, CardFabric, CardFabricApi, CardOptiFabric}
Card.IsSwapped = True
Next
'如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
For Each Control In GetAllAnimControls(PanSelect)
@@ -69,9 +67,16 @@ Public Class PageDownloadInstall
Control.RenderTransform = New TranslateTransform
End If
Next
End Sub
Private Sub EnterSelectPage()
If IsInSelectPage Then Return
AutoSelectedFabricApi = False
AutoSelectedOptiFabric = False
PrepareSelectPage()
'启动 Forge 加载
If McVersion.IsFormatFit(VanillaName) Then
If McVersion.IsStandardFormat(VanillaName) Then
Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & VanillaName, AddressOf DlForgeVersionMain)
LoadForge.State = ForgeLoader
ForgeLoader.Start(VanillaName)
@@ -87,35 +92,27 @@ Public Class PageDownloadInstall
AaCode(
Sub()
PanBack.ScrollToHome()
TextSelectName.Validate()
OptiFine_Loaded()
LiteLoader_Loaded()
Forge_Loaded()
NeoForge_Loaded()
Fabric_Loaded()
FabricApi_Loaded()
OptiFabric_Loaded()
ReloadSelected()
ReloadComponentLists()
PanMinecraft.Visibility = Visibility.Collapsed
End Sub, After:=True),
AaOpacity(PanSelect, 1 - PanSelect.Opacity, 70, 100),
AaTranslateX(PanSelect, -CType(PanSelect.RenderTransform, TranslateTransform).X, 160, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
AaCode(
Sub()
PanBack.IsHitTestVisible = True
'初始化 Binding
If IsFirstLoaded Then Return
IsFirstLoaded = True
BtnOptiFineClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardOptiFine.MainTextBlock, .Mode = BindingMode.OneWay})
BtnLiteLoaderClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardLiteLoader.MainTextBlock, .Mode = BindingMode.OneWay})
BtnForgeClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardForge.MainTextBlock, .Mode = BindingMode.OneWay})
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})
BtnOptiFabricClearInner.SetBinding(Shapes.Path.FillProperty, New Binding("Foreground") With {.Source = CardOptiFabric.MainTextBlock, .Mode = BindingMode.OneWay})
End Sub,, True)
AaCode(Sub() PanBack.IsHitTestVisible = True,, True)
}, "FrmDownloadInstall SelectPageSwitch", True)
End Sub
Public Sub ReloadComponentLists()
TextSelectName.Validate()
OptiFine_Loaded()
LiteLoader_Loaded()
Forge_Loaded()
NeoForge_Loaded()
Fabric_Loaded()
FabricApi_Loaded()
OptiFabric_Loaded()
ReloadSelected()
End Sub
Public Sub ExitSelectPage() Handles BtnBack.Click
If Not IsInSelectPage Then Return
IsInSelectPage = False
@@ -144,10 +141,11 @@ Public Class PageDownloadInstall
End Sub,, True)
}, "FrmDownloadInstall SelectPageSwitch")
End Sub
Public Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
Public Overridable Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
VanillaName = sender.Title
VanillaData = sender.Tag
VanillaIcon = sender.Logo
LabMinecraftVersion.Text = VanillaName
EnterSelectPage()
End Sub
@@ -156,9 +154,9 @@ Public Class PageDownloadInstall
#Region "选择"
'Minecraft
Private VanillaName As String
Private VanillaData As JObject
Private VanillaIcon As String
Public VanillaName As String
Public VanillaData As JObject
Public VanillaIcon As String
Private ReadOnly Property VanillaDrop As Integer
Get
Return McVersion.VersionToDrop(VanillaName)
@@ -166,24 +164,25 @@ Public Class PageDownloadInstall
End Property
'附加组件
Private SelectedOptiFine As DlOptiFineListEntry = Nothing
Private SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
Private SelectedForge As DlForgeVersionEntry = Nothing
Private SelectedNeoForge As DlNeoForgeListEntry = Nothing
Private SelectedFabric As String = Nothing
Private SelectedFabricApi As ResourceVersion = Nothing
Private SelectedOptiFabric As ResourceVersion = Nothing
Public SelectedOptiFine As DlOptiFineListEntry = Nothing
Public SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
Public SelectedForge As DlForgeVersionEntry = Nothing
Public SelectedNeoForge As DlNeoForgeListEntry = Nothing
Public SelectedFabric As String = Nothing
Public SelectedFabricApi As ResourceVersion = Nothing
Public SelectedOptiFabric As ResourceVersion = Nothing
''' <summary>
''' 重载已选择的项目的显示。
''' </summary>
Private Sub ReloadSelected() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardNeoForge.Swap, LoadNeoForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged
Public Overridable Sub ReloadSelected() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardNeoForge.Swap, LoadNeoForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged
Static Ongoing As Boolean = False '#3742 中,LoadOptiFineGetError 会初始化 LoadOptiFine,触发事件 LoadOptiFine.StateChanged,导致再次调用 SelectReload
If VanillaName Is Nothing OrElse Ongoing Then Return
Ongoing = True
'主预览
SelectNameUpdate()
ImgLogo.Source = GetSelectLogo()
ImgMinecraft.Source = VanillaIcon
'OptiFine
Dim OptiFineError As String = LoadOptiFineGetError()
CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed)
@@ -222,7 +221,7 @@ Public Class PageDownloadInstall
End If
End If
'Forge
If Not McVersion.IsFormatFit(VanillaName) Then
If Not McVersion.IsStandardFormat(VanillaName) Then
CardForge.Visibility = Visibility.Collapsed
Else
CardForge.Visibility = Visibility.Visible
@@ -356,7 +355,7 @@ Public Class PageDownloadInstall
''' <summary>
''' 清空已选择的项目。
''' </summary>
Private Sub ClearSelected()
Public Sub ClearSelected()
VanillaName = Nothing
VanillaData = Nothing
VanillaIcon = Nothing
@@ -367,7 +366,10 @@ Public Class PageDownloadInstall
SelectedFabric = Nothing
SelectedFabricApi = Nothing
SelectedOptiFabric = Nothing
IsSelectNameEdited = False
For Each ClearButton In {BtnOptiFineClear, BtnLiteLoaderClear, BtnForgeClear, BtnNeoForgeClear,
BtnFabricClear, BtnFabricApiClear, BtnOptiFabricClear}
ClearButton.Visibility = Visibility.Collapsed
Next
End Sub
'信息栏动画
Private Sub SetPanelVisibility(Panel As Grid, IsVisible As Boolean)
@@ -389,7 +391,7 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取版本图标。
''' </summary>
Private Function GetSelectLogo() As String
Public Overridable Function GetSelectLogo() As String
If SelectedFabric IsNot Nothing Then
Return "pack://application:,,,/images/Blocks/Fabric.png"
ElseIf SelectedForge IsNot Nothing Then
@@ -409,7 +411,7 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取默认版本名。
''' </summary>
Private Function GetSelectName() As String
Public Function GetSelectName() As String
Dim Name As String = VanillaName
If SelectedFabric IsNot Nothing Then Name += "-Fabric " & SelectedFabric.BeforeFirst("+")
If SelectedForge IsNot Nothing Then Name += "-Forge_" & SelectedForge.VersionName
@@ -418,36 +420,39 @@ Public Class PageDownloadInstall
If SelectedOptiFine IsNot Nothing Then Name += "-OptiFine_" & SelectedOptiFine.DisplayName.Replace(VanillaName & " ", "").Replace(" ", "_")
Return Name
End Function
Private IsSelectNameEdited As Boolean = False
Private IsSelectNameChanging As Boolean = False
Public IsSelectNameEdited As Boolean = False
Private Sub SelectNameUpdate()
If IsSelectNameEdited OrElse IsSelectNameChanging Then Return
IsSelectNameChanging = True
If IsSelectNameEdited Then Return
TextSelectName.Text = GetSelectName()
IsSelectNameChanging = False
IsSelectNameEdited = False
End Sub
Private Sub TextSelectName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSelectName.TextChanged
If IsSelectNameChanging Then Return
IsSelectNameEdited = True
ReloadSelected()
End Sub
Private Sub TextSelectName_ValidateChanged(sender As Object, e As EventArgs) Handles TextSelectName.ValidateChanged
BtnStart.IsEnabled = TextSelectName.IsValidated
HintSelectName.Text = TextSelectName.ValidateResult
HintSelectName.Visibility = If(TextSelectName.IsValidated, Visibility.Collapsed, Visibility.Visible)
End Sub
#End Region
#Region "加载器"
Public MinecraftVersions As JArray
Private Sub LoadMinecraft_OnFinish()
MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
ShowMinecraftVersionList()
TrySelectWaitingVersion() '自动选择版本
End Sub
'结果数据化
Private Sub LoadMinecraft_OnFinish()
Public Overridable Sub ShowMinecraftVersionList()
ExitSelectPage() '返回
Try
Dim Dict As New Dictionary(Of String, List(Of JObject)) From {
{"正式版", New List(Of JObject)}, {"预览版", New List(Of JObject)}, {"远古版", New List(Of JObject)}, {"愚人节版", New List(Of JObject)}
}
Dim Versions As JArray = DlClientListLoader.Output.Value("versions")
For Each Version As JObject In Versions
For Each Version As JObject In MinecraftVersions
'确定分类
Dim Type As String = Version("type")
Select Case Type
@@ -498,7 +503,7 @@ Public Class PageDownloadInstall
'清空当前
PanMinecraft.Children.Clear()
'添加最新版本
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15), .SwapType = 2}
Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15)}
Dim PinnedVersions As New List(Of JObject)
Dim Release As JObject = Dict("正式版")(0).DeepClone()
Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
@@ -508,36 +513,41 @@ Public Class PageDownloadInstall
Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
PinnedVersions.Add(Snapshot)
End If
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = PinnedVersions}
MyCard.StackInstall(PanInfo, 7)
Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
CardInfo.Children.Add(PanInfo)
CardInfo.SwapControl = PanInfo
CardInfo.SetContentBuilder(PinnedVersions, '使用 Lambda 转发点击事件,避免 #535
Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
CardInfo.BuildContent()
PanMinecraft.Children.Insert(0, CardInfo)
'添加其他版本
For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
If Not Pair.Value.Any() Then Continue For
'增加卡片
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = 7}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(Pair.Value,
Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
NewCard.IsSwapped = True
PanMinecraft.Children.Add(NewCard)
Next
'自动选择版本
If VersionWaitingSelect Is Nothing Then Exit Try
Logger.Info($"自动选择 MC 版本:{VersionWaitingSelect}")
For Each Version As JObject In Versions
If Version("id").ToString <> VersionWaitingSelect Then Continue For
Dim Item = McDownloadListItem(Version, Sub()
End Sub, False).Init()
MinecraftSelected(Item, Nothing)
VersionWaitingSelect = Nothing
Return
Next
Catch ex As Exception
Logger.Error(ex, "可视化安装版本列表出错")
End Try
End Sub
Private Sub TrySelectWaitingVersion()
If VersionWaitingSelect Is Nothing Then Return
For Each Version As JObject In MinecraftVersions
If Version("id").ToString <> VersionWaitingSelect Then Continue For
Logger.Info($"自动选择 MC 版本:{VersionWaitingSelect}")
MinecraftSelected(McDownloadListItem(Version, Nothing, False).Init(), Nothing)
VersionWaitingSelect = Nothing
Return
Next
End Sub
''' <summary>
''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
''' </summary>
@@ -550,16 +560,16 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadOptiFineGetError() As String
Public Function LoadOptiFineGetError() As String
If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
'检查 Forge 1.13 - 1.14.3:全部不兼容
If SelectedForge IsNot Nothing AndAlso
CompareVersion(VanillaName, "1.13") >= 0 AndAlso CompareVersion("1.14.3", VanillaName) >= 0 Then
McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
Return "与 Forge 不兼容"
End If
'检查 Fabric 1.20.5+:全部不兼容
If SelectedFabric IsNot Nothing AndAlso
CompareVersion(VanillaName, "1.20.4") > 0 Then
McUtils.CompareVersion(VanillaName, "1.20.4") > 0 Then
Return "与 Fabric 不兼容"
End If
'检查 Loader
@@ -584,12 +594,12 @@ Public Class PageDownloadInstall
End Function
'检查某个 OptiFine 是否与某个 Forge 兼容
Private Function IsOptiFineSuitForForge(OptiFine As DlOptiFineListEntry, Forge As DlForgeVersionEntry)
Public Shared Function IsOptiFineSuitForForge(OptiFine As DlOptiFineListEntry, Forge As DlForgeVersionEntry) As Boolean
If Forge.Inherit <> OptiFine.Inherit Then Return False '不是同一个大版本
If OptiFine.RequiredForgeVersion Is Nothing Then Return False '不兼容 Forge
If String.IsNullOrWhiteSpace(OptiFine.RequiredForgeVersion) Then Return True '#4183
If OptiFine.RequiredForgeVersion.Contains(".") Then 'XX.X.XXX
Return CompareVersion(Forge.Version.ToString, OptiFine.RequiredForgeVersion) = 0
Return McUtils.CompareVersion(Forge.Version.ToString, OptiFine.RequiredForgeVersion) = 0
Else 'XXXX
Return Forge.Version.Revision = OptiFine.RequiredForgeVersion
End If
@@ -619,7 +629,7 @@ Public Class PageDownloadInstall
Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
If Not Left.IsPreview AndAlso Right.IsPreview Then Return True
If Left.IsPreview AndAlso Not Right.IsPreview Then Return False
Return CompareVersionGE(Left.DisplayName, Right.DisplayName)
Return McUtils.CompareVersionGE(Left.DisplayName, Right.DisplayName)
End Function)
'可视化
PanOptiFine.Children.Clear()
@@ -641,12 +651,12 @@ Public Class PageDownloadInstall
CardOptiFine.IsSwapped = True
ReloadSelected()
End Sub
Private Sub OptiFine_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFineClear.MouseLeftButtonUp
Private Sub OptiFine_Clear(sender As Object, e As EventArgs) Handles BtnOptiFineClear.Click
SelectedOptiFine = Nothing
SelectedOptiFabric = Nothing
BtnOptiFabricClear.Visibility = Visibility.Collapsed
AutoSelectedOptiFabric = False
CardOptiFine.IsSwapped = True
e.Handled = True
Forge_Loaded()
NeoForge_Loaded()
ReloadSelected()
@@ -659,7 +669,7 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 LiteLoader 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadLiteLoaderGetError() As String
Public Function LoadLiteLoaderGetError() As String
'检查 Loader
If GetLoaderError(LoadLiteLoader) IsNot Nothing Then Return GetLoaderError(LoadLiteLoader)
'检查版本
@@ -699,10 +709,9 @@ Public Class PageDownloadInstall
CardLiteLoader.IsSwapped = True
ReloadSelected()
End Sub
Private Sub LiteLoader_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnLiteLoaderClear.MouseLeftButtonUp
Private Sub LiteLoader_Clear(sender As Object, e As EventArgs) Handles BtnLiteLoaderClear.Click
SelectedLiteLoader = Nothing
CardLiteLoader.IsSwapped = True
e.Handled = True
ReloadSelected()
End Sub
@@ -713,8 +722,8 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 Forge 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadForgeGetError() As String
If CompareVersionGE("1.5.1", VanillaName) AndAlso CompareVersionGE(VanillaName, "1.1") Then Return "无"
Public Function LoadForgeGetError() As String
If McUtils.CompareVersionGE("1.5.1", VanillaName) AndAlso McUtils.CompareVersionGE(VanillaName, "1.1") Then Return "无"
'检查 Loader
If GetLoaderError(LoadForge) IsNot Nothing Then Return GetLoaderError(LoadForge)
Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = LoadForge.State
@@ -725,7 +734,7 @@ Public Class PageDownloadInstall
If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
If SelectedOptiFine IsNot Nothing AndAlso
CompareVersionGE(VanillaName, "1.13") AndAlso CompareVersionGE("1.14.3", VanillaName) Then
McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
Return "与 OptiFine 不兼容" '1.13 ~ 1.14.3 OptiFine 检查
End If
If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, Version) Then Continue For
@@ -775,10 +784,9 @@ Public Class PageDownloadInstall
OptiFine_Loaded()
ReloadSelected()
End Sub
Private Sub Forge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnForgeClear.MouseLeftButtonUp
Private Sub Forge_Clear(sender As Object, e As EventArgs) Handles BtnForgeClear.Click
SelectedForge = Nothing
CardForge.IsSwapped = True
e.Handled = True
OptiFine_Loaded()
ReloadSelected()
End Sub
@@ -790,7 +798,7 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 NeoForge 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadNeoForgeGetError() As String
Public Function LoadNeoForgeGetError() As String
If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
@@ -832,10 +840,9 @@ Public Class PageDownloadInstall
OptiFine_Loaded()
ReloadSelected()
End Sub
Private Sub NeoForge_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnNeoForgeClear.MouseLeftButtonUp
Private Sub NeoForge_Clear(sender As Object, e As EventArgs) Handles BtnNeoForgeClear.Click
SelectedNeoForge = Nothing
CardNeoForge.IsSwapped = True
e.Handled = True
OptiFine_Loaded()
ReloadSelected()
End Sub
@@ -847,9 +854,9 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadFabricGetError() As String
Public Function LoadFabricGetError() As String
'检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容
If SelectedOptiFine IsNot Nothing AndAlso CompareVersionGE(VanillaName, "1.20.5") Then Return "与 OptiFine 不兼容"
If SelectedOptiFine IsNot Nothing AndAlso McUtils.CompareVersionGE(VanillaName, "1.20.5") Then Return "与 OptiFine 不兼容"
'检查 Loader
If GetLoaderError(LoadFabric) IsNot Nothing Then Return GetLoaderError(LoadFabric)
'检查版本
@@ -879,9 +886,10 @@ Public Class PageDownloadInstall
If Not Versions.Any() Then Return
'可视化
PanFabric.Children.Clear()
PanFabric.Tag = Versions
CardFabric.SwapControl = PanFabric
CardFabric.SwapType = 12
CardFabric.SetContentBuilder(Versions.OfType(Of JObject),
Function(Version) FabricDownloadListItem(Version, AddressOf Fabric_Selected))
If Not CardFabric.IsSwapped Then CardFabric.BuildContent()
Catch ex As Exception
Logger.Error(ex, "可视化 Fabric 安装版本列表出错")
End Try
@@ -895,14 +903,15 @@ Public Class PageDownloadInstall
CardFabric.IsSwapped = True
ReloadSelected()
End Sub
Private Sub Fabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricClear.MouseLeftButtonUp
Private Sub Fabric_Clear(sender As Object, e As EventArgs) Handles BtnFabricClear.Click
SelectedFabric = Nothing
SelectedFabricApi = Nothing
BtnFabricApiClear.Visibility = Visibility.Collapsed
AutoSelectedFabricApi = False
SelectedOptiFabric = Nothing
BtnOptiFabricClear.Visibility = Visibility.Collapsed
AutoSelectedOptiFabric = False
CardFabric.IsSwapped = True
e.Handled = True
ReloadSelected()
End Sub
@@ -911,14 +920,14 @@ Public Class PageDownloadInstall
#Region "Fabric API 列表"
''' <summary>
''' 判断某 Fabric API 是否适配当前选择的原版版本。
''' 判断某 Fabric API 是否适配指定的原版版本。
''' </summary>
Public Function IsFabricApiCompatible(FabricApi As ResourceVersion) As Boolean
Public Shared Function IsFabricApiCompatible(FabricApi As ResourceVersion, MinecraftVersion As String) As Boolean
Dim FabricApiName = FabricApi.Display
Try
If FabricApiName Is Nothing OrElse VanillaName Is Nothing Then Return False
If FabricApiName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False
FabricApiName = FabricApiName.Lower
Dim TargetName = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").Lower
Dim TargetName = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").Lower
If FabricApiName.StartsWithF("[" & TargetName & "]") Then Return True
If Not FabricApiName.Contains("/") OrElse Not FabricApiName.Contains("]") Then Return False
'直接的判断(例如 1.18.1/22w03a)
@@ -946,7 +955,7 @@ Public Class PageDownloadInstall
End While
Return True
Catch ex As Exception
Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {VanillaName})")
Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {MinecraftVersion})")
Return False
End Try
End Function
@@ -954,12 +963,12 @@ Public Class PageDownloadInstall
''' <summary>
''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadFabricApiGetError() As String
Public Function LoadFabricApiGetError() As String
'检查 Loader
If GetLoaderError(LoadFabricApi) IsNot Nothing Then Return GetLoaderError(LoadFabricApi)
If DlFabricApiLoader.Output Is Nothing Then Return If(SelectedFabric Is Nothing, "需要安装 Fabric", "获取中……")
'检查版本
If DlFabricApiLoader.Output.Any(Function(f) IsFabricApiCompatible(f)) Then
If DlFabricApiLoader.Output.Any(Function(f) IsFabricApiCompatible(f, VanillaName)) Then
Return If(SelectedFabric Is Nothing, "需要安装 Fabric", Nothing)
Else
Return "无"
@@ -971,7 +980,7 @@ Public Class PageDownloadInstall
If LoadFabricApiGetError() IsNot Nothing Then e.Handled = True
End Sub
Private AutoSelectedFabricApi As Boolean = False
Public AutoSelectedFabricApi As Boolean = False
''' <summary>
''' 尝试重新可视化 FabricApi 版本列表。
''' </summary>
@@ -982,7 +991,7 @@ Public Class PageDownloadInstall
'获取版本列表
Dim Versions As New List(Of ResourceVersion)
For Each Version In DlFabricApiLoader.Output
If IsFabricApiCompatible(Version) Then
If IsFabricApiCompatible(Version, VanillaName) Then
If Not Version.Display.StartsWithF("[") Then
Logger.Warn($"已特判修改 Fabric API 显示名:{Version.Display}")
Version.Display = "[" & VanillaName & "] " & Version.Display
@@ -995,7 +1004,6 @@ Public Class PageDownloadInstall
'可视化
PanFabricApi.Children.Clear()
For Each Version In Versions
If Not IsFabricApiCompatible(Version) Then Continue For
PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected))
Next
'自动选择 Fabric API
@@ -1016,10 +1024,9 @@ Public Class PageDownloadInstall
CardFabricApi.IsSwapped = True
ReloadSelected()
End Sub
Private Sub FabricApi_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnFabricApiClear.MouseLeftButtonUp
Private Sub FabricApi_Clear(sender As Object, e As EventArgs) Handles BtnFabricApiClear.Click
SelectedFabricApi = Nothing
CardFabricApi.IsSwapped = True
e.Handled = True
ReloadSelected()
End Sub
@@ -1028,23 +1035,23 @@ Public Class PageDownloadInstall
#Region "OptiFabric 列表"
''' <summary>
''' 判断某 OptiFabric 是否适配当前选择的原版版本。
''' 判断某 OptiFabric 是否适配指定的原版版本。
''' </summary>
Private Function IsOptiFabricCompatible(ModFile As ResourceVersion) As Boolean
Public Shared Function IsOptiFabricCompatible(ModFile As ResourceVersion, MinecraftVersion As String) As Boolean
Try
If VanillaName Is Nothing Then Return False
Return ModFile.GameVersions.Contains(VanillaName)
If MinecraftVersion Is Nothing Then Return False
Return ModFile.GameVersions.Contains(MinecraftVersion)
Catch ex As Exception
Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({VanillaName})")
Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({MinecraftVersion})")
Return False
End Try
End Function
Private AutoSelectedOptiFabric As Boolean = False
Public AutoSelectedOptiFabric As Boolean = False
''' <summary>
''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
''' </summary>
Private Function LoadOptiFabricGetError() As String
Public Function LoadOptiFabricGetError() As String
If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
'检查 Loader
If GetLoaderError(LoadOptiFabric) IsNot Nothing Then Return GetLoaderError(LoadOptiFabric)
@@ -1056,7 +1063,7 @@ Public Class PageDownloadInstall
Return "获取中……"
End If
For Each Version In DlOptiFabricLoader.Output
If Not IsOptiFabricCompatible(Version) Then Continue For '2135#
If Not IsOptiFabricCompatible(Version, VanillaName) Then Continue For '2135#
If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
@@ -1080,7 +1087,7 @@ Public Class PageDownloadInstall
'获取版本列表
Dim Versions As New List(Of ResourceVersion)
For Each Version In DlOptiFabricLoader.Output
If IsOptiFabricCompatible(Version) Then Versions.Add(Version)
If IsOptiFabricCompatible(Version, VanillaName) Then Versions.Add(Version)
Next
If Not Versions.Any() Then Return
'排序
@@ -1088,7 +1095,6 @@ Public Class PageDownloadInstall
'可视化
PanOptiFabric.Children.Clear()
For Each Version In Versions
If Not IsOptiFabricCompatible(Version) Then Continue For
PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected))
Next
'自动选择 OptiFabric
@@ -1108,10 +1114,9 @@ Public Class PageDownloadInstall
CardOptiFabric.IsSwapped = True
ReloadSelected()
End Sub
Private Sub OptiFabric_Clear(sender As Object, e As MouseButtonEventArgs) Handles BtnOptiFabricClear.MouseLeftButtonUp
Private Sub OptiFabric_Clear(sender As Object, e As EventArgs) Handles BtnOptiFabricClear.Click
SelectedOptiFabric = Nothing
CardOptiFabric.IsSwapped = True
e.Handled = True
ReloadSelected()
End Sub
@@ -1119,10 +1124,42 @@ Public Class PageDownloadInstall
#Region "安装"
''' <summary>
''' 获取当前选择的组件信息。
''' </summary>
Public Function GetSelectedComponents() As McComponents
Return New McComponents(
minecraftVersion:=VanillaName,
optiFineEntry:=If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
forgeVersion:=SelectedForge?.VersionName,
neoForgeVersion:=SelectedNeoForge?.VersionName,
fabricVersion:=SelectedFabric,
fabricApiVersion:=If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
optiFabricVersion:=If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)),
liteLoaderVersion:=SelectedLiteLoader?.JsonToken?("version").ToString())
End Function
Private Sub TextSelectName_KeyDown(sender As Object, e As KeyEventArgs) Handles TextSelectName.KeyDown
If e.Key = Key.Enter AndAlso IsInSelectPage AndAlso BtnStart.IsEnabled Then BtnStart_Click()
End Sub
Private Sub BtnStart_Click() Handles BtnStart.Click
'提交安装申请
Dim InstanceName As String = TextSelectName.Text
Dim Request As New McInstallRequest With {
.NewInstanceName = InstanceName,
.InstanceFolder = $"{McFolderSelected}versions\{InstanceName}\",
.Components = GetSelectedComponents(),
.MinecraftJson = VanillaData("url").ToString,
.ForgeEntry = SelectedForge,
.NeoForgeEntry = SelectedNeoForge,
.FabricApi = SelectedFabricApi,
.OptiFabric = SelectedOptiFabric,
.LiteLoaderEntry = SelectedLiteLoader
}
StartInstall(Request)
End Sub
Public Overridable Sub StartInstall(Request As McInstallRequest)
'确认版本隔离
If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
(Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 0 OrElse Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 2) Then
@@ -1132,22 +1169,7 @@ Public Class PageDownloadInstall
Return
End If
End If
'提交安装申请
Dim InstanceName As String = TextSelectName.Text
Dim Request As New McInstallRequest With {
.NewInstanceName = InstanceName,
.VersionFolder = $"{McFolderSelected}versions\{InstanceName}\",
.MinecraftJson = VanillaData("url").ToString,
.MinecraftName = VanillaName,
.OptiFineEntry = SelectedOptiFine,
.ForgeEntry = SelectedForge,
.NeoForgeEntry = SelectedNeoForge,
.FabricVersion = SelectedFabric,
.FabricApi = SelectedFabricApi,
.OptiFabric = SelectedOptiFabric,
.LiteLoaderEntry = SelectedLiteLoader
}
If Not McInstall(Request) Then Return
If Not McInstallStart(Request) Then Return
'返回,这样在再次进入安装页面时这个版本就会显示文件夹已重复
ExitSelectPage()
End Sub
@@ -0,0 +1,727 @@
''' <summary>
''' 版本修改页:完整还原已安装组件后,复用安装页的选择交互。
''' </summary>
Public Class PageDownloadModify
Inherits PageDownloadInstall
Private InitLoader As LoaderTask(Of McInstance, Object)
Private ForgeVersionLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry))
Private InitialTarget As McComponents '进入时的组件快照
''' <summary>
''' 原版的版本号是否可靠(不是通过 json 模糊分析)。
''' </summary>
Private IsVanillaVersionReliable As Boolean
Private OriginalName As String
Private CustomLogo As String
''' <summary>
''' 开始修改 MC 版本时,记录当前选择的组件,以在修改 MC 版本后尝试自动选中相同组件。
''' </summary>
Private ReadOnly PendingComponents As New Dictionary(Of MyLoading, String)
Private IsRestoringComponents As Boolean
Public Overrides Sub LoaderInit() '父级 Handles Me.Initialized
ControlInit()
InitLoader = New LoaderTask(Of McInstance, Object)("初始化版本修改", AddressOf LoadInstance)
PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, InitLoader, AddressOf LoadInstanceFinished, Function() PageInstanceLeft.Instance)
End Sub
Public Overrides Sub Init() '父级 Handles Me.Loaded
PanBack.ScrollToHome()
Static IsLoad As Boolean = False
If IsLoad Then PageLoaderRestart()
IsLoad = True
End Sub
Private Sub PageUnloaded() Handles Me.Unloaded
InitLoader.Cancel()
End Sub
''' <summary>
''' 读取或初始化版本的安装清单。
''' </summary>
Private Sub LoadInstance(Task As LoaderTask(Of McInstance, Object))
'旧状态清理
RunInUiWait(
Sub()
AniStop("FrmDownloadInstall SelectPageSwitch")
IsSelectNameEdited = False
ClearSelected()
PendingComponents.Clear()
InitialTarget = Nothing
IsInSelectPage = False
PanAllBack.IsHitTestVisible = False
End Sub)
Task.Input.Config.DiscardCache()
'获取 Components
Dim Manifest = Configs.McInstallManifest.Get(Task.Input.Config)
If Manifest IsNot Nothing AndAlso Manifest.SchemaVersion > Versions.McInstallManifestVersion Then
Logger.Warn($"安装清单由更新版本的 PCL 创建(清单版本:{Manifest.SchemaVersion},当前版本:{Versions.McInstallManifestVersion})")
Throw New Exception($"更新 PCL 后才能修改它!点击【设置 → 其他 → 检查更新】以更新。")
End If
If Manifest IsNot Nothing Then
Dim LatestManifest = Manifest.RefreshModComponents(Task.Input.PathIndie)
If LatestManifest IsNot Manifest Then
Manifest = LatestManifest
Configs.McInstallManifest.Set(Manifest, Task.Input.Config)
Task.Input.Config.Save()
End If
End If
Dim Components As McComponents
Dim OptiFineVersion As String = Nothing
If Manifest IsNot Nothing Then
'有清单:从清单读取
Components = Manifest.Components
If Components Is Nothing Then Throw New Exception("安装清单缺少组件信息,无法还原已安装组件。")
IsVanillaVersionReliable = True
Else
'无清单:通过版本 JSON 获取
Dim VersionInfo = New McInstance(Task.Input.PathVersion).Version
If VersionInfo.VanillaName = "Unknown" Then Throw New Exception("无法确定 Minecraft 版本,无法修改")
Dim UnknownLoaders = VersionInfo.ModLoaderVersions.
Where(Function(Loader) String.IsNullOrWhiteSpace(Loader.Value) OrElse Loader.Value = "未知版本").
Select(Function(Loader) Loader.Key.ToString()).ToList()
If UnknownLoaders.Any() Then Throw New Exception($"无法确定 {UnknownLoaders.Join("、")} 的版本,无法修改")
Components = New McComponents(VersionInfo.VanillaName, VersionInfo.ModLoaderVersions)
OptiFineVersion = VersionInfo.OptiFine
IsVanillaVersionReliable = VersionInfo.Reliable
End If
Dim MinecraftVersion = Components.MinecraftVersion
If String.IsNullOrWhiteSpace(MinecraftVersion) Then Throw New Exception("无法确定当前 Minecraft 版本,请检查版本文件")
'等待子列表加载器完成获取
Dim LoaderList As New Dictionary(Of String, LoaderBase) From {
{"Minecraft", DlClientListLoader}, {"OptiFine", DlOptiFineListLoader}, {"LiteLoader", DlLiteLoaderListLoader},
{"NeoForge", DlNeoForgeListLoader}, {"Fabric", DlFabricListLoader}, {"Fabric API", DlFabricApiLoader}, {"OptiFabric", DlOptiFabricLoader}}
Dim ForgeLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = Nothing
If McVersion.IsStandardFormat(MinecraftVersion) Then
ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & MinecraftVersion, AddressOf DlForgeVersionMain)
LoaderList.Add("Forge", ForgeLoader)
End If
For Each Loader In LoaderList.Values
Loader.Start(If(Loader Is ForgeLoader, MinecraftVersion, Nothing), Task.IsForceRestarting)
Next
Do
If Task.IsCanceled Then Return
For Each Pair In LoaderList
Dim Loader = Pair.Value
If Loader.State = LoadState.Failed Then
If Loader Is ForgeLoader AndAlso Loader.Error.Message = "无" Then Continue For 'Forge 无版本时不报错
Throw New Exception($"获取 {Pair.Key} 版本列表失败,请稍后再试,或使用 VPN 改善网络环境", Loader.Error)
ElseIf Loader.State = LoadState.Canceled Then
Throw New Exception($"获取 {Pair.Key} 版本列表被中断,请稍后再试,或使用 VPN 改善网络环境")
End If
Next
If LoaderList.Values.All(Function(Loader) Loader.State = LoadState.Finished OrElse Loader.State = LoadState.Failed) Then Exit Do
Task.Progress = 0.7 * LoaderList.Values.Average(Function(Loader) Loader.Progress)
Thread.Sleep(10)
Loop
If Task.IsCanceled Then Return
Task.Progress = 0.7
'Minecraft
MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
VanillaData = MinecraftVersions.OfType(Of JObject).FirstOrDefault(
Function(v) v("id").ToString.Replace("∞", "infinite") = MinecraftVersion.Replace("∞", "infinite"))
If VanillaData Is Nothing Then Throw New Exception($"Minecraft 列表中没有已安装的 {MinecraftVersion}")
VanillaData("id") = MinecraftVersion
'OptiFine
If Manifest IsNot Nothing Then
SelectedOptiFine = Components.OptiFineEntry?.ToObject(Of DlOptiFineListEntry)()
If SelectedOptiFine IsNot Nothing AndAlso Not DlOptiFineListLoader.Output.Value.Any(Function(v) v.FileName = SelectedOptiFine.FileName) Then
'如果 OptiFine 是 pre 版,可能只在 BMCLAPI 下有,官方源删了。如果是这种缺失数据的情况,换用 BMCLAPI 源作为输出
If DlOptiFineListBmclapiLoader.State <> LoadState.Finished Then DlOptiFineListBmclapiLoader.WaitForExit()
DlOptiFineListLoader.Output = DlOptiFineListBmclapiLoader.Output
End If
ElseIf OptiFineVersion IsNot Nothing AndAlso OptiFineVersion <> "未知版本" Then
SelectedOptiFine = DlOptiFineListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso
v.InstanceName.AfterLast("-OptiFine_").Replace("HD_U_", "") = OptiFineVersion.Replace("HD_U_", ""))
If SelectedOptiFine Is Nothing Then Throw New Exception($"OptiFine 列表中没有已安装的 {OptiFineVersion}")
End If
'Forge
If Components.ModLoaderVersions.ContainsKey(ModLoaders.Forge) Then
SelectedForge = ForgeLoader?.Output?.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.Forge) AndAlso
v.Category <> "universal" AndAlso v.Category <> "client" AndAlso (SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
If SelectedForge Is Nothing Then Throw New Exception($"Forge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Forge)}")
End If
'NeoForge
If Components.ModLoaderVersions.ContainsKey(ModLoaders.NeoForge) Then
SelectedNeoForge = DlNeoForgeListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.NeoForge))
If SelectedNeoForge Is Nothing Then Throw New Exception($"NeoForge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.NeoForge)}")
End If
'Fabric
If Components.ModLoaderVersions.ContainsKey(ModLoaders.Fabric) Then
Dim FabricEntry = DlFabricListLoader.Output.Value("loader").OfType(Of JObject).FirstOrDefault(
Function(v) v("version").ToString = Components.ModLoaderVersions(ModLoaders.Fabric),
Function(v) v("version").ToString.Replace("+build", "") = Components.ModLoaderVersions(ModLoaders.Fabric))
If FabricEntry Is Nothing OrElse Not DlFabricListLoader.Output.Value("game").OfType(Of JObject).
Any(Function(v) v("version").ToString = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")) Then
Throw New Exception($"Fabric 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Fabric)}")
End If
SelectedFabric = FabricEntry("version").ToString
End If
'LiteLoader
If Components.ModLoaderVersions.ContainsKey(ModLoaders.LiteLoader) Then
SelectedLiteLoader = DlLiteLoaderListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.JsonToken("version").ToString = Components.ModLoaderVersions(ModLoaders.LiteLoader))
If SelectedLiteLoader Is Nothing Then Throw New Exception($"LiteLoader 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.LiteLoader)}")
End If
'Fabric API
If Components.FabricApiVersion IsNot Nothing Then
SelectedFabricApi = DlFabricApiLoader.Output.FirstOrDefault(
Function(v) (v.Version = Components.FabricApiVersion OrElse v.Id = Components.FabricApiVersion OrElse
v.Display = Components.FabricApiVersion) AndAlso IsFabricApiCompatible(v, MinecraftVersion))
If SelectedFabricApi Is Nothing Then Throw New Exception($"Fabric API 列表中没有已安装的 {Components.FabricApiVersion}")
End If
'OptiFabric
If Components.OptiFabricVersion IsNot Nothing Then
SelectedOptiFabric = DlOptiFabricLoader.Output.FirstOrDefault(
Function(v) (v.Version = Components.OptiFabricVersion OrElse v.Id = Components.OptiFabricVersion OrElse
v.Display = Components.OptiFabricVersion) AndAlso IsOptiFabricCompatible(v, MinecraftVersion))
If SelectedOptiFabric Is Nothing Then Throw New Exception($"OptiFabric 列表中没有已安装的 {Components.OptiFabricVersion}")
End If
'扫描尚未识别的 Mod 组件
Dim ManagedFiles As New List(Of McInstallManifest.ManagedFile)
If Manifest IsNot Nothing Then ManagedFiles.AddRange(Manifest.ManagedFiles)
#Region "从 mods 文件夹中获取以 Mod 形式安装的组件信息"
'获取 mods 文件夹下的 jar 文件与 SHA1
Dim Mods As New Lazy(Of List(Of (File As FileInfo, Sha1 As String)))(
Function()
Dim ModsFolder = Task.Input.PathIndie & "mods\"
Dim Files = DirectoryUtils.EnumerateFiles(ModsFolder, searchPattern:="*.jar").ToList()
If SelectedLiteLoader IsNot Nothing Then 'LiteLoader 的 OptiFine 安装目录
Files.AddRange(DirectoryUtils.EnumerateFiles(Path.Combine(ModsFolder, MinecraftVersion), searchPattern:="*.jar"))
End If
Dim Entries As New List(Of (File As FileInfo, Sha1 As String))
For Each FilePath In Files
If Task.IsCanceled Then Return Entries
Dim File = FileUtils.GetInfo(FilePath)
Entries.Add((File, New LocalResourceFile(File).ModrinthHash))
Task.Progress = 0.7 + 0.28 * Entries.Count / Files.Count
Next
Return Entries
End Function)
'Fabric API
If SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing Then
Dim FabricApiList = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, MinecraftVersion)).ToList()
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
For Each Entry In Mods.Value
Dim Match = FabricApiList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
If Match Is Nothing Then Continue For
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("Fabric API", Matches)
If SelectedFile IsNot Nothing Then
SelectedFabricApi = SelectedFile.Value.Version
ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "FabricApi"))
End If
End If
If Task.IsCanceled Then Return
'OptiFabric
If SelectedFabric IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then
Dim OptiFabricList = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, MinecraftVersion)).ToList()
Dim HasMd5 = OptiFabricList.Any(Function(v) If(v.Hash, "").Length = 32) 'CurseForge 的 OptiFabric 信息可能提供 SHA1 或 MD5
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
For Each Entry In Mods.Value
Dim Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
If Match Is Nothing AndAlso HasMd5 Then
Dim Md5 = CryptographyUtils.ComputeFileHash(Entry.File.FullName, CryptographyUtils.HashMethod.Md5)
Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Md5)
End If
If Match Is Nothing Then Continue For
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("OptiFabric", Matches)
If SelectedFile IsNot Nothing Then
SelectedOptiFabric = SelectedFile.Value.Version
ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFabric"))
End If
End If
If Task.IsCanceled Then Return
'OptiFine
Dim OptiFineFiles As New List(Of (File As FileInfo, Sha1 As String, OriginalName As String))
If SelectedOptiFine Is Nothing AndAlso (SelectedForge IsNot Nothing OrElse SelectedLiteLoader IsNot Nothing OrElse SelectedFabric IsNot Nothing) Then 'OptiFine 只在有 Mod 加载器时才看在不在 mods 文件夹中
Static OptiFineHashes As JObject = FileUtils.ReadAsJson("Minecraft\OptiFineHashes.json", type:=GetType(Resource)) 'JSON 内容:{"SHA1": "原始文件名", ...}
OptiFineFiles = Mods.Value.
Select(Function(Entry) (Entry.File, Entry.Sha1, OriginalName:=OptiFineHashes(Entry.Sha1)?.ToString())). '用本地文件的 SHA1 匹配下载时的原始文件名,再根据原始文件名取得下载源信息
Where(Function(Entry) Entry.OriginalName IsNot Nothing).ToList()
Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As DlOptiFineListEntry))
For Each Entry In OptiFineFiles '遍历 hash 匹配到了的本地文件
Dim Match = DlOptiFineListLoader.Output.Value.FirstOrDefault(
Function(v) v.Inherit = MinecraftVersion AndAlso v.FileName = Entry.OriginalName AndAlso (SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
If Match Is Nothing Then
Hint($"无法从 OptiFine 列表还原 {Entry.File.Name} 对应的 {Entry.OriginalName},将跳过该文件。")
Continue For
End If
Matches.Add((Entry.File, Entry.Sha1, Match))
Next
Dim SelectedFile = SelectMod("OptiFine", Matches)
If SelectedFile IsNot Nothing Then
SelectedOptiFine = SelectedFile.Value.Version
Dim RelativePath = PathUtils.RemoveExtendedPrefix(SelectedFile.Value.File.FullName).Substring(Task.Input.PathIndie.Length).Replace("\", "/").Lower()
ManagedFiles.Add(New McInstallManifest.ManagedFile(RelativePath, SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFine"))
End If
End If
If Task.IsCanceled Then Return
#End Region
'保存 Manifest
If IsVanillaVersionReliable Then
Components = New McComponents(MinecraftVersion, Components.ModLoaderVersions,
If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)))
If Manifest Is Nothing OrElse Not Components.IsEquivalentTo(Manifest.Components) OrElse Not ManagedFiles.SequenceEqual(Manifest.ManagedFiles) Then
Configs.McInstallManifest.Set(New McInstallManifest(Versions.McInstallManifestVersion, Components, Date.UtcNow, ManagedFiles), Task.Input.Config)
Task.Input.Config.Save()
Logger.Info($"已为 {Task.Input.Name} 更新安装清单")
End If
End If
If Task.IsCanceled Then Return
ForgeVersionLoader = ForgeLoader
End Sub
Private Function SelectMod(Of T)(ComponentName As String, Matches As List(Of (File As FileInfo, Sha1 As String, Version As T))) As (File As FileInfo, Sha1 As String, Version As T)?
If Matches.Count = 0 Then Return Nothing
If Matches.Count = 1 Then Return Matches(0)
Dim Choice As Integer? = Nothing
RunInUiWait(Sub() Choice = MyMsgBoxSelect(Matches.Select(Function(Entry) CType(New MyRadioBox With {.Text = Entry.File.Name}, IMyRadio)), $"选择当前使用的 {ComponentName}"))
If Not Choice.HasValue Then Throw New Exception($"发现了多个 {ComponentName}")
Return Matches(Choice.Value)
End Function
''' <summary>
''' 初始化修改页
''' </summary>
Private Sub LoadInstanceFinished(Task As LoaderTask(Of McInstance, Object))
'还原原版信息
OriginalName = Task.Input.Name
CustomLogo = Nothing
If ReadIni(Task.Input.PathVersion & "PCL\Setup.ini", "LogoCustom", False) Then
CustomLogo = If(Task.Input.Logo.EndsWithF("PCL\Logo.png"), Task.Input.PathVersion & "PCL\Logo.png", Task.Input.Logo)
End If
Dim Item = McDownloadListItem(VanillaData, Nothing, False).Init()
VanillaName = Item.Title
VanillaIcon = Item.Logo
LabMinecraftVersion.Text = VanillaName
TextSelectName.Text = OriginalName
IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {
New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OriginalName})}
'修改页 UI
CardMinecraft.Visibility = Visibility.Visible
BtnBack.Visibility = Visibility.Collapsed
ImgLogo.Margin = New Thickness(15, 15, 0, 0)
TextSelectName.Margin = New Thickness(56, 16, 16, 0)
LabSelectName.Text = $"原名 :   {OriginalName}"
UpdateOriginalNameVisibility()
BtnStart.Text = "开始修改"
BtnStart.Logo = FrmInstanceLeft.ItemModify.Logo
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
PrepareSelectPage()
ReloadComponentLists()
'进入动画
PanMinecraft.Visibility = Visibility.Collapsed
PanMinecraft.Opacity = 0
PanMinecraft.RenderTransform = New TranslateTransform(-50, 0)
PanSelect.Opacity = 1
PanSelect.RenderTransform = New TranslateTransform
PanBack.IsHitTestVisible = True
PanAllBack.IsHitTestVisible = True
InitialTarget = GetSelectedComponents()
'设置原始数据
BtnMinecraftRestore.Tag = VanillaData
BtnOptiFineRestore.Tag = SelectedOptiFine
BtnForgeRestore.Tag = SelectedForge
BtnNeoForgeRestore.Tag = SelectedNeoForge
BtnFabricRestore.Tag = SelectedFabric
BtnFabricApiRestore.Tag = SelectedFabricApi
BtnOptiFabricRestore.Tag = SelectedOptiFabric
BtnLiteLoaderRestore.Tag = SelectedLiteLoader
ImgMinecraftOld.Source = VanillaIcon
LabMinecraftOld.Text = LabMinecraftVersion.Text
LabOptiFineOld.Text = If(SelectedOptiFine Is Nothing, "无", LabOptiFine.Text)
LabForgeOld.Text = If(SelectedForge Is Nothing, "无", LabForge.Text)
LabNeoForgeOld.Text = If(SelectedNeoForge Is Nothing, "无", LabNeoForge.Text)
LabFabricOld.Text = If(SelectedFabric Is Nothing, "无", LabFabric.Text)
LabLiteLoaderOld.Text = If(SelectedLiteLoader Is Nothing, "无", LabLiteLoader.Text)
LabFabricApiOld.Text = If(SelectedFabricApi Is Nothing, "无", LabFabricApi.Text)
LabOptiFabricOld.Text = If(SelectedOptiFabric Is Nothing, "无", LabOptiFabric.Text)
RefreshComparisons()
End Sub
Public Overrides Function GetSelectLogo() As String
Return If(CustomLogo, MyBase.GetSelectLogo())
End Function
Public Overrides Sub ShowMinecraftVersionList()
If IsInSelectPage Then
'把当前已选择的组件记录到 PendingComponents
Dim Components = GetSelectedComponents()
For Each Component In {
(Loader:=LoadOptiFine, Version:=Components.OptiFineVersion),
(Loader:=LoadFabricApi, Version:=Components.FabricApiVersion),
(Loader:=LoadOptiFabric, Version:=Components.OptiFabricVersion),
(Loader:=LoadForge, Version:=SelectedForge?.VersionName),
(Loader:=LoadNeoForge, Version:=SelectedNeoForge?.VersionName),
(Loader:=LoadFabric, Version:=SelectedFabric),
(Loader:=LoadLiteLoader, Version:=SelectedLiteLoader?.JsonToken?("version").ToString())}
'保留尚未加载完成的组件选择
If Component.Version IsNot Nothing Then PendingComponents(Component.Loader) = Component.Version
Next
End If
MyBase.ShowMinecraftVersionList()
End Sub
Public Overrides Sub ReloadSelected()
If IsRestoringComponents Then Return
If IsInSelectPage AndAlso VanillaName IsNot Nothing AndAlso PendingComponents.Any() Then
IsRestoringComponents = True
Try
Dim HasProcessed As Boolean = False
'优先恢复 Mod 加载器,再匹配兼容的附加组件
For Each LoadingComponent In {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader, LoadOptiFine, LoadFabricApi, LoadOptiFabric}
Dim Version As String = Nothing
If Not PendingComponents.TryGetValue(LoadingComponent, Version) Then Continue For
If Not LoadingComponent.State.IsLoader Then Continue For
Dim Loader = DirectCast(LoadingComponent.State, LoaderBase)
'检查前置组件
If LoadingComponent Is LoadOptiFine Then
If {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader}.Any(Function(Component) PendingComponents.ContainsKey(Component)) Then Continue For
ElseIf LoadingComponent Is LoadFabricApi Then
If PendingComponents.ContainsKey(LoadFabric) Then Continue For
ElseIf LoadingComponent Is LoadForge Then
If Not McVersion.IsStandardFormat(VanillaName) Then
PendingComponents.Remove(LoadingComponent)
Continue For
End If
If DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Input <> VanillaName Then Continue For
If Loader.State = LoadState.Failed AndAlso Loader.Error.Message = "无" Then
PendingComponents.Remove(LoadingComponent)
Continue For
End If
ElseIf LoadingComponent Is LoadOptiFabric Then
If PendingComponents.ContainsKey(LoadFabric) OrElse PendingComponents.ContainsKey(LoadOptiFine) Then Continue For
End If
If Loader.State <> LoadState.Finished Then Continue For
'移除已处理的组件
PendingComponents.Remove(LoadingComponent)
HasProcessed = True
'匹配目标 MC 版本下兼容的组件
If LoadingComponent Is LoadFabric Then
If SelectedFabric Is Nothing AndAlso LoadFabricGetError() Is Nothing Then
Dim Versions = DlFabricListLoader.Output.Value("loader").OfType(Of JObject)
SelectedFabric = If(Versions.FirstOrDefault(Function(v) v("version").ToString = Version), Versions.FirstOrDefault())?("version").ToString()
End If
ElseIf LoadingComponent Is LoadOptiFine Then
If SelectedOptiFine Is Nothing AndAlso LoadOptiFineGetError() Is Nothing Then
Dim Versions = DlOptiFineListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName AndAlso
(SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
SelectedOptiFine = If(Versions.FirstOrDefault(Function(v) v.InstanceName.AfterLast("-OptiFine_") = Version),
Versions.OrderBy(Function(v) v.IsPreview).
ThenByDescending(Function(v) v.DisplayName, Comparer(Of String).Create(AddressOf McUtils.CompareVersion)).FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadForge Then
If SelectedForge Is Nothing AndAlso LoadForgeGetError() Is Nothing Then
Dim Versions = DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Output.Where(
Function(v) v.Inherit = VanillaName AndAlso
v.Category <> "universal" AndAlso v.Category <> "client" AndAlso
(SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
SelectedForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.OrderByDescending(Function(v) v).FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadNeoForge Then
If SelectedNeoForge Is Nothing AndAlso LoadNeoForgeGetError() Is Nothing Then
Dim Versions = DlNeoForgeListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
SelectedNeoForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadLiteLoader Then
If SelectedLiteLoader Is Nothing AndAlso LoadLiteLoaderGetError() Is Nothing Then
Dim Versions = DlLiteLoaderListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
SelectedLiteLoader = If(Versions.FirstOrDefault(Function(v) v.JsonToken("version").ToString = Version), Versions.FirstOrDefault())
End If
ElseIf LoadingComponent Is LoadFabricApi Then
If SelectedFabricApi Is Nothing AndAlso LoadFabricApiGetError() Is Nothing Then
Dim Versions = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, VanillaName))
SelectedFabricApi = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
End If
If SelectedFabricApi IsNot Nothing Then AutoSelectedFabricApi = True
ElseIf LoadingComponent Is LoadOptiFabric Then
If SelectedOptiFabric Is Nothing AndAlso LoadOptiFabricGetError() Is Nothing Then
Dim Versions = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, VanillaName))
SelectedOptiFabric = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
End If
If SelectedOptiFabric IsNot Nothing Then AutoSelectedOptiFabric = True
End If
Next
'更新组件列表
If HasProcessed Then ReloadComponentLists()
Finally
IsRestoringComponents = False
End Try
End If
'刷新当前选择与原始版本的对比
MyBase.ReloadSelected()
If InitialTarget IsNot Nothing Then RefreshComparisons()
End Sub
Private Sub RefreshComparisons()
Dim Current = GetSelectedComponents()
ImgMinecraftNew.Source = VanillaIcon
'Minecraft
Dim IsMinecraftUnchanged = InitialTarget.MinecraftVersion = Current.MinecraftVersion
ShowComparison(PanMinecraftCompare, PanMinecraftInfo, InitialTarget.MinecraftVersion, Current.MinecraftVersion,
LabMinecraftVersion.Text, BtnMinecraftRestore, True)
'OptiFine
Dim OriginalOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
ShowComparison(PanOptiFineCompare, PanOptiFineInfo, InitialTarget.OptiFineVersion, Current.OptiFineVersion,
LabOptiFine.Text, BtnOptiFineRestore, InitialTarget.OptiFineEntry Is Nothing OrElse
IsMinecraftUnchanged AndAlso CardOptiFine.MainSwap.Visibility = Visibility.Visible AndAlso OriginalOptiFine IsNot Nothing AndAlso
(SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(OriginalOptiFine, SelectedForge)), BtnOptiFineClear)
'Fabric API
ShowComparison(PanFabricApiCompare, PanFabricApiInfo, InitialTarget.FabricApiVersion, Current.FabricApiVersion,
LabFabricApi.Text, BtnFabricApiRestore, InitialTarget.FabricApiVersion Is Nothing OrElse
IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso CardFabricApi.MainSwap.Visibility = Visibility.Visible, BtnFabricApiClear)
'OptiFabric
ShowComparison(PanOptiFabricCompare, PanOptiFabricInfo, InitialTarget.OptiFabricVersion, Current.OptiFabricVersion,
LabOptiFabric.Text, BtnOptiFabricRestore, InitialTarget.OptiFabricVersion Is Nothing OrElse
IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso CardOptiFabric.MainSwap.Visibility = Visibility.Visible, BtnOptiFabricClear)
'Mod Loader
For Each Loader In {
(Kind:=ModLoaders.Forge, Card:=CardForge, ComparePanel:=PanForgeCompare, CurrentPanel:=PanForgeInfo, Label:=LabForge, RestoreButton:=BtnForgeRestore, ClearButton:=BtnForgeClear),
(Kind:=ModLoaders.NeoForge, Card:=CardNeoForge, ComparePanel:=PanNeoForgeCompare, CurrentPanel:=PanNeoForgeInfo, Label:=LabNeoForge, RestoreButton:=BtnNeoForgeRestore, ClearButton:=BtnNeoForgeClear),
(Kind:=ModLoaders.Fabric, Card:=CardFabric, ComparePanel:=PanFabricCompare, CurrentPanel:=PanFabricInfo, Label:=LabFabric, RestoreButton:=BtnFabricRestore, ClearButton:=BtnFabricClear),
(Kind:=ModLoaders.LiteLoader, Card:=CardLiteLoader, ComparePanel:=PanLiteLoaderCompare, CurrentPanel:=PanLiteLoaderInfo, Label:=LabLiteLoader, RestoreButton:=BtnLiteLoaderRestore, ClearButton:=BtnLiteLoaderClear)}
Dim OldVersion As String = Nothing, NewVersion As String = Nothing
InitialTarget.ModLoaderVersions.TryGetValue(Loader.Kind, OldVersion)
Current.ModLoaderVersions.TryGetValue(Loader.Kind, NewVersion)
Dim CanRestore = OldVersion Is Nothing OrElse IsMinecraftUnchanged AndAlso
Loader.Card.MainSwap.Visibility = Visibility.Visible AndAlso Loader.RestoreButton.Tag IsNot Nothing
If Loader.Kind = ModLoaders.Forge AndAlso OldVersion IsNot Nothing AndAlso CanRestore Then
CanRestore = SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, DirectCast(Loader.RestoreButton.Tag, DlForgeVersionEntry))
End If
If Loader.Kind = ModLoaders.Fabric AndAlso OldVersion IsNot Nothing Then
Dim TargetVersion = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")
CanRestore = CardFabric.Visibility = Visibility.Visible AndAlso CardFabric.MainSwap.Visibility = Visibility.Visible AndAlso
DlFabricListLoader.Output.Value("loader").OfType(Of JObject).Any(Function(v) v("version").ToString = OldVersion) AndAlso
DlFabricListLoader.Output.Value("game").OfType(Of JObject).Any(Function(v) v("version").ToString = TargetVersion)
End If
ShowComparison(Loader.ComparePanel, Loader.CurrentPanel, OldVersion, NewVersion, Loader.Label.Text,
Loader.RestoreButton, CanRestore, Loader.ClearButton)
Next
UpdateStartButton()
End Sub
Private Sub ShowComparison(ComparePanel As Grid, CurrentPanel As FrameworkElement, OldVersion As String, NewVersion As String, NewText As String,
RestoreButton As MyIconButton, CanRestore As Boolean, Optional ClearButton As FrameworkElement = Nothing)
'获取不可安装的原因
Dim UnavailableReason As String = Nothing
If NewVersion Is Nothing Then
If RestoreButton Is BtnOptiFineRestore Then
UnavailableReason = LoadOptiFineGetError()
ElseIf RestoreButton Is BtnForgeRestore Then
UnavailableReason = If(McVersion.IsStandardFormat(VanillaName), LoadForgeGetError(), "无")
ElseIf RestoreButton Is BtnNeoForgeRestore Then
UnavailableReason = If(CardNeoForge.Visibility = Visibility.Collapsed, "无", LoadNeoForgeGetError())
ElseIf RestoreButton Is BtnFabricRestore Then
UnavailableReason = If(CardFabric.Visibility = Visibility.Collapsed, "无", LoadFabricGetError())
ElseIf RestoreButton Is BtnLiteLoaderRestore Then
UnavailableReason = If(CardLiteLoader.Visibility = Visibility.Collapsed, "无", LoadLiteLoaderGetError())
ElseIf RestoreButton Is BtnFabricApiRestore Then
UnavailableReason = LoadFabricApiGetError()
ElseIf RestoreButton Is BtnOptiFabricRestore Then
UnavailableReason = LoadOptiFabricGetError()
End If
NewText = If(UnavailableReason, "可以添加")
End If
'修改对比
Dim IsPending = NewVersion Is Nothing AndAlso (NewText = "获取中……" OrElse NewText.StartsWithF("获取失败:"))
Dim IsChanged = OldVersion <> NewVersion AndAlso Not IsPending
CanRestore = IsChanged AndAlso OldVersion IsNot Nothing AndAlso CanRestore
ComparePanel.Visibility = If(IsChanged, Visibility.Visible, Visibility.Collapsed)
CurrentPanel.Visibility = If(IsChanged, Visibility.Collapsed, Visibility.Visible)
'还原与取消安装按钮
RestoreButton.Visibility = If(CanRestore, Visibility.Visible, Visibility.Collapsed)
If ClearButton IsNot Nothing Then ClearButton.ToolTip = If(OldVersion Is Nothing, "取消安装", "移除")
Dim HasClearButton = ClearButton IsNot Nothing AndAlso ClearButton.Visibility = Visibility.Visible
RestoreButton.Margin = New Thickness(0, 5, If(CanRestore AndAlso HasClearButton, 60, 32), 0)
ComparePanel.Margin = New Thickness(ComparePanel.Margin.Left, ComparePanel.Margin.Top,
If(CanRestore AndAlso HasClearButton, 110, 80), ComparePanel.Margin.Bottom)
If Not IsChanged Then Return
'显示修改前已安装组件的对比卡片
If OldVersion IsNot Nothing Then
Dim Card = CType(ComparePanel.Parent, MyCard)
If Card.Visibility = Visibility.Collapsed Then
Card.IsSwapped = True
If Card.MainSwap IsNot Nothing Then Card.MainSwap.Visibility = Visibility.Collapsed
Card.Visibility = Visibility.Visible
End If
End If
Dim OldSide = CType(ComparePanel.Children(0), DockPanel)
Dim NewSide = CType(ComparePanel.Children(2), DockPanel)
OldSide.Children(0).Visibility = If(OldVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
CType(OldSide.Children(1), TextBlock).Margin = If(OldVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
NewSide.Children(0).Visibility = If(NewVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
Dim NewLabel = CType(NewSide.Children(1), TextBlock)
NewLabel.Margin = If(NewVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
NewLabel.Text = If(NewVersion Is Nothing, "移除", NewText)
If NewVersion Is Nothing AndAlso UnavailableReason IsNot Nothing Then
NewLabel.Inlines.Add(New Run($"({If(UnavailableReason = "无", "无可用版本", UnavailableReason)})") With {.Foreground = ColorGray4})
End If
End Sub
Private Sub RestoreComponent(sender As Object, e As EventArgs) Handles BtnMinecraftRestore.Click, BtnOptiFineRestore.Click, BtnForgeRestore.Click,
BtnNeoForgeRestore.Click, BtnFabricRestore.Click, BtnFabricApiRestore.Click, BtnOptiFabricRestore.Click, BtnLiteLoaderRestore.Click
Dim Button = DirectCast(sender, MyIconButton)
'恢复初始选择
If Button Is BtnMinecraftRestore Then
IsRestoringComponents = True
Try
PendingComponents.Clear()
MinecraftSelected(McDownloadListItem(DirectCast(Button.Tag, JObject), Nothing, False).Init(), Nothing)
If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
'恢复初始的组件
SelectedOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
SelectedForge = TryCast(BtnForgeRestore.Tag, DlForgeVersionEntry)
SelectedNeoForge = TryCast(BtnNeoForgeRestore.Tag, DlNeoForgeListEntry)
SelectedFabric = TryCast(BtnFabricRestore.Tag, String)
SelectedLiteLoader = TryCast(BtnLiteLoaderRestore.Tag, DlLiteLoaderListEntry)
SelectedFabricApi = TryCast(BtnFabricApiRestore.Tag, ResourceVersion)
SelectedOptiFabric = TryCast(BtnOptiFabricRestore.Tag, ResourceVersion)
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
Finally
IsRestoringComponents = False
End Try
ReloadComponentLists()
Return
ElseIf Button Is BtnOptiFineRestore Then
SelectedOptiFine = TryCast(Button.Tag, DlOptiFineListEntry)
AutoSelectedOptiFabric = SelectedOptiFine IsNot Nothing AndAlso SelectedFabric IsNot Nothing
If SelectedOptiFine Is Nothing Then SelectedOptiFabric = Nothing
ElseIf Button Is BtnForgeRestore Then
SelectedForge = TryCast(Button.Tag, DlForgeVersionEntry)
ElseIf Button Is BtnNeoForgeRestore Then
SelectedNeoForge = TryCast(Button.Tag, DlNeoForgeListEntry)
ElseIf Button Is BtnFabricRestore Then
SelectedFabric = TryCast(Button.Tag, String)
AutoSelectedFabricApi = SelectedFabric IsNot Nothing
AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
If SelectedFabric Is Nothing Then
SelectedFabricApi = Nothing
SelectedOptiFabric = Nothing
End If
ElseIf Button Is BtnFabricApiRestore Then
SelectedFabricApi = TryCast(Button.Tag, ResourceVersion)
AutoSelectedFabricApi = True
ElseIf Button Is BtnOptiFabricRestore Then
SelectedOptiFabric = TryCast(Button.Tag, ResourceVersion)
AutoSelectedOptiFabric = True
ElseIf Button Is BtnLiteLoaderRestore Then
SelectedLiteLoader = TryCast(Button.Tag, DlLiteLoaderListEntry)
End If
DirectCast(Button.Parent, MyCard).IsSwapped = True
'刷新组件
ReloadComponentLists()
End Sub
Private Sub CardMinecraft_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles CardMinecraft.MouseLeftButtonUp
ShowMinecraftVersionList()
e.Handled = True
End Sub
Private Sub UpdateStartButton() Handles TextSelectName.TextChanged, TextSelectName.ValidateChanged
BtnStart.IsEnabled = TextSelectName.IsValidated AndAlso InitialTarget IsNot Nothing AndAlso
Not PendingComponents.Any() AndAlso
(Not IsVanillaVersionReliable OrElse Not InitialTarget.IsEquivalentTo(GetSelectedComponents()) OrElse
Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal))
End Sub
Private Sub UpdateOriginalNameVisibility() Handles TextSelectName.TextChanged
Dim IsNameChanged = Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal)
Dim NewVisibility = If(IsNameChanged, Visibility.Visible, Visibility.Collapsed)
If LabSelectName.Visibility = NewVisibility Then Return
LabSelectName.Visibility = NewVisibility
BtnOriginalNameRestore.Visibility = NewVisibility
Dim RightMargin = If(IsNameChanged, 40, 16)
If IsInSelectPage AndAlso AniControlEnabled = 0 Then
AniStart(AaDouble(Sub(Delta) TextSelectName.Margin = New Thickness(56, 16, TextSelectName.Margin.Right + Delta, 0),
RightMargin - TextSelectName.Margin.Right, 150,, New AniEaseOutFluent), "DownloadModify Name Margin")
Else
AniStop("DownloadModify Name Margin")
TextSelectName.Margin = New Thickness(56, 16, RightMargin, 0)
End If
End Sub
Private Sub BtnOriginalNameRestore_Click(sender As Object, e As EventArgs) Handles BtnOriginalNameRestore.Click
TextSelectName.Text = OriginalName
IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
End Sub
Public Overrides Sub StartInstall(Request As McInstallRequest)
UpdateStartButton()
If Not BtnStart.IsEnabled Then Return
Dim TargetInstance = InitLoader.Input
Dim Target = Request.Components
Dim InstanceName = Request.NewInstanceName
Dim IsNameChanged = Not String.Equals(InstanceName, TargetInstance.Name, StringComparison.Ordinal)
Request.NewInstanceName = If(IsNameChanged, InstanceName, Nothing)
Request.InstanceFolder = TargetInstance.PathVersion
'警告
Dim Warnings As New List(Of String)
If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
Not PathUtils.IsSame(TargetInstance.PathIndie, TargetInstance.PathVersion) Then
Warnings.Add("该版本尚未开启版本隔离,可能与别的 MC 版本共用同一个 Mod 和存档文件夹。" & vbCrLf &
"修改版本后,已有的 Mod、存档等需要手动迁移到新的游戏目录。")
End If
Dim IsMinecraftVersionChanged = InitialTarget.MinecraftVersion <> Target.MinecraftVersion
If Not IsVanillaVersionReliable AndAlso Not IsMinecraftVersionChanged Then
Warnings.Add($"PCL 未能精准识别该版本,请确认它是不是 Minecraft {InitialTarget.MinecraftVersion},如果不正确,请取消并选择正确的版本!")
End If
If Not Configs.HintModifyInstanceWarning.Get() Then
Warnings.Add("如果该版本安装了 PCL 不兼容的加载器,或经过第三方修改,这些改动将会丢失!")
End If
Dim IsModLoaderChanged As Boolean = False
For Each Loader In InitialTarget.ModLoaderVersions.Keys.Union(Target.ModLoaderVersions.Keys)
Dim CurrentVersion As String = Nothing, TargetVersion As String = Nothing
Dim HasCurrent = InitialTarget.ModLoaderVersions.TryGetValue(Loader, CurrentVersion)
Dim HasTarget = Target.ModLoaderVersions.TryGetValue(Loader, TargetVersion)
If HasCurrent <> HasTarget OrElse CurrentVersion <> TargetVersion Then
IsModLoaderChanged = True
Exit For
End If
Next
If (IsModLoaderChanged OrElse IsMinecraftVersionChanged) AndAlso InitialTarget.ModLoaderVersions.Any Then
Warnings.Add("现有的 Mod、资源包等在修改后可能会不兼容!")
End If
If Warnings.Any() Then
If MyMsgBox("请注意:" & vbCrLf & "• " & Warnings.Join(vbCrLf & "• "), "修改确认", "开始修改", "取消", IsWarn:=True) <> 1 Then Return
End If
If Not Configs.HintModifyInstanceWarning.Get() Then Configs.HintModifyInstanceWarning.Set(True)
'开始修改
If IsVanillaVersionReliable AndAlso InitialTarget.IsEquivalentTo(Target) Then
'仅重命名
Try
Dim OldPath = TargetInstance.PathVersion
Dim RenamedInstance = TargetInstance.Rename(InstanceName)
If PageInstanceLeft.Instance IsNot Nothing AndAlso PathUtils.IsSame(PageInstanceLeft.Instance.PathVersion, OldPath) Then PageInstanceLeft.Instance = RenamedInstance
If McInstanceSelected IsNot Nothing AndAlso PathUtils.IsSame(McInstanceSelected.PathVersion, OldPath) Then McInstanceSelected = RenamedInstance
PageInstanceLeft.ReloadCurrentJava()
Hint("重命名成功!", HintType.Green)
LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
FrmMain.PageBack()
Catch ex As Exception
Logger.Error(ex, "重命名版本失败", LogBehavior.Alert)
End Try
Else
'实际修改
If McModificationStart(Request, TargetInstance) Then FrmMain.PageBack()
End If
End Sub
End Class
@@ -64,10 +64,10 @@ Public Class PageDownloadResourceDetail
If IsXSpecial Then Return 1
If IsYSpecial Then Return -1
'比较版本号
Dim VersionCodeSort = -CompareVersion(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", ""))
Dim VersionCodeSort = -McUtils.CompareVersion(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", ""))
If VersionCodeSort <> 0 Then Return VersionCodeSort
'比较全部
Return -CompareVersion(x, y)
Return -McUtils.CompareVersion(x, y)
End Function
Public Sub New(Optional Topmost As String = "")
Me.Topmost = If(Topmost, "")
@@ -98,7 +98,7 @@ Public Class PageDownloadResourceDetail
Dim UpdateFilters =
Sub()
Filters = Results.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, GroupedDrop, GroupedOld)).
Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
Distinct.OrderByDescending(Function(s) s, Comparer(Of String).Create(Function(x, y) McUtils.CompareVersion(x, y))).ToList
End Sub
'确定分组方式
@@ -174,7 +174,7 @@ GroupDone:
Dim Loaders As New List(Of String)
If Project.ModLoaders.Flags.Count > 1 AndAlso '工程至少有两个加载器
Version.ResourceType = ResourceTypes.Mod AndAlso '是 Mod
McVersion.IsFormatFit(VerName) Then '不是 “快照版本” 之类的
McVersion.IsStandardFormat(VerName) Then '不是 “快照版本” 之类的
Loaders.AddRange(Version.ModLoaders.Flags.Select(Function(f) f.ToString & " "))
End If
If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
@@ -206,16 +206,17 @@ GroupDone:
If Not Pair.Value.Any() Then Continue For
If Pair.Key = TargetCardName.Replace("(所选版本)", "") Then Continue For
'增加卡片
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(TargetResourceType = ResourceTypes.ModPack, 9, 8)} '9 是安装,8 是另存为
Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15)}
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
NewCard.Children.Add(NewStack)
NewCard.SwapControl = NewStack
NewCard.SetContentBuilder(AddressOf InitializeStackResourceVersions)
PanResults.Children.Add(NewCard)
'确定卡片是否默认展开
If Pair.Key = TargetCardName OrElse
(FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
MyCard.StackInstall(NewStack, If(TargetResourceType = ResourceTypes.ModPack, 9, 8), Pair.Key) '9 是安装,8 是另存为
NewCard.BuildContent()
Else
NewCard.IsSwapped = True
End If
@@ -240,13 +241,53 @@ GroupDone:
Logger.Error(ex, "可视化工程下载列表出错")
End Try
End Sub
''' <summary>
''' 根据卡片中的 StackPanel.Tag 将资源版本列表数据实例化为 UI。
''' </summary>
Private Sub InitializeStackResourceVersions(Stack As StackPanel)
Dim Versions = CType(Stack.Tag, List(Of ResourceVersion)).SortByComparison(Function(a, b) a.ReleaseDate > b.ReleaseDate)
Dim HasDuplicates = Versions.DistinctBy(Function(a) a.Display).Count <> Versions.Count
Dim Dependences As List(Of String)
'获取并添加前置信息
If TargetResourceType = ResourceTypes.ModPack Then
Dependences = New List(Of String) '整合包的前置信息会显示其中包含的一大堆 Mod,不是真正意义的前置
Else
Dependences = Versions.SelectMany(Function(f) f.Dependencies).Distinct.Ordered.ToList()
End If
If Dependences.Any() Then
Dependences = Dependences.Where(
Function(d)
If Not ResourceProject.Cache.ContainsKey(d) Then Logger.Warn($"未找到 ID {d} 的前置信息")
Return ResourceProject.Cache.ContainsKey(d)
End Function).ToList
'添加开头间隔
Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
'添加前置列表
For Each Dep In Dependences
Dim Item = ResourceProject.Cache(Dep).ToResourceItem(False, False)
Stack.Children.Add(Item)
Next
'添加结尾间隔
Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
End If
'实例化每个元素
For Each Version In Versions
If TargetResourceType = ResourceTypes.ModPack Then
Stack.Children.Add(Version.ToListItem(AddressOf Install_Click, AddressOf Save_Click, IsBadDisplay:=HasDuplicates))
Else
Stack.Children.Add(Version.ToListItem(AddressOf Save_Click, IsBadDisplay:=HasDuplicates))
End If
Next
Stack.Children.Add(New FrameworkElement With {.Height = 18})
Stack.Tag = Nothing
End Sub
Private Function GetGroupedVersionName(Name As String, GroupedByDrop As Boolean, FoldOld As Boolean) As String
Dim Drop = McVersion.VersionToDrop(Name)
If Name Is Nothing Then
Return "其他"
ElseIf Name.Contains("w") OrElse Drop = 209 Then
Return "快照版"
ElseIf Not McVersion.IsFormatFit(Name) OrElse (FoldOld AndAlso Drop < 120) Then
ElseIf Not McVersion.IsStandardFormat(Name) OrElse (FoldOld AndAlso Drop < 120) Then
Return "远古版"
ElseIf GroupedByDrop Then
Return McVersion.DropToVersion(Drop)
@@ -469,19 +510,23 @@ GroupDone:
'弹窗要求选择保存位置
Dim Target As String
Dim Ext As String = If(File.ResourceType = ResourceTypes.Mod,
If(File.FileName.EndsWithF(".litemod"), "litemod", "jar"),
If(File.FileName.EndsWithF(".mrpack"), "mrpack", "zip"))
If(PathUtils.GetExtension(File.FileName) = "litemod", "litemod", "jar"),
If(PathUtils.GetExtension(File.FileName) = "mrpack", "mrpack", "zip"))
Target = Dialogs.SaveFile("选择保存位置", FileName, DefaultFolder, {(Ext, Desc & "文件")})
If Target Is Nothing Then Return
'构造步骤加载器
Dim LoaderName As String = Desc & "下载:" & PathUtils.GetFileNameWithoutExtension(Target) & " "
If Target <> DefaultFolder Then CachedFolder(File.ResourceType) = PathUtils.RemoveLastPart(Target)
If Not PathUtils.IsSame(PathUtils.RemoveLastPart(Target), DefaultFolder) Then CachedFolder(File.ResourceType) = PathUtils.RemoveLastPart(Target)
Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderDownload("下载文件", {
File.ToNetFile(Target, If(TargetResourceType = ResourceTypes.Any, ResourceVersion.DownloadReason.Dependency, ResourceVersion.DownloadReason.Standalone))
}) With {.ProgressWeight = 6, .Block = True})
'启动
Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged =
Sub(MyLoader)
LoaderStateChangedHintOnly(MyLoader)
If MyLoader.State = LoadState.Finished AndAlso File.ResourceType = ResourceTypes.ModPack Then OpenExplorer(Target)
End Sub}
Loader.Start(1)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -510,29 +555,4 @@ GroupDone:
PageLoaderRestart(IsForceRestart:=True)
End Sub
''' <summary>
''' 预载包含大量 ProjectVersion 的卡片,添加必要的元素和前置列表。
''' </summary>
Public Shared Sub ResourceFilesCardPreload(Stack As StackPanel, Files As List(Of ResourceVersion))
'获取卡片对应的前置 ID
'如果为整合包就不会有 Dependencies 信息,所以不用管
Dim Deps As List(Of String) = Files.SelectMany(Function(f) f.Dependencies).Distinct.ToList()
Deps.Sort()
If Not Deps.Any() Then Return
Deps = Deps.Where(
Function(dep)
If Not ResourceProject.Cache.ContainsKey(dep) Then Logger.Warn($"未找到 ID {dep} 的前置信息")
Return ResourceProject.Cache.ContainsKey(dep)
End Function).ToList
'添加开头间隔
Stack.Children.Add(New TextBlock With {.Text = "前置资源", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
'添加前置列表
For Each Dep In Deps
Dim Item = ResourceProject.Cache(Dep).ToResourceItem(False, False)
Stack.Children.Add(Item)
Next
'添加结尾间隔
Stack.Children.Add(New TextBlock With {.Text = "版本列表", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 12, 0, 5)})
End Sub
End Class
@@ -90,11 +90,11 @@ Public Class PageResource
Next
Return ComboSearchLoader.Items(0)
End Function
If TargetInstance.Version.HasForge Then
If TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Forge) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge")
ElseIf TargetInstance.Version.HasFabric Then
ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.Fabric) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric")
ElseIf TargetInstance.Version.HasNeoForge Then
ElseIf TargetInstance.Version.ModLoaders.HasFlagF(ModLoaders.NeoForge) Then
ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
End If
TargetInstance = Nothing