This commit is contained in:
龙腾猫跃
2023-09-12 00:55:44 +08:00
parent f18ede2c7a
commit 3b8de89309
28 changed files with 615 additions and 451 deletions
+2 -2
View File
@@ -133,7 +133,7 @@
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate> <ControlTemplate>
<Border BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1.5" CornerRadius="4" MaxWidth="500" Background="#E5FFFFFF" Margin="0,0,4,4" SnapsToDevicePixels="True"> <Border Loaded="TooltipLoaded" Unloaded="TooltipUnloaded" BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1.5" CornerRadius="4" MaxWidth="500" Background="#E5FFFFFF" Margin="0,0,4,4" SnapsToDevicePixels="True">
<Border.Effect> <Border.Effect>
<DropShadowEffect Opacity="0.3" BlurRadius="4" ShadowDepth="2" Color="{DynamicResource ColorObject1}" /> <DropShadowEffect Opacity="0.3" BlurRadius="4" ShadowDepth="2" Color="{DynamicResource ColorObject1}" />
</Border.Effect> </Border.Effect>
@@ -147,7 +147,7 @@
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate> <ControlTemplate>
<Border BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1" CornerRadius="3" Background="White" Margin="0,0,4,4" SnapsToDevicePixels="True"> <Border Loaded="TooltipLoaded" Unloaded="TooltipUnloaded" BorderBrush="{DynamicResource ColorBrush1}" BorderThickness="1" CornerRadius="3" Background="White" Margin="0,0,4,4" SnapsToDevicePixels="True">
<TextBlock Text="{TemplateBinding ContentControl.Content}" TextWrapping="Wrap" Foreground="{DynamicResource ColorBrush1}" Margin="7,5" FontSize="12"/> <TextBlock Text="{TemplateBinding ContentControl.Content}" TextWrapping="Wrap" Foreground="{DynamicResource ColorBrush1}" Margin="7,5" FontSize="12"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>
@@ -247,4 +247,12 @@ Public Class Application
End Select End Select
End Sub End Sub
Public Shared ShowingTooltips As New List(Of Border)
Private Sub TooltipLoaded(sender As Border, e As EventArgs)
ShowingTooltips.Add(sender)
End Sub
Private Sub TooltipUnloaded(sender As Border, e As RoutedEventArgs)
ShowingTooltips.Remove(sender)
End Sub
End Class End Class
@@ -41,10 +41,6 @@
End Sub End Sub
Private IsLoad As Boolean = False Private IsLoad As Boolean = False
Private Sub MyCard_Loaded() Handles Me.Loaded Private Sub MyCard_Loaded() Handles Me.Loaded
'卡片的进入时动画已被页面通用切换动画替代
'If LoadAnimation AndAlso IsLoad Then
' StartHeightAnimation(Math.Min(50, ActualHeight), ActualHeight - Math.Min(50, ActualHeight), True)
'End If
If IsLoad Then Exit Sub If IsLoad Then Exit Sub
'初次加载限定 '初次加载限定
If Not Title = "" AndAlso IsNothing(MainTextBlock) Then If Not Title = "" AndAlso IsNothing(MainTextBlock) Then
@@ -16,9 +16,9 @@
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" /> <DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" />
</Border.Effect> </Border.Effect>
</Border> </Border>
<Border x:Name="PanProgress" CornerRadius="1000" BorderThickness="0.001" Background="#2AFFFFFF" ClipToBounds="True" Visibility="Collapsed"> <Border x:Name="PanProgress" CornerRadius="1000" BorderThickness="0.001" Background="#2FFFFFFF" ClipToBounds="True" Visibility="Collapsed">
<Border.Clip> <Border.Clip>
<RectangleGeometry x:Name="RectProgress" Rect="0,0,40,25" /> <RectangleGeometry x:Name="RectProgress" Rect="0,5,40,35" />
</Border.Clip> </Border.Clip>
</Border> </Border>
<Path x:Name="Path" Stretch="Uniform" Margin="12" Fill="{DynamicResource ColorBrush8}" RenderTransformOrigin="0.5,0.5"> <Path x:Name="Path" Stretch="Uniform" Margin="12" Fill="{DynamicResource ColorBrush8}" RenderTransformOrigin="0.5,0.5">
@@ -17,7 +17,7 @@
PanProgress.Visibility = Visibility.Collapsed PanProgress.Visibility = Visibility.Collapsed
Else Else
PanProgress.Visibility = Visibility.Visible PanProgress.Visibility = Visibility.Visible
RectProgress.Rect = New Rect(0, 0, 40, 40 * (1 - value)) RectProgress.Rect = New Rect(0, 40 * (1 - value), 40, 40 * value)
End If End If
End Set End Set
End Property End Property
@@ -179,7 +179,7 @@
AniStop("MyExtraButton Color " & Uuid) AniStop("MyExtraButton Color " & Uuid)
If IsMouseOver Then If IsMouseOver Then
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush2") PanColor.SetResourceReference(BackgroundProperty, "ColorBrush4")
Else Else
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3") PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3")
End If End If
+27 -21
View File
@@ -29,21 +29,28 @@
'逐个进入动画 '逐个进入动画
Dim AniList As New List(Of AniData) Dim AniList As New List(Of AniData)
Dim Id As Integer = 0, Delay As Integer = 0 Dim Id As Integer = 0, Delay As Integer = 0
Dim Controls = GetAllAnimControls() For Each Element As FrameworkElement In GetAllAnimControls(True)
For Each Element As FrameworkElement In Controls If Element.Visibility = Visibility.Collapsed Then
Element.Opacity = 0 '还原之前的隐藏动画可能导致的改变(#2436)
Element.RenderTransform = New TranslateTransform(-25, 0) Element.Opacity = 1
AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak))) Element.RenderTransform = New TranslateTransform(0, 0)
AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent)) If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak))) Else
If TypeOf Element Is MyListItem Then Element.Opacity = 0
AniList.Add(AaCode(Sub() Element.RenderTransform = New TranslateTransform(-25, 0)
CType(Element, MyListItem).IsMouseOverAnimationEnabled = True If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
CType(Element, MyListItem).RefreshColor(Me, New EventArgs) AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
End Sub, Delay + 280)) AniList.Add(AaTranslateX(Element, 5, 200, Delay, New AniEaseOutFluent))
AniList.Add(AaTranslateX(Element, 20, 300, Delay, New AniEaseOutBack(AniEasePower.Weak)))
If TypeOf Element Is MyListItem Then
AniList.Add(AaCode(Sub()
CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
CType(Element, MyListItem).RefreshColor(Me, New EventArgs)
End Sub, Delay + 280))
End If
Delay += Math.Max(8, 20 - Id) * 2.5
Id += 1
End If End If
Delay += Math.Max(8, 20 - Id) * 2.5
Id += 1
Next Next
AniStart(AniList, "PageLeft PageChange " & Uuid) AniStart(AniList, "PageLeft PageChange " & Uuid)
End If End If
@@ -74,23 +81,22 @@
End Sub End Sub
'遍历获取所有需要生成动画的控件 '遍历获取所有需要生成动画的控件
Private Function GetAllAnimControls() As List(Of FrameworkElement) Private Function GetAllAnimControls(Optional IgnoreInvisibility As Boolean = False) As List(Of FrameworkElement)
Dim AllControls As New List(Of FrameworkElement) Dim AllControls As New List(Of FrameworkElement)
GetAllAnimControls(AnimatedControl, AllControls) GetAllAnimControls(AnimatedControl, AllControls, IgnoreInvisibility)
Return AllControls Return AllControls
End Function End Function
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement)) Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean)
If Element.Visibility = Visibility.Collapsed Then Exit Sub If Not IgnoreInvisibility AndAlso Element.Visibility = Visibility.Collapsed Then Exit Sub
If TypeOf Element Is MyTextButton Then If TypeOf Element Is MyTextButton Then
AllControls.Add(Element) AllControls.Add(Element)
ElseIf TypeOf Element Is MyListItem Then ElseIf TypeOf Element Is MyListItem Then
CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
AllControls.Add(Element) AllControls.Add(Element)
ElseIf TypeOf Element Is ContentControl Then ElseIf TypeOf Element Is ContentControl Then
GetAllAnimControls(CType(Element, ContentControl).Content, AllControls) GetAllAnimControls(CType(Element, ContentControl).Content, AllControls, IgnoreInvisibility)
ElseIf TypeOf Element Is Panel Then ElseIf TypeOf Element Is Panel Then
For Each Element2 As FrameworkElement In CType(Element, Panel).Children For Each Element2 As FrameworkElement In CType(Element, Panel).Children
GetAllAnimControls(Element2, AllControls) GetAllAnimControls(Element2, AllControls, IgnoreInvisibility)
Next Next
Else Else
AllControls.Add(Element) AllControls.Add(Element)
@@ -15,8 +15,12 @@
'如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作 '如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作
Exit Sub Exit Sub
End If End If
PerformVerticalOffsetDelta(-e.Delta)
e.Handled = True e.Handled = True
PerformVerticalOffsetDelta(-e.Delta)
'关闭 Tooltip (#2552)
For Each TooltipBorder In Application.ShowingTooltips
AniStart(AaOpacity(TooltipBorder, -1, 100), $"Hide Tooltip {GetUuid()}")
Next
End Sub End Sub
Public Sub PerformVerticalOffsetDelta(Delta As Double) Public Sub PerformVerticalOffsetDelta(Delta As Double)
AniStart(AaDouble(Sub(AnimDelta As Double) AniStart(AaDouble(Sub(AnimDelta As Double)
+24
View File
@@ -10,6 +10,11 @@ Public Class FormMain
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String)) Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
'统计更新日志条目 '统计更新日志条目
#If BETA Then #If BETA Then
If LastVersion < 304 Then 'Release 2.6.8
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复开始或结束游戏时可能报错的 Bug"))
FeatureCount += 20
BugCount += 25
End If
If LastVersion < 302 Then 'Release 2.6.7 If LastVersion < 302 Then 'Release 2.6.7
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug"))
BugCount += 1 BugCount += 1
@@ -128,6 +133,11 @@ Public Class FormMain
'3:BUG+ IMP* FEAT- '3:BUG+ IMP* FEAT-
'2:BUG* IMP- '2:BUG* IMP-
'1:BUG- '1:BUG-
If LastVersion < 305 Then 'Snapshot 2.6.8
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复开始或结束游戏时可能报错的 Bug"))
FeatureCount += 20
BugCount += 25
End If
If LastVersion < 303 Then 'Snapshot 2.6.7 If LastVersion < 303 Then 'Snapshot 2.6.7
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug")) FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug"))
BugCount += 1 BugCount += 1
@@ -331,6 +341,7 @@ Public Class FormMain
End Sub End Sub
'窗口加载 '窗口加载
Private Declare Auto Function ChangeWindowMessageFilterEx Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal message As UInt32, ByVal action As UInt32, ByVal changeInfo As UInt32) As Boolean
Private IsWindowLoadFinished As Boolean = False Private IsWindowLoadFinished As Boolean = False
''' <summary> ''' <summary>
''' 是否为联机提权后自动重启。 ''' 是否为联机提权后自动重启。
@@ -442,6 +453,7 @@ Public Class FormMain
TimerMainStartRun() TimerMainStartRun()
'加载池 '加载池
RunInNewThread(Sub() RunInNewThread(Sub()
'EULA 提示
If Not Setup.Get("SystemEula") Then If Not Setup.Get("SystemEula") Then
Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明", Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明",
Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW")) Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW"))
@@ -451,6 +463,7 @@ Public Class FormMain
EndProgram(False) EndProgram(False)
End Select End Select
End If End If
'启动加载器池
Try Try
JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作 JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作
Thread.Sleep(200) Thread.Sleep(200)
@@ -460,11 +473,22 @@ Public Class FormMain
Catch ex As Exception Catch ex As Exception
Log(ex, "初始化加载池运行失败", LogLevel.Feedback) Log(ex, "初始化加载池运行失败", LogLevel.Feedback)
End Try End Try
'清理自动更新文件
Try Try
If File.Exists(Path & "PCL\Plain Craft Launcher 2.exe") Then File.Delete(Path & "PCL\Plain Craft Launcher 2.exe") If File.Exists(Path & "PCL\Plain Craft Launcher 2.exe") Then File.Delete(Path & "PCL\Plain Craft Launcher 2.exe")
Catch ex As Exception Catch ex As Exception
Log(ex, "清理自动更新文件失败") Log(ex, "清理自动更新文件失败")
End Try End Try
'开启管理员权限下的文件拖拽(#2531)
Try
If IsAdmin() Then
Log("[System] PCL 正以管理员权限运行")
ChangeWindowMessageFilterEx(Handle, &H233, 1, Nothing)
ChangeWindowMessageFilterEx(Handle, &H49, 1, Nothing)
End If
Catch ex As Exception
Log(ex, "开启管理员权限下的文件拖拽失败")
End Try
End Sub, "Start Loader", ThreadPriority.Lowest) End Sub, "Start Loader", ThreadPriority.Lowest)
Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms") Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
+20 -24
View File
@@ -11,12 +11,12 @@ Public Module ModBase
#Region "声明" #Region "声明"
'下列版本信息由更新器自动修改 '下列版本信息由更新器自动修改
Public Const VersionBaseName As String = "2.6.7" '不含分支前缀的显示用版本名 Public Const VersionBaseName As String = "2.6.8" '不含分支前缀的显示用版本名
Public Const VersionStandardCode As String = "2.6.7." & VersionBranchCode '标准格式的四段式版本号 Public Const VersionStandardCode As String = "2.6.8." & VersionBranchCode '标准格式的四段式版本号
#If BETA Then #If BETA Then
Public Const VersionCode As Integer = 302 'Release Public Const VersionCode As Integer = 304 'Release
#Else #Else
Public Const VersionCode As Integer = 303 'Snapshot Public Const VersionCode As Integer = 305 'Snapshot
#End If #End If
'自动生成的版本信息 '自动生成的版本信息
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -1242,23 +1242,7 @@ Re:
Public Function ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing) As Boolean Public Function ExtractFile(CompressFilePath As String, DestDirectory As String, Optional Encode As Encoding = Nothing) As Boolean
Try Try
Directory.CreateDirectory(DestDirectory) Directory.CreateDirectory(DestDirectory)
If CompressFilePath.EndsWith(".zip") OrElse CompressFilePath.EndsWith(".jar") OrElse CompressFilePath.EndsWith(".mrpack") Then If CompressFilePath.EndsWith(".gz") Then
'以 zip 方式解压
Using stream As New FileStream(CompressFilePath, FileMode.Open)
Using archive As New ZipArchive(stream, ZipArchiveMode.Read, False, If(Encode, Encoding.GetEncoding("GB18030")))
For Each entry As ZipArchiveEntry In archive.Entries
Dim destinationPath As String = IO.Path.Combine(DestDirectory, entry.FullName)
If destinationPath.EndsWith("\") OrElse destinationPath.EndsWith("/") Then
Directory.CreateDirectory(destinationPath)
Else
If File.Exists(destinationPath) Then File.Delete(destinationPath)
entry.ExtractToFile(destinationPath, True)
End If
Next
End Using
End Using
Return True
ElseIf CompressFilePath.EndsWith(".gz") Then
'以 gz 方式解压 '以 gz 方式解压
Dim stream As New GZipStream(New FileStream(CompressFilePath, FileMode.Open, FileAccess.ReadWrite), CompressionMode.Decompress) Dim stream As New GZipStream(New FileStream(CompressFilePath, FileMode.Open, FileAccess.ReadWrite), CompressionMode.Decompress)
Dim decompressedFile As New FileStream(DestDirectory & GetFileNameFromPath(CompressFilePath).ToLower.Replace(".tar", "").Replace(".gz", ""), FileMode.OpenOrCreate, FileAccess.Write) Dim decompressedFile As New FileStream(DestDirectory & GetFileNameFromPath(CompressFilePath).ToLower.Replace(".tar", "").Replace(".gz", ""), FileMode.OpenOrCreate, FileAccess.Write)
@@ -1271,7 +1255,19 @@ Re:
stream.Close() stream.Close()
Return True Return True
Else Else
Return False '以 zip 方式解压
Using Archive = ZipFile.Open(CompressFilePath, ZipArchiveMode.Read, If(Encode, Encoding.GetEncoding("GB18030")))
For Each Entry As ZipArchiveEntry In Archive.Entries
Dim DestinationPath As String = IO.Path.Combine(DestDirectory, Entry.FullName)
If DestinationPath.EndsWith("\") OrElse DestinationPath.EndsWith("/") Then
Continue For '不创建空文件夹
Else
Directory.CreateDirectory(GetPathFromFullPath(DestinationPath))
Entry.ExtractToFile(DestinationPath, True)
End If
Next
End Using
Return True
End If End If
Catch ex As Exception Catch ex As Exception
Log(ex, "尝试解压文件失败") Log(ex, "尝试解压文件失败")
@@ -1301,7 +1297,7 @@ Re:
Next Next
Temp = Directory.GetDirectories(Path) Temp = Directory.GetDirectories(Path)
For Each str As String In Temp For Each str As String In Temp
DeleteDirectory(str) DeleteDirectory(str, IgnoreIssue)
Next Next
Try Try
Directory.Delete(Path, True) Directory.Delete(Path, True)
@@ -1578,7 +1574,7 @@ Re:
''' 为字符串进行 XML 转义。 ''' 为字符串进行 XML 转义。
''' </summary> ''' </summary>
Public Function EscapeXML(Str As String) As String Public Function EscapeXML(Str As String) As String
Return Str.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("'", "&apos;").Replace("""", "&quot;") Return Str.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("'", "&apos;").Replace("""", "&quot;").Replace(vbCrLf, "&#xa;")
End Function End Function
'正则 '正则
@@ -437,10 +437,6 @@
For Each Loader In Loaders For Each Loader In Loaders
Loader.Abort() Loader.Abort()
Next Next
'从列表删除
SyncLock LoaderTaskbarLock
If LoaderTaskbar.Contains(Me) Then LoaderTaskbar.Remove(Me)
End SyncLock
End Sub) End Sub)
End Sub End Sub
@@ -571,6 +567,7 @@ Restart:
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRemove(Loader) If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRemove(Loader)
SyncLock LoaderTaskbarLock SyncLock LoaderTaskbarLock
LoaderTaskbar.Add(Loader) LoaderTaskbar.Add(Loader)
Log($"[Taskbar] {Loader.Name} 已加入任务列表")
End SyncLock End SyncLock
End Sub End Sub
Public Sub LoaderTaskbarProgressRefresh() Public Sub LoaderTaskbarProgressRefresh()
@@ -585,16 +582,13 @@ Restart:
If Loader.Show AndAlso Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False If Loader.Show AndAlso Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False
Next Next
'若单个任务已中止或全部任务已完成,则刷新并移除 '若单个任务已中止或全部任务已完成,则刷新并移除
Dim RemovedTask As New List(Of LoaderBase) For Each Task In LoaderTaskbar.ToList()
For i = 0 To LoaderTaskbar.Count - 1 If (IsAllDownloadTaskCompleted OrElse Task.State = LoadState.Aborted OrElse Task.State = LoadState.Waiting) AndAlso Task.Show Then
If (IsAllDownloadTaskCompleted OrElse LoaderTaskbar(i).State = LoadState.Aborted) AndAlso LoaderTaskbar(i).Show Then If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(Task)
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(LoaderTaskbar(i)) LoaderTaskbar.Remove(Task)
RemovedTask.Add(LoaderTaskbar(i)) Log($"[Taskbar] {Task.Name} 已移出任务列表")
End If End If
Next Next
For Each Task In RemovedTask
LoaderTaskbar.Remove(Task)
Next
End SyncLock End SyncLock
'更新平滑后的进度 '更新平滑后的进度
If NewProgress <= 0 OrElse NewProgress >= 1 OrElse LoaderTaskbarProgress > NewProgress Then If NewProgress <= 0 OrElse NewProgress >= 1 OrElse LoaderTaskbarProgress > NewProgress Then
@@ -625,8 +619,9 @@ Restart:
Try Try
Dim Total As Double = 0, Count As Integer = 0 Dim Total As Double = 0, Count As Integer = 0
SyncLock LoaderTaskbarLock SyncLock LoaderTaskbarLock
For i = 0 To LoaderTaskbar.Count - 1 For Each Task In LoaderTaskbar.ToList
Total += LoaderTaskbar(i).Progress If Not Task.Show Then Continue For
Total += Task.Progress
Count += 1 '避免多线程影响导致计数出错 Count += 1 '避免多线程影响导致计数出错
Next Next
End SyncLock End SyncLock
+11 -29
View File
@@ -28,7 +28,7 @@ Public Class MyBitmap
Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource
Dim Bitmap = Image.Pic Dim Bitmap = Image.Pic
Dim rect = New System.Drawing.Rectangle(0, 0, Bitmap.Width, Bitmap.Height) Dim rect = New System.Drawing.Rectangle(0, 0, Bitmap.Width, Bitmap.Height)
Dim bitmapData = Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb) Dim bitmapData = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Try Try
Dim size = rect.Width * rect.Height * 4 Dim size = rect.Width * rect.Height * 4
Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, Bitmap.HorizontalResolution, Bitmap.VerticalResolution, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride) Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, Bitmap.HorizontalResolution, Bitmap.VerticalResolution, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride)
@@ -104,45 +104,27 @@ Public Class MyBitmap
End Using End Using
End Sub End Sub
''' <summary>
''' 获取旋转的图片,这个方法不会导致原对象改变且会返回一个新的对象。图片长宽必须相等。
''' </summary>
''' <param name="angle">旋转角度(单位为角度)。</param>
Public Function Rotation(angle As Double) As System.Drawing.Bitmap
Dim img As System.Drawing.Image = Me.Pic
Dim bitSize As Single = img.Width
Dim bmp As New System.Drawing.Bitmap(CInt(bitSize), CInt(bitSize), img.PixelFormat)
bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)
Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
g.TranslateTransform(bitSize / 2, bitSize / 2)
g.RotateTransform(angle)
g.TranslateTransform(-bitSize / 2, -bitSize / 2)
g.DrawImage(img, New System.Drawing.Rectangle(0, 0, img.Width, img.Width))
End Using
Return bmp
End Function
''' <summary> ''' <summary>
''' 获取裁切的图片,这个方法不会导致原对象改变且会返回一个新的对象。 ''' 获取裁切的图片,这个方法不会导致原对象改变且会返回一个新的对象。
''' </summary> ''' </summary>
Public Function Clip(rect As System.Drawing.Rectangle) As System.Drawing.Bitmap Public Function Clip(X As Integer, Y As Integer, Width As Integer, Height As Integer) As MyBitmap
Dim img As System.Drawing.Image = Pic Dim bmp As New System.Drawing.Bitmap(Width, Height, Pic.PixelFormat)
Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height, img.PixelFormat) bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)
Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp) Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
g.DrawImageUnscaled(img, rect.X, rect.Y) g.TranslateTransform(-X, -Y)
g.DrawImage(Pic, New System.Drawing.Rectangle(0, 0, Pic.Width, Pic.Height))
End Using End Using
Return bmp Return bmp
End Function End Function
''' <summary> ''' <summary>
''' 获取左右翻转的图片,这个方法不会导致原对象改变。 ''' 获取旋转或翻转后的图片,这个方法不会导致原对象改变且会返回一个新的对象。
''' </summary> ''' </summary>
Public Function LeftRightFilp() As System.Drawing.Bitmap Public Function RotateFlip(Type As System.Drawing.RotateFlipType) As MyBitmap
Dim bmp As New System.Drawing.Bitmap(Pic) Dim bmp As New System.Drawing.Bitmap(Pic)
bmp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX) bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
bmp.RotateFlip(Type)
Return bmp Return bmp
End Function End Function
@@ -152,7 +134,7 @@ Public Class MyBitmap
Public Sub Save(FilePath As String) Public Sub Save(FilePath As String)
Dim encoder As BitmapEncoder = New PngBitmapEncoder() Dim encoder As BitmapEncoder = New PngBitmapEncoder()
encoder.Frames.Add(BitmapFrame.Create(Me)) encoder.Frames.Add(BitmapFrame.Create(Me))
Using fileStream = New System.IO.FileStream(FilePath, FileMode.Create) Using fileStream As New FileStream(FilePath, FileMode.Create)
encoder.Save(fileStream) encoder.Save(fileStream)
End Using End Using
End Sub End Sub
@@ -434,7 +434,7 @@
Dim NormalNameList = New List(Of String) Dim NormalNameList = New List(Of String)
For Each Name In NameLists For Each Name In NameLists
Dim LowerName As String = Name.ToLower Dim LowerName As String = Name.ToLower
If Name.ToUpper = Name Then If Name.ToUpper = Name AndAlso Name <> "FPS" AndAlso Name <> "HUD" Then
'缩写 '缩写
ExNameList.Add(Name) ExNameList.Add(Name)
ElseIf (LowerName.Contains("forge") OrElse LowerName.Contains("fabric") OrElse LowerName.Contains("quilt")) AndAlso ElseIf (LowerName.Contains("forge") OrElse LowerName.Contains("fabric") OrElse LowerName.Contains("quilt")) AndAlso
@@ -487,8 +487,10 @@ NoExName:
'将 “Forge” 等提示改为 “Forge 版” '将 “Forge” 等提示改为 “Forge 版”
If (Ex.ToLower.Contains("forge") OrElse Ex.ToLower.Contains("fabric") OrElse Ex.ToLower.Contains("quilt")) AndAlso Not Ex.Contains("版") AndAlso If (Ex.ToLower.Contains("forge") OrElse Ex.ToLower.Contains("fabric") OrElse Ex.ToLower.Contains("quilt")) AndAlso Not Ex.Contains("版") AndAlso
Ex.ToLower.Replace("forge", "").Replace("fabric", "").Replace("quilt", "").Length <= 3 Then Ex.ToLower.Replace("forge", "").Replace("fabric", "").Replace("quilt", "").Length <= 3 Then
Ex = Ex.Replace("Edition", "").Trim.Capitalize & " 版" Ex = Ex.Replace("Edition", "").Replace("edition", "").Trim.Capitalize & " 版"
End If End If
'将 “forge” 等词语的首字母大写
Ex = Ex.Replace("forge", "Forge").Replace("neo", "Neo").Replace("fabric", "Fabric").Replace("quilt", "Quilt")
ExName &= " | " & Ex.Trim ExName &= " | " & Ex.Trim
Next Next
If ExName = "" Then GoTo NoExName If ExName = "" Then GoTo NoExName
@@ -933,7 +935,10 @@ Retry:
Next Next
'更新结果 '更新结果
SyncLock ResultsLock SyncLock ResultsLock
RawResults.AddRange(ProjectList) For Each Project In ProjectList
If Task.Input.Type = CompType.Mod AndAlso Not Project.ModLoaders.Any() Then Continue For '过滤插件(#2458)
RawResults.Add(Project)
Next
End SyncLock End SyncLock
Storage.ModrinthOffset += ProjectList.Count Storage.ModrinthOffset += ProjectList.Count
Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer) Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer)
@@ -988,7 +993,7 @@ Retry:
Not Storage.Results.Any(Function(b) r.IsLike(b))).ToList Not Storage.Results.Any(Function(b) r.IsLike(b))).ToList
'加入列表 '加入列表
RealResults.AddRange(RawResults) RealResults.AddRange(RawResults)
Log($"[Comp] 去重后累计新增结果 {RealResults.Count} 个") Log($"[Comp] 去重、筛选后累计新增结果 {RealResults.Count} 个")
#End Region #End Region
@@ -127,7 +127,7 @@
End Try End Try
'导入其中的日志文件 '导入其中的日志文件
For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles.ToList()
Try Try
If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For
Dim Ext As String = TargetFile.Extension.ToLower Dim Ext As String = TargetFile.Extension.ToLower
@@ -137,6 +137,8 @@
Else Else
AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName, Encoding.UTF8).Split(vbCrLf.ToCharArray))) AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName, Encoding.UTF8).Split(vbCrLf.ToCharArray)))
End If End If
Else
File.Delete(TargetFile.FullName)
End If End If
Catch ex As Exception Catch ex As Exception
Log(ex, "导入单个日志文件失败") Log(ex, "导入单个日志文件失败")
@@ -235,6 +237,7 @@
End If End If
Case AnalyzeFileType.MinecraftLog Case AnalyzeFileType.MinecraftLog
LogMc = "" LogMc = ""
LogMcDebug = ""
'创建文件名词典 '创建文件名词典
Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String())) Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String()))
For Each SelectedFile In SelectedFiles For Each SelectedFile In SelectedFiles
@@ -272,11 +275,21 @@
Exit For Exit For
End If End If
Next Next
'查找 Debug Log
For Each FileName As String In {"debug.log", "debug log.txt"}
If FileNameDict.ContainsKey(FileName) Then
Dim CurrentLog = FileNameDict(FileName)
LogMcDebug += GetHeadTailLines(CurrentLog.Value, 1000, 0)
Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft Debug 日志")
Exit For
End If
Next
'检查错误 '检查错误
If LogMc = "" Then If LogMc = "" Then
LogMc = Nothing LogMc = Nothing
Throw New Exception("无法找到匹配的 Minecraft Log") Throw New Exception("无法找到匹配的 Minecraft Log")
End If End If
If LogMcDebug = "" Then LogMcDebug = Nothing
Case AnalyzeFileType.ExtraLog Case AnalyzeFileType.ExtraLog
'全部丢过去 '全部丢过去
For Each SelectedFile In SelectedFiles For Each SelectedFile In SelectedFiles
@@ -294,7 +307,7 @@
If ResultCount = 0 Then If ResultCount = 0 Then
Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志") Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志")
Else Else
Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析") Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogMcDebug Is Nothing, "", "游戏 Debug 日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析")
End If End If
Return ResultCount Return ResultCount
@@ -315,7 +328,7 @@
End Function End Function
'3:根据文本分析崩溃原因 '3:根据文本分析崩溃原因
Private LogMc As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing Private LogMc As String = Nothing, LogMcDebug As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing
Private LogAll As String Private LogAll As String
'可能导致崩溃的原因与附加信息 '可能导致崩溃的原因与附加信息
Private CrashReasons As New Dictionary(Of CrashReason, List(Of String)) Private CrashReasons As New Dictionary(Of CrashReason, List(Of String))
@@ -324,6 +337,7 @@
''' </summary> ''' </summary>
Private Enum CrashReason Private Enum CrashReason
Mod文件被解压 Mod文件被解压
MixinBootstrap缺失
内存不足 内存不足
使用JDK 使用JDK
显卡不支持OpenGL 显卡不支持OpenGL
@@ -344,9 +358,8 @@
ModMixin失败 ModMixin失败
Mod加载器报错 Mod加载器报错
Mod初始化失败 Mod初始化失败
崩溃日志堆栈分析发现关键字 堆栈分析发现关键字
崩溃日志堆栈分析发现Mod名称 堆栈分析发现Mod名称
MC日志堆栈分析发现关键字
OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
特定方块导致崩溃 特定方块导致崩溃
特定实体导致崩溃 特定实体导致崩溃
@@ -372,7 +385,6 @@
''' </summary> ''' </summary>
Public Sub Analyze(Optional Version As McVersion = Nothing) Public Sub Analyze(Optional Version As McVersion = Nothing)
Log("[Crash] 步骤 3:分析崩溃原因") Log("[Crash] 步骤 3:分析崩溃原因")
LogAll = (If(LogMc, "") & If(LogHs, "") & If(LogCrash, "")).ToLower
'1. 精准日志匹配,中/高优先级 '1. 精准日志匹配,中/高优先级
AnalyzeCrit1() AnalyzeCrit1()
@@ -381,37 +393,41 @@
If CrashReasons.Count > 0 Then GoTo Done If CrashReasons.Count > 0 Then GoTo Done
'2. 堆栈分析 '2. 堆栈分析
If LogAll.Contains("forge") OrElse LogAll.Contains("fabric") OrElse LogAll.Contains("liteloader") Then LogAll = (If(LogMc, "") & If(LogMcDebug, "") & If(LogHs, "") & If(LogCrash, ""))
'来源崩溃日志的分析 If LogAll.Contains("orge") OrElse LogAll.Contains("abric") OrElse LogAll.Contains("uilt") OrElse LogAll.Contains("iteloader") Then
Dim Keywords As New List(Of String)
'崩溃日志
If LogCrash IsNot Nothing Then If LogCrash IsNot Nothing Then
Log("[Crash] 开始进行崩溃日志堆栈分析") Log("[Crash] 开始进行崩溃日志堆栈分析")
Dim StackLogs As String = LogCrash.Split("System Details").First Dim StackLogs As String = LogCrash.Split("System Details").First
Dim Keywords = AnalyzeStackKeyword(StackLogs) Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
If Keywords.Count > 0 Then
Dim Names = AnalyzeModName(Keywords)
If Names Is Nothing Then
AppendReason(CrashReason.崩溃日志堆栈分析发现关键字, Keywords)
Else
AppendReason(CrashReason.崩溃日志堆栈分析发现Mod名称, Names)
End If
GoTo Done
End If
End If End If
'来自 Minecraft 日志的分析 'Minecraft 日志
If LogMc IsNot Nothing Then If LogMc IsNot Nothing Then
Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)") Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)")
Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项") Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项")
If Fatals.Count > 0 Then If Fatals.Count > 0 Then
Dim Keywords As New List(Of String)
For Each Fatal In Fatals For Each Fatal In Fatals
Keywords.AddRange(AnalyzeStackKeyword(Fatal)) Keywords.AddRange(AnalyzeStackKeyword(Fatal))
Next Next
If Keywords.Count > 0 Then
AppendReason(CrashReason.MC日志堆栈分析发现关键字, Keywords.Distinct.ToList)
GoTo Done
End If
End If End If
End If End If
'虚拟机日志
If LogHs IsNot Nothing Then
Log("[Crash] 开始进行虚拟机堆栈分析")
Dim StackLogs As String = LogHs.Split("Registers:").First.Split("T H R E A D").Last
Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
End If
'Mod 名称分析
If Keywords.Count > 0 Then
Dim Names = AnalyzeModName(Keywords)
If Names Is Nothing Then
AppendReason(CrashReason.堆栈分析发现关键字, Keywords)
Else
AppendReason(CrashReason.堆栈分析发现Mod名称, Names)
End If
GoTo Done
End If
Else Else
Log("[Crash] 可能并未安装 Mod,不进行堆栈分析") Log("[Crash] 可能并未安装 Mod,不进行堆栈分析")
End If End If
@@ -565,22 +581,6 @@ Done:
'游戏日志分析 '游戏日志分析
If LogMc IsNot Nothing Then If LogMc IsNot Nothing Then
'Mixin 崩溃
If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("MixinApplyError") OrElse
LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then
Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^./ ]+(?=\] from)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=for mod )[^./ ]+(?= failed)")
'兜底名称判断
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "[^./ \]]+(?=.mixins.json")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=mixins.)[^./ \]]+(?=.json")
AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray)))
End If
'常规信息 '常规信息
If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf)) If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
@@ -594,6 +594,24 @@ Done:
'游戏日志分析 '游戏日志分析
If LogMc IsNot Nothing Then If LogMc IsNot Nothing Then
'Mixin 崩溃
If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") OrElse LogMc.Contains("MixinApplyError") OrElse
LogMc.Contains("mixin.injection.throwables.") OrElse LogMc.Contains(".mixins.json] FAILED during )") Then
Dim ModId As String = RegexSeek(LogMc, "(?<=in )[^./ ]+(?=.mixins.json.+failed injection check)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=in mixins.)[^./ ]+(?=.json.+failed injection check)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in )[^./ ]+(?=.mixins.json)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed .+ in mixins.)[^./ ]+(?=.json)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= failed mixins.)[^./ ]+(?=.json:)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[)[^./ ]+(?=.mixins.json\] FAILED during )")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<= in config \[mixins.)[^./ ]+(?=.json\] FAILED during )")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^./ ]+(?=\] from)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=for mod )[^./ ]+(?= failed)")
'兜底名称判断
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "[^./ \]]+(?=.mixins.json)")
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=mixins.)[^./ \]]+(?=.json)")
AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(If(ModId, "").TrimEnd((vbCrLf & " ").ToCharArray)))
End If
'极短的程序输出
If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then
AppendReason(CrashReason.极短的程序输出, LogMc) AppendReason(CrashReason.极短的程序输出, LogMc)
End If End If
@@ -667,7 +685,7 @@ NextStack:
End Function End Function
''' <summary> ''' <summary>
''' 根据 Mod 关键词与详细信息(崩溃报告第二部分)与,获取实际的 Mod 名称。 ''' 根据 Mod 关键词尝试获取实际的 Mod 名称。
''' 若失败则返回 Nothing。 ''' 若失败则返回 Nothing。
''' </summary> ''' </summary>
Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String) Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String)
@@ -682,60 +700,95 @@ NextStack:
Next Next
Keywords = RealKeywords Keywords = RealKeywords
'获取崩溃报告对应部分 '从崩溃报告获取 Mod 信息
If LogCrash Is Nothing Then Return Nothing If LogCrash IsNot Nothing AndAlso LogCrash.Contains("A detailed walkthrough of the error") Then
If Not LogCrash.Contains("A detailed walkthrough of the error") Then Return Nothing Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨")
Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨") Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
If IsFabricDetail Then
Details = Details.Replace("Fabric Mods", "¨")
Log("[Crash] 检测到 Fabric Mod 信息格式")
End If
Details = Details.Split("¨").Last
'[Forge] 获取所有包含 .jar 的行
'[Fabric] 获取所有包含 Mod 信息的行
Dim ModNameLines As New List(Of String)
For Each Line In Details.Split(vbLf)
If Line.ToLower.Contains(".jar") OrElse
(IsFabricDetail AndAlso Line.StartsWith(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line)
Next
Log("[Crash] 找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
'获取 Mod ID 与关键词的匹配行
Dim HintLines As New List(Of String)
For Each KeyWord As String In Keywords
For Each ModString As String In ModNameLines
Dim RealModString As String = ModString.ToLower.Replace("_", "")
If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For
If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For
HintLines.Add(ModString.Trim(vbCrLf.ToCharArray))
Exit For
Next
Next
HintLines = HintLines.Distinct.ToList
Log("[Crash] 找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
For Each ModName As String In HintLines
Log("[Crash] - " & ModName)
Next
'从 Mod 匹配行中提取 .jar 文件的名称
For Each Line As String In HintLines
Dim Name As String
If IsFabricDetail Then If IsFabricDetail Then
Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)") Details = Details.Replace("Fabric Mods", "¨")
Else Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式")
Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
End If End If
If Name IsNot Nothing Then ModFileNames.Add(Name) Details = Details.Split("¨").Last
Next
ModFileNames = ModFileNames.Distinct.ToList
Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
For Each ModFileName As String In ModFileNames
Log("[Crash] - " & ModFileName)
Next
Return If(ModFileNames.Count = 0, Nothing, ModFileNames) '[Forge] 获取所有包含 .jar 的行
'[Fabric] 获取所有包含 Mod 信息的行
Dim ModNameLines As New List(Of String)
For Each Line In Details.Split(vbLf)
If Line.ToLower.Contains(".jar") OrElse
(IsFabricDetail AndAlso Line.StartsWith(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line)
Next
Log("[Crash] 崩溃报告中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
'获取 Mod ID 与关键词的匹配行
Dim HintLines As New List(Of String)
For Each KeyWord As String In Keywords
For Each ModString As String In ModNameLines
Dim RealModString As String = ModString.ToLower.Replace("_", "")
If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For
If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For
HintLines.Add(ModString.Trim(vbCrLf.ToCharArray))
Exit For
Next
Next
HintLines = HintLines.Distinct.ToList
Log("[Crash] 崩溃报告中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
For Each ModLine As String In HintLines
Log("[Crash] - " & ModLine)
Next
'从 Mod 匹配行中提取 .jar 文件的名称
For Each Line As String In HintLines
Dim Name As String
If IsFabricDetail Then
Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)")
Else
Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
End If
If Name IsNot Nothing Then ModFileNames.Add(Name)
Next
End If
'从 debug.log 获取 Mod 信息
If LogMcDebug IsNot Nothing Then
'Forge: Found valid mod file YungsBetterStrongholds-1.20-Forge-4.0.1.jar with {betterstrongholds} mods - versions {1.20-Forge-4.0.1}
Dim ModNameLines As List(Of String) = RegexSearch(LogMcDebug, "(?<=valid mod file ).*", RegularExpressions.RegexOptions.Multiline)
Log("[Crash] Debug 信息中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
'获取 Mod ID 与关键词的匹配行
Dim HintLines As New List(Of String)
For Each KeyWord As String In Keywords
For Each ModString As String In ModNameLines
If ModString.Contains($"{{{KeyWord}}}") Then HintLines.Add(ModString)
Next
Next
HintLines = HintLines.Distinct.ToList
Log("[Crash] Debug 信息中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
For Each ModLine As String In HintLines
Log("[Crash] - " & ModLine)
Next
'从 Mod 匹配行中提取 .jar 文件的名称
For Each Line As String In HintLines
Dim Name As String
Name = RegexSeek(Line, ".*(?= with)")
If Name IsNot Nothing Then ModFileNames.Add(Name)
Next
End If
'输出
ModFileNames = ModFileNames.Distinct.ToList
If ModFileNames.Count = 0 Then
Return Nothing
Else
Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
For Each ModFileName As String In ModFileNames
Log("[Crash] - " & ModFileName)
Next
Return ModFileNames
End If
End Function End Function
''' <summary> ''' <summary>
''' 尝试获取 Mod 名称,若失败则返回原关键字。 ''' 尝试获取 Mod 名称,若失败则返回原关键字。
@@ -841,26 +894,26 @@ NextStack:
Case CrashReason.Mod名称包含特殊字符 Case CrashReason.Mod名称包含特殊字符
Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。") Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。")
Case CrashReason.MixinBootstrap缺失 Case CrashReason.MixinBootstrap缺失
Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap 解决。若安装后崩溃,尝试在该文件名前添加英文“!”后再试") Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap。若安装后依然崩溃,可以尝试在文件名前添加英文感叹号。")
Case CrashReason.使用32位Java导致JVM无法分配足够多的内存 Case CrashReason.使用32位Java导致JVM无法分配足够多的内存
If Environment.Is64BitOperatingSystem Then If Environment.Is64BitOperatingSystem Then
Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。") Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
Else Else
Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h") Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
End If End If
Case CrashReason.崩溃日志堆栈分析发现关键字, CrashReason.MC日志堆栈分析发现关键字
If Additional.Count = 1 Then
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
Else
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
End If
Case CrashReason.Mod缺少前置或MC版本错误 Case CrashReason.Mod缺少前置或MC版本错误
If Additional.Count > 0 Then If Additional.Count > 0 Then
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n未满足的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。") Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n未满足的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
Else Else
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h") Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
End If End If
Case CrashReason.崩溃日志堆栈分析发现Mod名称 Case CrashReason.堆栈分析发现关键字
If Additional.Count = 1 Then
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道它对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
Else
Results.Add("你的游戏遇到了一些问题,这可能是某些 Mod 所引起的,PCL 找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道这些关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
End If
Case CrashReason.堆栈分析发现Mod名称
If Additional.Count = 1 Then If Additional.Count = 1 Then
Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h") Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h")
Else Else
@@ -1141,14 +1141,11 @@ SystemBrowser:
'1.15:Java 8 - 15 '1.15:Java 8 - 15
MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 15, 999, 999) MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 15, 999, 999)
ElseIf VersionSortBoolean(McVersionCurrent.Version.ForgeVersion, "34.0.0") AndAlso VersionSortBoolean("36.2.25", McVersionCurrent.Version.ForgeVersion) Then ElseIf VersionSortBoolean(McVersionCurrent.Version.ForgeVersion, "34.0.0") AndAlso VersionSortBoolean("36.2.25", McVersionCurrent.Version.ForgeVersion) Then
'1.16.X,Forge 34.X ~ 36.2.25:最高 Java 8u320 '1.16,Forge 34.X ~ 36.2.25:最高 Java 8u320
MaxVer = New Version(1, 8, 0, 320) MaxVer = New Version(1, 8, 0, 320)
ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 99 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305 ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 19 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305
'1.18+:若安装了 OptiFine,最高 Java 18 '1.18:若安装了 OptiFine,最高 Java 18
MaxVer = New Version(1, 18, 999, 999) MaxVer = New Version(1, 18, 999, 999)
ElseIf McVersionCurrent.Version.McCodeMain >= 19 AndAlso McVersionCurrent.Version.McCodeMain < 99 Then
'1.19+:最高 Java 19
MaxVer = New Version(1, 19, 999, 999)
End If End If
End If End If
@@ -1753,6 +1750,15 @@ NextVersion:
'更新 options.txt '更新 options.txt
Dim SetupFileAddress As String = McVersionCurrent.PathIndie & "options.txt" Dim SetupFileAddress As String = McVersionCurrent.PathIndie & "options.txt"
If Not File.Exists(SetupFileAddress) Then
'Yosbr Mod 兼容(#2385):https://www.curseforge.com/minecraft/mc-mods/yosbr
Dim YosbrFileAddress As String = McVersionCurrent.PathIndie & "config\yosbr\options.txt"
If File.Exists(YosbrFileAddress) Then
McLaunchLog("将修改 Yosbr Mod 中的 options.txt")
SetupFileAddress = YosbrFileAddress
WriteIni(SetupFileAddress, "lang", "none") '忽略默认语言
End If
End If
Try Try
'语言 '语言
'1.0- :没有语言选项 '1.0- :没有语言选项
@@ -1773,11 +1779,11 @@ NextVersion:
WriteIni(SetupFileAddress, "lang", RequiredLang) WriteIni(SetupFileAddress, "lang", RequiredLang)
McLaunchLog($"已将语言从 {CurrentLang} 修改为 {RequiredLang}") McLaunchLog($"已将语言从 {CurrentLang} 修改为 {RequiredLang}")
End If End If
'如果是初次设置,一并修改 forceUnicodeFont ''如果是初次设置,一并修改 forceUnicodeFont
If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then 'If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then
WriteIni(SetupFileAddress, "forceUnicodeFont", "true") ' WriteIni(SetupFileAddress, "forceUnicodeFont", "true")
McLaunchLog("已开启 forceUnicodeFont") ' McLaunchLog("已开启 forceUnicodeFont")
End If 'End If
'窗口 '窗口
Select Case Setup.Get("LaunchArgumentWindowType") Select Case Setup.Get("LaunchArgumentWindowType")
Case 0 '全屏 Case 0 '全屏
@@ -1861,7 +1867,7 @@ NextVersion:
Dim Skin As New MyBitmap(PathAppdata & "CustomSkin.png") Dim Skin As New MyBitmap(PathAppdata & "CustomSkin.png")
If (McVersionCurrent.Version.McCodeMain = 6 OrElse McVersionCurrent.Version.McCodeMain = 7) AndAlso Skin.Pic.Height = 64 Then If (McVersionCurrent.Version.McCodeMain = 6 OrElse McVersionCurrent.Version.McCodeMain = 7) AndAlso Skin.Pic.Height = 64 Then
McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪") McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪")
Skin = Skin.Clip(New System.Drawing.Rectangle(0, 0, 64, 32)) Skin = Skin.Clip(0, 0, 64, 32)
End If End If
Skin.Save(Path & "PCL\CustomSkin_Cliped.png") Skin.Save(Path & "PCL\CustomSkin_Cliped.png")
'构建压缩文件 '构建压缩文件
@@ -1,4 +1,6 @@
Public Module ModModpack Imports System.IO.Compression
Public Module ModModpack
'触发整合包安装的外部接口 '触发整合包安装的外部接口
''' <summary> ''' <summary>
@@ -109,7 +111,7 @@
'整合包缓存清理 '整合包缓存清理
Private IsInstallCacheCleared As Boolean = False Private IsInstallCacheCleared As Boolean = False
Private IsInstallCacheClearing As Boolean = False Private IsInstallCacheClearing As Boolean = False
Private Sub UnpackFiles(InstallTemp As String, FileAddress As String) Private Sub UnpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase)
'清理缓存文件夹 '清理缓存文件夹
If Not IsInstallCacheCleared Then If Not IsInstallCacheCleared Then
IsInstallCacheCleared = True IsInstallCacheCleared = True
@@ -135,9 +137,8 @@
Try Try
Retry: Retry:
'完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试 '完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试
Directory.CreateDirectory(InstallTemp)
DeleteDirectory(InstallTemp) DeleteDirectory(InstallTemp)
Compression.ZipFile.ExtractToDirectory(FileAddress, InstallTemp, Encode) ExtractFile(FileAddress, InstallTemp, Encode)
Catch ex As Exception Catch ex As Exception
Log(ex, "第 " & RetryCount & " 次解压尝试失败") Log(ex, "第 " & RetryCount & " 次解压尝试失败")
If TypeOf ex Is ArgumentException Then If TypeOf ex Is ArgumentException Then
@@ -146,6 +147,7 @@ Retry:
End If End If
If RetryCount < 5 Then If RetryCount < 5 Then
Thread.Sleep(RetryCount * 2000) Thread.Sleep(RetryCount * 2000)
If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Exit Sub
RetryCount += 1 RetryCount += 1
GoTo Retry GoTo Retry
Else Else
@@ -215,13 +217,15 @@ Retry:
If OverrideHome <> "" Then If OverrideHome <> "" Then
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
UnpackFiles(InstallTemp, FileAddress) UnpackFiles(InstallTemp, FileAddress, Task)
Task.Progress = 0.5 Task.Progress = 0.5
Dim OverridePath As String = InstallTemp & ArchiveBaseFolder & OverrideHome
'复制结果 '复制结果
If Directory.Exists(InstallTemp & ArchiveBaseFolder & OverrideHome) Then If Directory.Exists(OverridePath) Then
CopyDirectory(InstallTemp & ArchiveBaseFolder & OverrideHome, PathMcFolder & "versions\" & VersionName) CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName)
Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}")
Else Else
Log("[ModPack] 整合包中未找到 override 目录,已跳过") Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}")
End If End If
Task.Progress = 0.9 Task.Progress = 0.9
'开启版本隔离 '开启版本隔离
@@ -393,7 +397,7 @@ Retry:
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
UnpackFiles(InstallTemp, FileAddress) UnpackFiles(InstallTemp, FileAddress, Task)
Task.Progress = 0.5 Task.Progress = 0.5
'复制 overrides 文件夹和 client-overrides 文件夹 '复制 overrides 文件夹和 client-overrides 文件夹
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
@@ -425,7 +429,7 @@ Retry:
End Select End Select
End If End If
'添加下载文件 '添加下载文件
FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString).ToArray, FileList.Add(New NetFile(File("downloads").Select(Function(t) t.ToString.Replace("://edge.forgecdn", "://media.forgecdn")).ToArray, '修复 #2390
PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString, PathMcFolder & "versions\" & VersionName & "\" & File("path").ToString,
New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString))) New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString)))
Next Next
@@ -520,7 +524,7 @@ Retry:
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
UnpackFiles(InstallTemp, FileAddress) UnpackFiles(InstallTemp, FileAddress, Task)
Task.Progress = 0.5 Task.Progress = 0.5
'复制结果 '复制结果
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
@@ -619,7 +623,7 @@ Retry:
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
UnpackFiles(InstallTemp, FileAddress) UnpackFiles(InstallTemp, FileAddress, Task)
Task.Progress = 0.5 Task.Progress = 0.5
'复制结果 '复制结果
If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then
@@ -714,7 +718,7 @@ Retry:
Dim InstallLoaders As New List(Of LoaderBase) Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件", InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer)) Sub(Task As LoaderTask(Of String, Integer))
UnpackFiles(InstallTemp, FileAddress) UnpackFiles(InstallTemp, FileAddress, Task)
Task.Progress = 0.5 Task.Progress = 0.5
'复制结果 '复制结果
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
@@ -805,7 +809,7 @@ Retry:
If String.IsNullOrWhiteSpace(NewName) Then Exit Sub If String.IsNullOrWhiteSpace(NewName) Then Exit Sub
'解压 '解压
Hint("正在解压压缩包……") Hint("正在解压压缩包……")
UnpackFiles(TargetFolder, FileAddress) UnpackFiles(TargetFolder, FileAddress, Nothing)
'加入文件夹列表 '加入文件夹列表
PageSelectLeft.AddFolder(TargetFolder, NewName, False) PageSelectLeft.AddFolder(TargetFolder, NewName, False)
Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish) Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish)
+2 -2
View File
@@ -25,7 +25,7 @@
For Each File In My.Computer.FileSystem.GetFiles(Path & "PCL\Musics\", FileIO.SearchOption.SearchAllSubDirectories, "*.*") For Each File In My.Computer.FileSystem.GetFiles(Path & "PCL\Musics\", FileIO.SearchOption.SearchAllSubDirectories, "*.*")
'文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的 '文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
Dim Extend As String = File.Split(".").Last.ToLower Dim Extend As String = File.Split(".").Last.ToLower
If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" OrElse Extend = "lrc" OrElse Extend = "png") Then If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" OrElse Extend = "lrc" OrElse Extend = "db" OrElse Extend = "png") Then
MusicAllList.Add(File) MusicAllList.Add(File)
End If End If
Next Next
@@ -212,7 +212,7 @@
''' 继续音乐播放,返回是否成功切换了状态。 ''' 继续音乐播放,返回是否成功切换了状态。
''' </summary> ''' </summary>
Public Function MusicResume() As Boolean Public Function MusicResume() As Boolean
If MusicState = MusicStates.Play Then If MusicState = MusicStates.Play OrElse Not MusicAllList.Any() Then
Return False Return False
Else Else
RunInThread(Sub() RunInThread(Sub()
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*” ' 方法是按如下所示使用“*”
<Assembly: AssemblyVersion("2.6.7.0")> <Assembly: AssemblyVersion("2.6.8.0")>
<Assembly: AssemblyFileVersion("2.6.7.0")> <Assembly: AssemblyFileVersion("2.6.8.0")>
<Assembly: NeutralResourcesLanguage("")> <Assembly: NeutralResourcesLanguage("")>
@@ -196,7 +196,7 @@ Public Module ModDownloadLib
Else Else
NewItem.Info = Entry("lore").ToString NewItem.Info = Entry("lore").ToString
End If End If
If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特别提供] " & NewItem.Info If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特供下载] " & NewItem.Info
AddHandler NewItem.Click, OnClick AddHandler NewItem.Click, OnClick
'建立菜单 '建立菜单
If IsSaveOnly Then If IsSaveOnly Then
@@ -299,8 +299,8 @@ Public Module ModDownloadLib
WikiName = "Java版Infdev_20100618" WikiName = "Java版Infdev_20100618"
ElseIf Id = "c0.30_01c" OrElse Id = "c0.30_survival" OrElse Id.Contains("生存测试") Then ElseIf Id = "c0.30_01c" OrElse Id = "c0.30_survival" OrElse Id.Contains("生存测试") Then
WikiName = "Java版Classic_0.30(生存模式)" WikiName = "Java版Classic_0.30(生存模式)"
ElseIf Id = "c0.31" Then ElseIf Id.StartsWith("c0.31") Then
WikiName = "Java版Indev_0.31(2010年1月30日)" WikiName = "Java版Indev_0.31_20100130"
ElseIf Id.StartsWith("c") Then ElseIf Id.StartsWith("c") Then
WikiName = "Java版" & Id.Replace("c", "Classic_") WikiName = "Java版" & Id.Replace("c", "Classic_")
ElseIf Id.StartsWith("rd-") Then ElseIf Id.StartsWith("rd-") Then
@@ -382,7 +382,7 @@ Public Module ModDownloadLib
Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback) Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback)
End Try End Try
End Sub End Sub
Private Sub McDownloadOptiFineInstall(BaseMcFolderHome As String, Target As String, Task As LoaderTask(Of List(Of NetFile), Boolean)) Private Sub McDownloadOptiFineInstall(BaseMcFolderHome As String, Target As String, Task As LoaderTask(Of List(Of NetFile), Boolean), UseJavaWrapper As Boolean)
'选择 Java '选择 Java
Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
If Java Is Nothing Then If Java Is Nothing Then
@@ -399,7 +399,12 @@ Public Module ModDownloadLib
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
End If End If
'添加 Java Wrapper 作为主 Jar '添加 Java Wrapper 作为主 Jar
Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -Duser.home=""" & BaseMcFolderHome & """ -cp """ & Target & """ -jar """ & ExtractJavaWrapper() & """ optifine.Installer" Dim Arguments As String
If UseJavaWrapper Then
Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" -jar ""{ExtractJavaWrapper()}"" optifine.Installer"
Else
Arguments = $"-Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" optifine.Installer"
End If
If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments
'开始启动 '开始启动
SyncLock InstallSyncLock SyncLock InstallSyncLock
@@ -504,67 +509,66 @@ Public Module ModDownloadLib
'获取下载地址 '获取下载地址
Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 OptiFine 主文件下载地址", Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 OptiFine 主文件下载地址",
Sub(Task As LoaderTask(Of String, List(Of NetFile))) Sub(Task As LoaderTask(Of String, List(Of NetFile)))
'启动依赖版本的下载 '启动依赖版本的下载
If ClientDownloadLoader Is Nothing Then If ClientDownloadLoader Is Nothing Then
If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹") If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit) ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
End If End If
Task.Progress = 0.1 Task.Progress = 0.1
Dim Sources As New List(Of String) Dim Sources As New List(Of String)
'BMCLAPI 源 'BMCLAPI 源
If DownloadInfo.IsPreview Then If DownloadInfo.IsPreview Then
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Else Else
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
End If End If
'官方源 '官方源
Dim PageData As String Dim PageData As String
Try Try
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html") PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
Task.Progress = 0.8 Task.Progress = 0.8
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0)) Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0)) Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
Catch ex As Exception Catch ex As Exception
Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败") Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
End Try End Try
Task.Progress = 0.9 'OptiFine 中文镜像源
'OptiFine 中文镜像源 Sources.Add("https://optifine.cn/download/" & DownloadInfo.NameFile)
Sources.Add("https://optifine.cn/download/" & DownloadInfo.NameFile) '构造文件请求
'构造文件请求 Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, Target, New FileChecker(MinSize:=64 * 1024))}
Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, Target, New FileChecker(MinSize:=64 * 1024))} End Sub) With {.ProgressWeight = 8})
End Sub) With {.ProgressWeight = 8})
Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 8}) Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 8})
Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("等待原版下载", Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("等待原版下载",
Sub(Task As LoaderTask(Of List(Of NetFile), Boolean)) Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
'等待原版文件下载完成 '等待原版文件下载完成
If ClientDownloadLoader Is Nothing Then Exit Sub If ClientDownloadLoader Is Nothing Then Exit Sub
Dim TargetLoaders As List(Of LoaderBase) = Dim TargetLoaders As List(Of LoaderBase) =
ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName). ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName).
Where(Function(l) l.State <> LoadState.Finished).ToList Where(Function(l) l.State <> LoadState.Finished).ToList
If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成") If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成")
Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted
TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList
Thread.Sleep(50) Thread.Sleep(50)
Loop Loop
If Task.IsAborted Then Exit Sub If Task.IsAborted Then Exit Sub
'拷贝原版文件 '拷贝原版文件
If Not IsCustomFolder Then Exit Sub If Not IsCustomFolder Then Exit Sub
SyncLock VanillaSyncLock SyncLock VanillaSyncLock
Dim ClientName As String = GetFolderNameFromPath(ClientFolder) Dim ClientName As String = GetFolderNameFromPath(ClientFolder)
Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit) Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit)
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then
CopyFile(ClientFolder & ClientName & ".json", CopyFile(ClientFolder & ClientName & ".json",
McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json")
End If End If
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then
CopyFile(ClientFolder & ClientName & ".jar", CopyFile(ClientFolder & ClientName & ".jar",
McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
End If End If
End SyncLock End SyncLock
End Sub) With {.ProgressWeight = 0.1, .Show = False}) End Sub) With {.ProgressWeight = 0.1, .Show = False})
'安装(新旧方式均需要原版 Jar 和 Json) '安装(新旧方式均需要原版 Jar 和 Json)
If IsNewVersion Then If IsNewVersion Then
@@ -586,7 +590,19 @@ Public Module ModDownloadLib
BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
Task.Progress = 0.06 Task.Progress = 0.06
'进行安装 '进行安装
McDownloadOptiFineInstall(BaseMcFolderHome, Target, Task) Dim UseJavaWrapper As Boolean = True
Retry:
Try
McDownloadOptiFineInstall(BaseMcFolderHome, Target, Task, UseJavaWrapper)
Catch ex As Exception
If UseJavaWrapper Then
Log(ex, "使用 JavaWrapper 安装 OptiFine 失败,将不使用 JavaWrapper 并重试")
UseJavaWrapper = False
GoTo Retry
Else
Throw New Exception("运行 OptiFine 安装器失败", ex)
End If
End Try
Task.Progress = 0.96 Task.Progress = 0.96
'复制文件 '复制文件
File.Delete(BaseMcFolder & "launcher_profiles.json") File.Delete(BaseMcFolder & "launcher_profiles.json")
@@ -596,7 +612,7 @@ Public Module ModDownloadLib
File.Delete(Target) File.Delete(Target)
DeleteDirectory(BaseMcFolderHome) DeleteDirectory(BaseMcFolderHome)
Catch ex As Exception Catch ex As Exception
Throw New Exception("安装新版 OptiFine 版本失败", ex) Throw New Exception("安装 OptiFine(方式 A)失败", ex)
End Try End Try
End Sub) With {.ProgressWeight = 8}) End Sub) With {.ProgressWeight = 8})
Else Else
@@ -645,7 +661,7 @@ Public Module ModDownloadLib
End If End If
WriteFile(VersionFolder & Id & ".json", Json) WriteFile(VersionFolder & Id & ".json", Json)
Catch ex As Exception Catch ex As Exception
Throw New Exception("安装旧版 OptiFine 版本失败", ex) Throw New Exception("安装 OptiFine(方式 B)失败", ex)
End Try End Try
End Sub) With {.ProgressWeight = 1}) End Sub) With {.ProgressWeight = 1})
End If End If
@@ -1110,7 +1126,7 @@ Public Module ModDownloadLib
End Try End Try
End Sub End Sub
Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String) Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
'选择 Java '选择 Java
Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
If Java Is Nothing Then If Java Is Nothing Then
@@ -1127,7 +1143,12 @@ Public Module ModDownloadLib
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
End If End If
'添加 Java Wrapper 作为主 Jar '添加 Java Wrapper 作为主 Jar
Dim Arguments = "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """ -cp """ & PathTemp & "Cache\forge_installer.jar;" & Target & """ -jar """ & ExtractJavaWrapper() & """ com.bangbang93.ForgeInstaller """ & McFolder Dim Arguments As String
If UseJavaWrapper Then
Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" -jar ""{ExtractJavaWrapper()}"" com.bangbang93.ForgeInstaller ""{McFolder}"
Else
Arguments = $"-cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" com.bangbang93.ForgeInstaller ""{McFolder}"
End If
If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments
'开始启动 '开始启动
SyncLock InstallSyncLock SyncLock InstallSyncLock
@@ -1415,12 +1436,25 @@ Public Module ModDownloadLib
'释放 launcher_installer.json '释放 launcher_installer.json
McFolderLauncherProfilesJsonCreate(McFolder) McFolderLauncherProfilesJsonCreate(McFolder)
Task.Progress = 0.05 Task.Progress = 0.05
'释放 Forge 注入器 '运行 Forge 安装器
WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller")) Dim UseJavaWrapper As Boolean = True
Task.Progress = 0.06 Retry:
'运行注入器 Try
ForgeInjector(InstallerAddress, Task, McFolder) '释放 Forge 注入器
Task.Progress = 0.97 WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller"))
Task.Progress = 0.06
'运行注入器
ForgeInjector(InstallerAddress, Task, McFolder, UseJavaWrapper)
Task.Progress = 0.97
Catch ex As Exception
If UseJavaWrapper Then
Log(ex, "使用 JavaWrapper 安装 Forge 失败,将不使用 JavaWrapper 并重试")
UseJavaWrapper = False
GoTo Retry
Else
Throw New Exception("运行 Forge 安装器失败", ex)
End If
End Try
'拷贝新增的版本 Json '拷贝新增的版本 Json
Dim DeltaList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.SkipWhile(Function(Info As DirectoryInfo) As Boolean Dim DeltaList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.SkipWhile(Function(Info As DirectoryInfo) As Boolean
Return OldList.Contains(Info.FullName) Return OldList.Contains(Info.FullName)
@@ -1470,7 +1504,7 @@ Public Module ModDownloadLib
Task.Progress = 0.6 Task.Progress = 0.6
'解压支持库文件 '解压支持库文件
Installer.Dispose() Installer.Dispose()
ZipFile.ExtractToDirectory(InstallerAddress, InstallerAddress & "_unrar\") ExtractFile(InstallerAddress, InstallerAddress & "_unrar\")
CopyDirectory(InstallerAddress & "_unrar\maven\", McFolder & "libraries\") CopyDirectory(InstallerAddress & "_unrar\maven\", McFolder & "libraries\")
DeleteDirectory(InstallerAddress & "_unrar\") DeleteDirectory(InstallerAddress & "_unrar\")
Else Else
@@ -848,7 +848,7 @@
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……" If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message
For Each Version As JObject In DlFabricListLoader.Output.Value("game") For Each Version As JObject In DlFabricListLoader.Output.Value("game")
If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite") Then If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容" If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
'If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容" 'If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
Return Nothing Return Nothing
@@ -908,7 +908,7 @@
Public Shared Function IsSuitableFabricApi(DisplayName As String, MinecraftVersion As String) As Boolean Public Shared Function IsSuitableFabricApi(DisplayName As String, MinecraftVersion As String) As Boolean
Try Try
If DisplayName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False If DisplayName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False
DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.ToLower DisplayName = DisplayName.ToLower : MinecraftVersion = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").ToLower
If DisplayName.StartsWith("[" & MinecraftVersion & "]") Then Return True If DisplayName.StartsWith("[" & MinecraftVersion & "]") Then Return True
If Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False If Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False
'直接的判断(例如 1.18.1/22w03a) '直接的判断(例如 1.18.1/22w03a)
@@ -104,7 +104,7 @@
(Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样 (Image.Pic.GetPixel(1, 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso '或是头部颜色和透明区均不一样
Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso Image.Pic.GetPixel(Image.Pic.Width - 1, Image.Pic.Height - 1) <> Image.Pic.GetPixel(Scale * 41, Scale * 9) AndAlso
Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then Image.Pic.GetPixel(Image.Pic.Width - 2, Image.Pic.Height / 2 - 2) <> Image.Pic.GetPixel(Scale * 41, Scale * 9)) Then
ImgFore.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 40, Scale * 8, Scale * 8, Scale * 8)) ImgFore.Source = Image.Clip(Scale * 40, Scale * 8, Scale * 8, Scale * 8)
Else Else
ImgFore.Source = Nothing ImgFore.Source = Nothing
End If End If
@@ -112,7 +112,7 @@
ImgFore.Source = Nothing ImgFore.Source = Nothing
End If End If
'脸层 '脸层
ImgBack.Source = New CroppedBitmap(Image, New Int32Rect(Scale * 8, Scale * 8, Scale * 8, Scale * 8)) ImgBack.Source = Image.Clip(Scale * 8, Scale * 8, Scale * 8, Scale * 8)
Log("[Skin] 载入头像成功:" & Loader.Name) Log("[Skin] 载入头像成功:" & Loader.Name)
Catch ex As Exception Catch ex As Exception
Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint) Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint)
@@ -673,14 +673,7 @@ ExitRefresh:
McLaunchProcess = Nothing McLaunchProcess = Nothing
McLaunchWatcher = Nothing McLaunchWatcher = Nothing
'获取 “你知道吗” 提示 '获取 “你知道吗” 提示
Dim Hint As String = PageOtherTest.GetRandomHint() LabLaunchingHint.Text = PageOtherTest.GetRandomHint()
Try
Dim RawHints = ReadFile(Path & "PCL\hints.txt").Split(vbCrLf.ToCharArray).Where(Function(s) Not String.IsNullOrWhiteSpace(s)).ToList
If RawHints.Count > 0 Then Hint = RandomOne(RawHints)
Catch ex As Exception
Log(ex, "获取自定义 你知道吗 提示失败", LogLevel.Hint)
End Try
LabLaunchingHint.Text = Hint
End Sub End Sub
Private ShowProgress As Double = 0 Private ShowProgress As Double = 0
''' <summary> ''' <summary>
@@ -133,7 +133,7 @@ StartDownload:
Log("[IOI] 解压联机模块以完成下载") Log("[IOI] 解压联机模块以完成下载")
If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe") If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe")
If File.Exists(PathIoi) Then File.Delete(PathIoi) If File.Exists(PathIoi) Then File.Delete(PathIoi)
Compression.ZipFile.ExtractToDirectory(PathTemp & "联机模块.zip", PathTemp) ExtractFile(PathTemp & "联机模块.zip", PathTemp)
File.Delete(PathTemp & "联机模块.zip") File.Delete(PathTemp & "联机模块.zip")
CopyFile(PathTemp & "ioi.exe", PathIoi) CopyFile(PathTemp & "ioi.exe", PathIoi)
File.Delete(PathTemp & "ioi.exe") File.Delete(PathTemp & "ioi.exe")
@@ -14,7 +14,7 @@
If String.IsNullOrEmpty(Entry.XamlContent) Then Throw New Exception("帮助 xaml 文件为空") If String.IsNullOrEmpty(Entry.XamlContent) Then Throw New Exception("帮助 xaml 文件为空")
Me.Entry = Entry Me.Entry = Entry
PanCustom.Children.Clear() PanCustom.Children.Clear()
FileContent = FileContent.Replace("{path}", Path) FileContent = HelpArgumentReplace(FileContent)
PanCustom.Children.Add(GetObjectFromXML(FileContent)) PanCustom.Children.Add(GetObjectFromXML(FileContent))
Return True Return True
Catch ex As Exception Catch ex As Exception
@@ -219,6 +219,7 @@
If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""), If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""),
"版本删除确认", , "取消",, True) "版本删除确认", , "取消",, True)
Case 1 Case 1
IniClearCache(Version.Path & "PCL\Setup.ini")
If IsShiftPressed Then If IsShiftPressed Then
DeleteDirectory(Version.Path) DeleteDirectory(Version.Path)
Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish) Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish)
@@ -74,7 +74,7 @@
<local:MyCheckBox Text="正式版更新提示" Grid.Column="1" x:Name="CheckUpdateRelease" Tag="ToolUpdateRelease" ToolTip="在 Minecraft 正式版更新时弹窗提示" /> <local:MyCheckBox Text="正式版更新提示" Grid.Column="1" x:Name="CheckUpdateRelease" Tag="ToolUpdateRelease" ToolTip="在 Minecraft 正式版更新时弹窗提示" />
<local:MyCheckBox Text="测试版更新提示" Grid.Column="2" Height="22" x:Name="CheckUpdateSnapshot" Tag="ToolUpdateSnapshot" ToolTip="在 Minecraft 测试版(快照、预发布版)更新时弹窗提示" /> <local:MyCheckBox Text="测试版更新提示" Grid.Column="2" Height="22" x:Name="CheckUpdateSnapshot" Tag="ToolUpdateSnapshot" ToolTip="在 Minecraft 测试版(快照、预发布版)更新时弹窗提示" />
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="游戏语言" Margin="0,0,25,0" /> <TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="游戏语言" Margin="0,0,25,0" />
<local:MyCheckBox Text="默认设置为中文" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5" x:Name="CheckHelpChinese" Tag="ToolHelpChinese" /> <local:MyCheckBox Text="自动设置为中文" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="5" x:Name="CheckHelpChinese" Tag="ToolHelpChinese" />
</Grid> </Grid>
</local:MyCard> </local:MyCard>
<local:MyCard Margin="0,0,0,15" Title="系统"> <local:MyCard Margin="0,0,0,15" Title="系统">
@@ -195,7 +195,6 @@
AniDispose(sender, False) AniDispose(sender, False)
AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack()) AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack())
RightCards.Remove(Loader.Name) RightCards.Remove(Loader.Name)
LoaderTaskbar.Remove(Loader)
Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}") Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}")
RunInThread(Sub() Loader.Abort()) RunInThread(Sub() Loader.Abort())
End Sub End Sub
+185 -127
View File
@@ -4,102 +4,102 @@
你也可以使用 Ctrl + F 快速查找。例如,若需要改变文本颜色,则搜索 “颜色” 即可。 --> 你也可以使用 Ctrl + F 快速查找。例如,若需要改变文本颜色,则搜索 “颜色” 即可。 -->
<local:MyCard Title="纯文本" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="纯文本" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="每个 local:MyCard 代表一张卡片,你可以添加、删除格式类似的 MyCard 来添加多个卡片。每个 TextBlock 代表一段文本,你可以在 Text 属性中书写任何你想写的内容,也可以自行添加更多的 TextBlock。" /> Text="每个 local:MyCard 代表一张卡片,你可以添加、删除格式类似的 MyCard 来添加多个卡片。每个 TextBlock 代表一段文本,你可以在 Text 属性中书写任何你想写的内容,也可以自行添加更多的 TextBlock。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" FontSize="11" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4" FontSize="11"
Text="你可以通过添加、删除属性修改样式,例如上一行的 FontSize 就会将字号改为 11 号。" /> Text="你可以通过添加、删除属性修改样式,例如上一行的 FontSize 就会将字号改为 11 号。" />
<TextBlock TextWrapping="Wrap" Margin="20,5,20,0" Foreground="#8C7721" <TextBlock TextWrapping="Wrap" Margin="20,5,20,0" Foreground="#8C7721"
Text="它还有许多可以调整的属性:上一行的 Margin 调整了边距,Foreground 则会让文字变色。" /> Text="它还有许多可以调整的属性:上一行的 Margin 调整了边距,Foreground 则会让文字变色。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="卡片、提示条" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="卡片、提示条" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<local:MyHint Text="local:MyHint 代表提示条,它默认是警告样式。" /> <local:MyHint Text="local:MyHint 代表提示条,它默认是警告样式。" />
<local:MyHint Margin="0,8,0,2" IsWarn="False" <local:MyHint Margin="0,8,0,2" IsWarn="False"
Text="将提示条的 IsWarn 属性改为 False 即可改为提示样式。&#xA;使用左边的那堆字符可以在任意地方手动换行,如果需要使用等号、引号等特殊字符,可以自行百度 XAML 转义字符。" /> Text="将提示条的 IsWarn 属性改为 False 即可改为提示样式。&#xA;使用左边的那堆字符可以在任意地方手动换行,如果需要使用等号、引号等特殊字符,可以自行百度 XAML 转义字符。" />
<TextBlock TextWrapping="Wrap" Margin="0,6,0,0" <TextBlock TextWrapping="Wrap" Margin="0,6,0,0"
Text="卡片(local:MyCard)的 Title 属性决定了它的标题。&#xA;为卡片添加 CanSwap 属性让它可以被折叠,True 代表是,False 代表否。在此基础上,再使用 IsSwaped 属性调整它是否默认被折叠。" /> Text="卡片(local:MyCard)的 Title 属性决定了它的标题。&#xA;为卡片添加 CanSwap 属性让它可以被折叠,True 代表是,False 代表否。在此基础上,再使用 IsSwaped 属性调整它是否默认被折叠。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="长宽属性、图片" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="长宽属性、图片" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" Width="320" HorizontalAlignment="Right" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4" Width="320" HorizontalAlignment="Right"
Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" /> Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,6" <TextBlock TextWrapping="Wrap" Margin="0,0,0,6"
Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" /> Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" />
<Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" /> <Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="按钮" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="按钮" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="添加 local:MyButton 即可新建一个按钮。你需要限定它的尺寸与位置,并通过 Padding 属性进一步控制它的内边距。" /> Text="添加 local:MyButton 即可新建一个按钮。你需要限定它的尺寸与位置,并通过 Padding 属性进一步控制它的内边距。" />
<local:MyButton Margin="0,4,0,10" Height="35" HorizontalAlignment="Left" Padding="25,0,25,0" <local:MyButton Margin="0,4,0,10" Height="35" HorizontalAlignment="Left" Padding="25,0,25,0"
Text="这是一个按钮!一个按钮!" /> Text="这是一个按钮!一个按钮!" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="你可以修改 ColorType 属性来调整按钮配色:Highlight 代表当前主题色,Red 代表红色。ToolTip 属性还可以让你在鼠标悬浮在上面的时候显示提示文本。" /> Text="你可以修改 ColorType 属性来调整按钮配色:Highlight 代表当前主题色,Red 代表红色。ToolTip 属性还可以让你在鼠标悬浮在上面的时候显示提示文本。" />
<local:MyButton Margin="0,4,0,10" Width="250" Height="35" ColorType="Highlight" <local:MyButton Margin="0,4,0,10" Width="250" Height="35" ColorType="Highlight"
Text="把鼠标停在这里别动……" ToolTip="……就能看到这句话!" /> Text="把鼠标停在这里别动……" ToolTip="……就能看到这句话!" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="local:MyTextButton 是没有边框的 “纯文本按钮”,其作用与 local:MyButton 一致。" /> Text="local:MyTextButton 是没有边框的 “纯文本按钮”,其作用与 local:MyButton 一致。" />
<local:MyTextButton Margin="0,8,0,10" HorizontalAlignment="Center" <local:MyTextButton Margin="0,8,0,10" HorizontalAlignment="Center"
Text="一个精简版的文本按钮" /> Text="一个精简版的文本按钮" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="按钮可以做到很多事,具体将在 “事件” 部分介绍。" /> Text="按钮可以做到很多事,具体将在 “事件” 部分介绍。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="事件 1:打开网页、弹出窗口" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="事件 1:打开网页、弹出窗口" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" /> Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" />
<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="打开 MCBBS" EventType="打开网页" EventData="https://www.mcbbs.net/" /> Text="打开 MCBBS" EventType="打开网页" EventData="https://www.mcbbs.net/" />
<TextBlock TextWrapping="Wrap" Margin="0,10,0,10" <TextBlock TextWrapping="Wrap" Margin="0,10,0,10"
Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" /> Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" />
<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="显示一个弹窗" EventType="弹出窗口" EventData="这是标题|标题与内容以竖线间隔。\n你也可以直接在其中换行。" /> Text="显示一个弹窗" EventType="弹出窗口" EventData="这是标题|标题与内容以竖线间隔。\n你也可以直接在其中换行。" />
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0" <TextBlock TextWrapping="Wrap" Margin="0,10,0,0"
Text="local:MyTextButton 同样可以设置 EventType 与 EventData 让它具有特定功能。" /> Text="local:MyTextButton 同样可以设置 EventType 与 EventData 让它具有特定功能。" />
<local:MyTextButton Margin="0,8,0,0" HorizontalAlignment="Center" <local:MyTextButton Margin="0,8,0,0" HorizontalAlignment="Center"
Text="点击打开知乎" EventType="打开网页" EventData="https://www.zhihu.com/" /> Text="点击打开知乎" EventType="打开网页" EventData="https://www.zhihu.com/" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="事件 2:启动游戏" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="事件 2:启动游戏" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="将 EventType 设置为 启动游戏,EventData 设置为具体的游戏版本,即可通过点击按钮启动游戏。如果当前游戏文件夹没有该版本,则无法启动。" /> Text="将 EventType 设置为 启动游戏,EventData 设置为具体的游戏版本,即可通过点击按钮启动游戏。如果当前游戏文件夹没有该版本,则无法启动。" />
<local:MyButton Margin="0,4,0,10" Width="250" Height="35" <local:MyButton Margin="0,4,0,10" Width="250" Height="35"
Text="启动 Minecraft 1.12.2" EventType="启动游戏" EventData="1.12.2" /> Text="启动 Minecraft 1.12.2" EventType="启动游戏" EventData="1.12.2" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="在 EventData 后面添加一条竖线(|),竖线后填写服务器 IP,即可在启动该版本的同时自动进入服务器。" /> Text="在 EventData 后面添加一条竖线(|),竖线后填写服务器 IP,即可在启动该版本的同时自动进入服务器。" />
<local:MyButton Margin="0,4,0,0" Width="250" Height="35" <local:MyButton Margin="0,4,0,0" Width="250" Height="35"
Text="启动 1.12.2 并进入 Hypixel" EventType="启动游戏" EventData="1.12.2|mc.hypixel.net" ToolTip="服务器 IP:mc.hypixel.net" /> Text="启动 1.12.2 并进入 Hypixel" EventType="启动游戏" EventData="1.12.2|mc.hypixel.net" ToolTip="服务器 IP:mc.hypixel.net" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="事件 3:打开文件" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="事件 3:打开文件" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="将 EventType 设置为 打开文件,EventData 设置为文件路径,即可点击打开特定文件或启动程序。" /> Text="将 EventType 设置为 打开文件,EventData 设置为文件路径,即可点击打开特定文件或启动程序。" />
<local:MyButton Margin="0,4,0,8" Width="200" Height="35" <local:MyButton Margin="0,4,0,8" Width="200" Height="35"
Text="打开记事本" EventType="打开文件" EventData="notepad.exe" /> Text="打开记事本" EventType="打开文件" EventData="notepad.exe" />
<TextBlock TextWrapping="Wrap" Margin="0,4,0,0" <TextBlock TextWrapping="Wrap" Margin="0,4,0,0"
Text="如果能在 PCL 文件夹中找到该文件,则会优先打开 PCL 文件夹中的文件。当然,你可以使用例如 C:\ 开头的绝对路径。" /> Text="如果能在 PCL 文件夹中找到该文件,则会优先打开 PCL 文件夹中的文件。当然,你可以使用例如 C:\ 开头的绝对路径。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="此外,PCL2 会将文件中的花括号 path 替换为 PCL 可执行文件所在文件夹({path}),可以用于执行特定程序或加载图片。" /> Text="此外,PCL 会将文件中的 “花括号 path” 替换为 PCL 可执行文件所在文件夹({path}),可以用于执行特定程序或加载图片。" />
<local:MyButton Margin="0,4,0,10" Width="200" Height="35" <local:MyButton Margin="0,4,0,10" Width="200" Height="35"
Text="打开 PCL 日志" EventType="打开文件" EventData="Log1.txt" ToolTip="PCL2 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" /> Text="打开 PCL 日志" EventType="打开文件" EventData="Log1.txt" ToolTip="PCL 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="就像为游戏指定服务器 IP 一样,你也可以在 EventData 的竖线后为程序添加启动参数。" /> Text="就像为游戏指定服务器 IP 一样,你也可以在 EventData 的竖线后为程序添加启动参数。" />
<local:MyButton Margin="0,4,0,0" Width="200" Height="35" <local:MyButton Margin="0,4,0,0" Width="200" Height="35"
Text="定位游戏启动脚本" EventType="打开文件" EventData="explorer.exe|/select,LatestLaunch.bat" ToolTip="PCL2 会将上一次启动 MC 的脚本保存在 PCL 文件夹内,且名为 LatestLaunch.bat。&#xA;点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" /> Text="定位游戏启动脚本" EventType="打开文件" EventData="explorer.exe|/select,LatestLaunch.bat" ToolTip="PCL 会将上一次启动 MC 的脚本保存在 PCL 文件夹内,且名为 LatestLaunch.bat。&#xA;点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="事件 4:其他事件类型" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="事件 4:其他事件类型" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
@@ -133,128 +133,176 @@
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="横向布局 1:StackPanel" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="StackPanel 横向布局" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="你需要使用 StackPanel 在一行里塞下多个按钮。你可以仅在这个教学卡片的基础上稍作调整,来实现自己的按钮布局。" /> Text="你需要使用 StackPanel 在一行里塞下多个按钮。你可以仅在这个教学卡片的基础上稍作调整,来实现自己的按钮布局。" />
<!-- 这是第一行 --> <!-- 这是第一行 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" ColorType="Highlight" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" ColorType="Highlight"
Text="打开 B 站" EventType="打开网页" EventData="https://www.bilibili.com/" /> Text="打开 B 站" EventType="打开网页" EventData="https://www.bilibili.com/" />
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="进入 Hypixel" EventType="启动游戏" EventData="1.16.3|mc.hypixel.net" ToolTip="使用 Minecraft 1.16.3 进入 Hypixel" /> Text="进入 Hypixel" EventType="启动游戏" EventData="1.16.3|mc.hypixel.net" ToolTip="使用 Minecraft 1.16.3 进入 Hypixel" />
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="打开画图" EventType="打开文件" EventData="mspaint.exe" /> Text="打开画图" EventType="打开文件" EventData="mspaint.exe" />
</StackPanel> </StackPanel>
<!-- 这是第二行 --> <!-- 这是第二行 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10,0,0">
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" ColorType="Red" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" ColorType="Red"
Text="复制召唤命令" EventType="复制文本" EventData="/execute as @a at @s run summon Creeper ~ ~2 ~" ToolTip="这条 Minecraft 命令会在每个玩家头顶召唤一只苦力怕!" /> Text="复制召唤命令" EventType="复制文本" EventData="/execute as @a at @s run summon Creeper ~ ~2 ~" ToolTip="这条 Minecraft 命令会在每个玩家头顶召唤一只苦力怕!" />
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="内存优化" EventType="内存优化" /> Text="内存优化" EventType="内存优化" />
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="下载作者的头像" EventType="下载文件" EventData="https://www.mcbbs.net/uc_server/data/avatar/000/14/51/06_avatar_small.jpg?ts=1471402592" /> Text="下载作者的头像" EventType="下载文件" EventData="https://www.mcbbs.net/uc_server/data/avatar/000/14/51/06_avatar_small.jpg?ts=1471402592" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</local:MyCard>
<local:MyCard Title="横向布局 2:Grid" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="如果要实现更复杂的布局,则必须使用 Grid。Grid 可以让按钮们自动适应窗口宽度:随意拉伸 PCL 窗口,按钮大小会自动改变。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="你可以上网查找 “WPF Grid” 的相关教程,这里仅给出一个例子,不作详细介绍。你可以仅在这个教学卡片的基础上稍作调整,来实现自己的按钮布局。" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.6*" />
<!-- 这个按钮的宽度是其他按钮的 1.6 倍 -->
<ColumnDefinition Width="1*" />
<!-- 如果不打 *,则会占用固定的宽度 -->
</Grid.ColumnDefinitions>
<!-- 为按钮添加 Grid.Column 属性,删除 Width 与 HorizontalAlignment 属性 -->
<local:MyButton Grid.Column="0" Margin="0,0,10,0" Height="35" ColorType="Highlight"
Text="打开 B 站" EventType="打开网页" EventData="https://www.bilibili.com/" />
<local:MyButton Grid.Column="1" Margin="0,0,10,0" Height="35"
Text="进入 Hypixel" EventType="启动游戏" EventData="1.16.3|mc.hypixel.net" ToolTip="使用 Minecraft 1.16.3 进入 Hypixel" />
<local:MyButton Grid.Column="2" Margin="0,0,10,0" Height="35"
Text="打开画图" EventType="打开文件" EventData="mspaint.exe" />
</Grid>
</StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="列表项 1:基础" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="列表项 1:基础" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="你也可以使用列表项 “local:MyListItem” 来替代按钮,其使用方式与按钮类似。" /> Text="你也可以使用列表项 “local:MyListItem” 来替代按钮,其使用方式与按钮类似。" />
<local:MyListItem Margin="-5,2,-5,8" <local:MyListItem Margin="-5,2,-5,8"
Logo="https://www.baidu.com/favicon.ico" Title="百度" Info="百度一下,你就知道!" Logo="https://www.baidu.com/favicon.ico" Title="百度" Info="百度一下,你就知道!"
EventType="打开网页" EventData="https://www.baidu.com/" Type="Clickable" /> EventType="打开网页" EventData="https://www.baidu.com/" Type="Clickable" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="Logo 属性为列表项的图标,指定的可以是链接也可以是文件路径;Title 是大标题,Info 是可选的小标题(详细信息);Type 如果省略,该列表项则单纯只展示信息,仅当 Type 为 Clickable 才能触发点击效果。"/> Text="Logo 属性为列表项的图标,指定的可以是链接也可以是文件路径;Title 是大标题,Info 是可选的小标题(详细信息);Type 如果省略,该列表项则单纯只展示信息,仅当 Type 为 Clickable 才能触发点击效果。"/>
<local:MyListItem Margin="-5,2,-5,8" <local:MyListItem Margin="-5,2,-5,8"
Logo="pack://application:,,,/images/Blocks/Grass.png" Logo="pack://application:,,,/images/Blocks/Grass.png"
EventType="打开帮助" EventData="帮助/自定义帮助.json" Type="Clickable" /> EventType="打开帮助" EventData="帮助/自定义帮助.json" Type="Clickable" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="EventType、EventData 与按钮几乎一致,唯一不同的是,列表项不支持联网加载帮助文件。" /> Text="EventType、EventData 与按钮几乎一致,唯一不同的是,列表项不支持联网加载帮助文件。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="列表项 2:内置图片" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="列表项 2:内置图片" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="PCL 内置了一些 Minecraft 方块与物品图片,可以直接使用。"/> Text="PCL 内置了一些 Minecraft 方块与物品图片,可以直接使用。"/>
<StackPanel Orientation="Horizontal" Margin="10,8,0,15" Height="30"> <StackPanel Orientation="Horizontal" Margin="10,8,0,15" Height="30">
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/CommandBlock.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/CommandBlock.png"/>
<!-- 命令方块 --> <!-- 命令方块 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Cobblestone.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Cobblestone.png"/>
<!-- 圆石 --> <!-- 圆石 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/GoldBlock.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/GoldBlock.png"/>
<!-- 金块 --> <!-- 金块 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Grass.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Grass.png"/>
<!-- 草方块 --> <!-- 草方块 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/GrassPath.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/GrassPath.png"/>
<!-- 草径 --> <!-- 草径 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Anvil.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Anvil.png"/>
<!-- 铁砧 --> <!-- 铁砧 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneBlock.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneBlock.png"/>
<!-- 红石块 --> <!-- 红石块 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneLampOn.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneLampOn.png"/>
<!-- 红石灯(亮) --> <!-- 红石灯(亮) -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneLampOff.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/RedstoneLampOff.png"/>
<!-- 红石灯(灭) --> <!-- 红石灯(灭) -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Egg.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Egg.png"/>
<!-- 鸡蛋 --> <!-- 鸡蛋 -->
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Fabric.png"/> <Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Fabric.png"/>
<!-- 布料(Fabric 图标) --> <!-- 布料(Fabric 图标) -->
</StackPanel> </StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" <TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="使用时,将上方对应行的 Source 复制到 MyListItem 的 Logo 属性处即可。"/> Text="使用时,将上方对应行的 Source 复制到 MyListItem 的 Logo 属性处即可。"/>
<local:MyListItem Margin="-5,0,-5,8" <local:MyListItem Margin="-5,0,-5,8"
Logo="pack://application:,,,/images/Blocks/CommandBlock.png" Logo="pack://application:,,,/images/Blocks/CommandBlock.png"
Title="清理垃圾" Info="使用内置的命令方块图片作为 Logo" EventType="清理垃圾" Type="Clickable" /> Title="清理垃圾" Info="使用内置的命令方块图片作为 Logo" EventType="清理垃圾" Type="Clickable" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
<local:MyCard Title="主题色" Margin="0,0,0,15" CanSwap="True" IsSwaped="True"> <local:MyCard Title="主题色" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15"> <StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,15" <TextBlock TextWrapping="Wrap" Margin="0,0,0,15"
Text="你可以用类似 {DynamicResource ColorBrush5} 的格式使用 PCL 当前的主题颜色,修改末尾的数字编号以改变颜色浓度。" /> Text="你可以用类似 {DynamicResource ColorBrush5} 的格式使用 PCL 当前的主题颜色,修改末尾的数字编号以改变颜色浓度。" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4">
<TextBlock TextWrapping="Wrap" Text=" 浓度 1 " Margin="5,0" Background="{DynamicResource ColorBrush1}" Foreground="#FFFFFF" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 1 " Margin="5,0" Background="{DynamicResource ColorBrush1}" Foreground="#FFFFFF" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 2 " Margin="5,0" Background="{DynamicResource ColorBrush2}" Foreground="#FFFFFF" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 2 " Margin="5,0" Background="{DynamicResource ColorBrush2}" Foreground="#FFFFFF" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 3 " Margin="5,0" Background="{DynamicResource ColorBrush3}" Foreground="#FFFFFF" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 3 " Margin="5,0" Background="{DynamicResource ColorBrush3}" Foreground="#FFFFFF" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 4 " Margin="5,0" Background="{DynamicResource ColorBrush4}" Foreground="#FFFFFF" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 4 " Margin="5,0" Background="{DynamicResource ColorBrush4}" Foreground="#FFFFFF" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4">
<TextBlock TextWrapping="Wrap" Text=" 浓度 5 " Margin="5,0" Background="{DynamicResource ColorBrush5}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 5 " Margin="5,0" Background="{DynamicResource ColorBrush5}" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 6 " Margin="5,0" Background="{DynamicResource ColorBrush6}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 6 " Margin="5,0" Background="{DynamicResource ColorBrush6}" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" />
</StackPanel> </StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}" <TextBlock Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" /> Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" />
</StackPanel> </StackPanel>
</local:MyCard>
<local:MyCard Title="替换标记" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="在花括号 {} 中写入特定内容,PCL 会在加载时对其进行替换。&#xa;例如,path 会被替换为 PCL 可执行文件所在文件夹({path}),用于在 EventData 中指定特定文件,或是加载图片。" />
<local:MyHint Margin="0,6,0,10" Text="以下特性于 PCL 2.6.5 (299) 加入,不适用于更早版本的 PCL。" IsWarn="False" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="你也可以使用替换标记来获取随机内容:cave 会被替换为一条随机的回声洞内容,hint 则是 “你知道吗” 的内容。" />
<TextBlock TextWrapping="Wrap" Margin="10,6,0,5" Foreground="{DynamicResource ColorBrush3}"
Text="你知道吗:{hint}" />
</StackPanel>
</local:MyCard>
<local:MyCard Title="进阶:Grid 布局" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="如果要实现更复杂的布局,则必须使用 Grid。Grid 可以让按钮们自动适应窗口宽度:随意拉伸 PCL 窗口,按钮大小会自动改变。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="你可以上网查找 “WPF Grid” 的相关教程,这里仅给出一个例子,不作详细介绍。你可以仅在这个教学卡片的基础上稍作调整,来实现自己的按钮布局。" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.6*" MinWidth="200" />
<!-- 第二个按钮的宽度是第一个按钮的 1.6 倍,且至少为 200 -->
<ColumnDefinition Width="150" />
<!-- 如果不打 *,则会占用固定的宽度,也可以写 Auto 来自适应 -->
</Grid.ColumnDefinitions>
<!-- 为按钮添加 Grid.Column 属性指定它所在的列,不要添加 Width 与 HorizontalAlignment 属性 -->
<local:MyButton Grid.Column="0" Margin="0,0,10,0" Height="35" ColorType="Highlight"
Text="打开 B 站" EventType="打开网页" EventData="https://www.bilibili.com/" />
<local:MyButton Grid.Column="1" Margin="0,0,10,0" Height="35"
Text="进入 Hypixel" EventType="启动游戏" EventData="1.16.3|mc.hypixel.net" ToolTip="使用 Minecraft 1.16.3 进入 Hypixel" />
<local:MyButton Grid.Column="2" Margin="0,0,10,0" Height="35"
Text="打开画图" EventType="打开文件" EventData="mspaint.exe" />
</Grid>
</StackPanel>
</local:MyCard>
<local:MyCard Title="进阶:图标按钮" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15">
<local:MyHint Margin="0,0,0,10" Text="以下特性于 PCL 2.6.5 (299) 加入,不适用于更早版本的 PCL。" IsWarn="False" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="MyIconTextButton 是一种带有自定义矢量图标的按钮变体,它同样支持 EventType 与 EventData 属性。&#xa;通过设置 Logo 属性来控制它的图标,设置 LogoScale 属性还可以调整图标的缩放比例。" />
<local:MyIconTextButton Margin="0,4,0,8" Height="35" HorizontalAlignment="Center"
Text="查看 SVG Path 简介" EventType="打开网页" EventData="https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Paths"
LogoScale="1.02" Logo="M1091 0H78C35 0 0 35 0 78v863c0 43 35 78 78 78H1091c43 0 78-35 78-78V78C1170 35 1134 0 1091 0z m-8 87v78H87v-78h994zM87 933V254h994v679H87v0zM724 340l-206 547a43 43 0 0 1-82-31l206-547a43 43 0 0 1 82 30zM369 545l-85 85 85 85a43 43 0 0 1-62 62l-116-116a43 43 0 0 1 0-62l116-116a43 43 0 0 1 62 61z m610-37a43 43 0 0 1 0 62l-116 116a43 43 0 0 1-62 0 43 43 0 0 1 0-62l85-85-85-85a43 43 0 0 1 62-62l116 116z" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="Logo 属性的内容是 SVG Path 的值,你需要使用 SVG 编辑器或者从图标资源网站(如 IconFont)查找它。不要自己写,写不懂的。&#xa;此外,它还有两种 ColorType:Black(默认)和 Highlight。" />
<local:MyIconTextButton Margin="0,4,0,8" Height="35" HorizontalAlignment="Center"
ColorType="Highlight" Text="打开 IconFont" EventType="打开网页" EventData="https://www.iconfont.cn/"
LogoScale="1" Logo="M510 959c-51-31-40-86-74-120-61-63-168-66-231-138C-5 457 196 3 593 70c231 39 407 363 250 592-67 98-198 106-277 213-23 23-11 81-55 83zM176 432c-9 176 240 167 240 9 0-80-72-145-166-111-66 23-71 49-74 101z m509 129c212 59 217-307 0-231-102 36-90 206 0 231z m-213 111c33 7 44-91 9-92-19 19-64 79-9 92z m83 0H575c23-37-8-87-46-92 3 37-11 92 27 92z" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="如果只想要图标,不要文字,还可以使用 MyIconButton。它不能设置 Text 属性,只有一个图标可以按。&#xa;它的 Theme 属性可以设置为 Color(默认)、White、Black、Red。" />
<local:MyIconButton Margin="0,4,0,0" Width="25" Height="25" HorizontalAlignment="Center"
Theme="Black" EventType="弹出窗口" EventData="事件|MyIconButton 当然也支持 EventType 和 EventData——要不然介绍它干啥?"
LogoScale="1" Logo="M149 873c47 47 101 83 162 109 63 26 130 40 199 40 69 0 136-13 199-40 61-25 115-62 162-109s83-101 109-162c26-63 40-130 40-199 0-69-13-136-40-199-25-61-62-115-109-162-46-46-101-83-162-109C648 13 580 0 511 0s-136 13-199 40c-61 25-115 62-162 109s-83 101-109 162C13 375 0 442 0 511s13 136 40 199c25 61 62 115 109 162zM97 511c0-228 185-414 414-414 228 0 414 185 414 414S740 926 511 926c-228 0-414-185-414-414z M539 244c-8-4-17-7-27-7-9 0-19 2-27 7-16 9-27 27-27 47 0 30 24 54 54 54 30 0 54-24 54-54 0-19-10-37-27-47zM566 732v-284c0-30-24-54-54-54-30 0-54 24-54 54v284c0 30 24 54 54 54 30 0 54-24 54-54z" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
Text="如果只想显示一个图标,不想让它是能按的按钮,可以使用 WPF 自带的 Path 等绘图控件,感兴趣的话可以自行搜索。想偷懒也可以把 MyIconButton 的 IsHitTestVisible 属性改为 False……" />
<Path Margin="0,4,0,0" Width="20" Height="20" HorizontalAlignment="Center"
Stretch="Uniform" Fill="{DynamicResource ColorBrush1}"
Data="M149 873c47 47 101 83 162 109 63 26 130 40 199 40 69 0 136-13 199-40 61-25 115-62 162-109s83-101 109-162c26-63 40-130 40-199 0-69-13-136-40-199-25-61-62-115-109-162-46-46-101-83-162-109C648 13 580 0 511 0s-136 13-199 40c-61 25-115 62-162 109s-83 101-109 162C13 375 0 442 0 511s13 136 40 199c25 61 62 115 109 162zM97 511c0-228 185-414 414-414 228 0 414 185 414 414S740 926 511 926c-228 0-414-185-414-414z M539 244c-8-4-17-7-27-7-9 0-19 2-27 7-16 9-27 27-27 47 0 30 24 54 54 54 30 0 54-24 54-54 0-19-10-37-27-47zM566 732v-284c0-30-24-54-54-54-30 0-54 24-54 54v284c0 30 24 54 54 54 30 0 54-24 54-54z" />
</StackPanel>
</local:MyCard>
<local:MyCard Title="进阶:联网自定义主页" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
<StackPanel Margin="25,40,23,15">
<local:MyHint Margin="0,0,0,10" Text="以下特性于 PCL 2.6.5 (299) 加入,不适用于更早版本的 PCL。" IsWarn="False" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="如果你在尝试制作联网更新的自定义主页,可以查看下方的 GitHub 讨论页。&#xa;你可以根据其中的介绍为主页添加版本号检查以节省流量,也可以通过检查 Referer 和 User Agent 来确定对方的 PCL 版本。" />
<local:MyButton Margin="0,0,0,8" Height="35" HorizontalAlignment="Center" Padding="20,0,20,0"
Text="打开 GitHub 讨论页" EventType="打开网页" EventData="https://github.com/Hex-Dragon/PCL2/discussions/2528" />
</StackPanel>
</local:MyCard> </local:MyCard>
<!-- <!--
@@ -262,8 +310,10 @@
全部通用的属性: 全部通用的属性:
- Width、Height:设置宽度与高度 - Width、Height:设置宽度与高度
- ToolTip:设置鼠标指向时的提示文本 - ToolTip:设置鼠标指向时的提示文本
- Background:调整背景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
- Margin:调整外边距(格式为 左,上,右,下。例如 Margin="0,0,0,2" 即为 2 单位的下边距) - Margin:调整外边距(格式为 左,上,右,下。例如 Margin="0,0,0,2" 即为 2 单位的下边距)
- HorizontalAlignment:设置横向对齐方式,居左:Left、居中:Center、居右:Right、拉伸(默认):Stretch
- VerticalAlignment:设置纵向对齐方式,居上:Top、居中:Center、居下:Bottom、拉伸(默认):Stretch
- IsHitTestVisible:该控件是否响应交互(点击、指向动画等),True 为是(默认),False 为否
TextBlock(纯文本): TextBlock(纯文本):
- Text:设置显示的文本(如果要使用引号等特殊字符,自行百度 XAML 转义) - Text:设置显示的文本(如果要使用引号等特殊字符,自行百度 XAML 转义)
@@ -271,37 +321,32 @@ TextBlock(纯文本):
- FontSize:设置字号 - FontSize:设置字号
- FontWeight:设置为 Bold 时,文字加粗 - FontWeight:设置为 Bold 时,文字加粗
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明) - Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
Image(图片): Image(图片):
- Source:要显示的图片的网址 - Source:要显示的图片的网址,或 PCL 内置图片位置
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
local:MyCard(卡片): local:MyCard(卡片):
- Title:设置显示的标题文本 - Title:设置显示的标题文本
- CanSwap:这张卡片是否可以折叠,True 为是,False 为否 - CanSwap:卡片是否可以折叠,True 为是,False 为否
- IsSwaped:这张卡片是否默认折叠,要求 CanSwap 必须为 True - IsSwaped:卡片是否默认折叠,要求 CanSwap 必须为 True
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明) - HorizontalAlignment:若使用,要求 CanSwap 必须为 False
- HorizontalAlignment:设置对齐方式(Left、Center、Right)(使用时,CanSwap 属性需为 False) - UseAnimation:是否在展开等高度改变时触发动画,True 为是(默认),False 为否
- SwapLogoRight:卡片折叠时的箭头是朝下还是朝右,True 为朝右,False 为朝下(默认)
- HasMouseAnimation:是否在鼠标指向时改变卡片颜色,True 为是(默认),False 为否
local:MyHint(提示条): local:MyHint(提示条):
- Text:设置显示的文本(&#xA; 代表换行) - Text:设置显示的文本(&#xA; 代表换行)
- IsWarn:设置为 True 代表警告样式,False 代表提示样式 - IsWarn:设置为 True 代表警告样式,False 代表提示样式
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
local:MyButton(按钮): local:MyButton(按钮):
- Text:设置显示的文本 - Text:设置显示的文本
- Padding:设置内边距,格式与 Margin 一致 - Padding:设置内边距,格式与 Margin 一致
- ColorType:设置颜色主题(Highlight 为当前启动器的主题颜色,Red 为红色,默认为黑色) - ColorType:设置颜色主题(Highlight 为当前启动器的主题颜色,Red 为红色,默认为黑色)
- EventType、EventData:触发特定事件 - EventType、EventData:触发特定事件
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
local:MyTextButton(文本按钮): local:MyTextButton(文本按钮):
- Text:设置显示的文本 - Text:设置显示的文本
- EventType、EventData:触发特定事件 - EventType、EventData:触发特定事件
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
local:MyListItem(列表项): local:MyListItem(列表项):
- Logo:列表项的图标(所指定的可以是链接也可以是文件路径) - Logo:列表项的图标(所指定的可以是链接也可以是文件路径)
@@ -309,4 +354,17 @@ local:MyListItem(列表项):
- Info:列表项的详细信息,它将会以灰色显示在大标题下方 - Info:列表项的详细信息,它将会以灰色显示在大标题下方
- Type:若留空仅单纯展示信息,设置为 Clickable 触发相应活动 - Type:若留空仅单纯展示信息,设置为 Clickable 触发相应活动
--> local:MyIconTextButton(带图标的按钮):
- Text:设置显示的文本
- Logo:设置显示的图标
- LogoScale:设置图标的缩放,默认为 1
- ColorType:设置颜色主题(Highlight 为当前启动器的主题颜色,默认为黑色)
- EventType、EventData:触发特定事件
local:MyIconButton(图标按钮):
- Logo:设置显示的图标
- LogoScale:设置图标的缩放,默认为 1
- Theme:设置颜色主题,Color(默认)为当前启动器的主题颜色,也可设置为 White、Black、Red
- EventType、EventData:触发特定事件
-->
Binary file not shown.