Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0c634db78 | ||
|
|
7b623dbc2d | ||
|
|
037e6b19b4 | ||
|
|
5ff7aed46e | ||
|
|
3779d74e93 | ||
|
|
3b8de89309 | ||
|
|
f18ede2c7a | ||
|
|
4158e95f87 | ||
|
|
a77c2385cd | ||
|
|
fe5dc2d07f | ||
|
|
17c2049d2c | ||
|
|
0eebb8ec78 | ||
|
|
58d40976a7 | ||
|
|
b2f2bcf4b6 | ||
|
|
2a6708cc08 | ||
|
|
c9c00bc23f | ||
|
|
7fa092eb25 | ||
|
|
d4e692b69f | ||
|
|
8479c47da0 | ||
|
|
971aa0034d | ||
|
|
b717bb31c8 | ||
|
|
3037a9e384 | ||
|
|
6794b24292 | ||
|
|
d774de5436 | ||
|
|
e4ae1a057a | ||
|
|
41250d8387 | ||
|
|
79778490d5 | ||
|
|
e1e084aea3 | ||
|
|
836d55e445 | ||
|
|
3574931bfb | ||
|
|
c9aabd6a55 | ||
|
|
e6c6975047 | ||
|
|
9c41a807c3 | ||
|
|
a0fe5903f0 | ||
|
|
06be5b5b17 | ||
|
|
73d43310c9 | ||
|
|
9b54ea3dee | ||
|
|
fa5026ccd7 | ||
|
|
ca3e7f40e1 | ||
|
|
8004385d6f | ||
|
|
2c2bd2d489 | ||
|
|
80cf5957f2 | ||
|
|
7d139cbd73 | ||
|
|
8a770f63cf | ||
|
|
07e824b0a1 | ||
|
|
794c3003f3 | ||
|
|
d956fb2521 | ||
|
|
6005856661 | ||
|
|
920817cc34 | ||
|
|
c1167632a6 | ||
|
|
352799c0ed | ||
|
|
3e5ba70516 | ||
|
|
9420c37640 |
No files matched your search
@@ -1,5 +1,5 @@
|
||||
name: "Minecraft 崩溃"
|
||||
description: "PCL2 提示 “Minecraft 出现错误”,或游戏崩溃"
|
||||
description: "PCL 提示 “Minecraft 出现错误”,或游戏崩溃"
|
||||
labels: [· Bug, 新反馈]
|
||||
body:
|
||||
- type: checkboxes
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "**我所启动的游戏不是整合包,且未安装任何 Mod。** 安装 Mod 后的游戏崩溃不是 PCL2 的原因,请 **不要** 提交反馈。你可以在 QQ 群里或者论坛找人求助,但这里并不是你求助的地方。"
|
||||
- label: "**我所启动的游戏不是整合包,且未安装任何 Mod。** 安装 Mod 后的游戏崩溃基本不是 PCL 的原因,请 **不要** 提交反馈。你可以在论坛或社区找人求助,但这里并不是你求助的地方。"
|
||||
required: false
|
||||
- label: "**我已尝试使用 HMCL 启动,HMCL 没有出现问题。** 如果 HMCL 也无法启动就不是 PCL 导致的问题,请 **不要** 提交反馈。[下载 HMCL](https://hmcl.huangyuhui.net/download)"
|
||||
required: true
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是游戏本身的问题,并非 PCL2 的问题,请 **不要** 提交反馈。[下载其他启动器](https://www.mcbbs.net/forum.php?mod=forumdisplay&fid=43&filter=typeid&typeid=908)"
|
||||
required: true
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
@@ -26,8 +24,16 @@ body:
|
||||
- type: textarea
|
||||
id: "yml-3"
|
||||
attributes:
|
||||
label: 错误报告、日志与附件
|
||||
description: "上传 PCL2 提供的错误报告(在崩溃时选择导出错误报告)。如果没有,也可以上传 PCL 文件夹中的 Log1~5.txt。"
|
||||
label: PCL 错误报告、日志与附件
|
||||
description: "上传 PCL 提供的错误报告(在崩溃时选择导出错误报告)。如果没有,也可以上传 PCL 文件夹中的 Log1~5.txt。"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-4"
|
||||
attributes:
|
||||
label: HMCL 启动脚本
|
||||
description: "在 HMCL 中进入版本列表,点击版本右侧的三个点,选择 生成启动脚本。"
|
||||
placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传。"
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "下载或登录时卡住或失败"
|
||||
description: "下载游戏、Mod 或整合包,登录正版账号时出现问题"
|
||||
description: "下载游戏、Mod 或整合包,或登录正版账号时出现问题"
|
||||
labels: [· Bug, 新反馈]
|
||||
body:
|
||||
- type: checkboxes
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是网络环境不佳[(解决方法)](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-0hDr),并非 PCL2 的问题,请 **不要** 提交反馈。[下载其他启动器](https://www.mcbbs.net/forum.php?mod=forumdisplay&fid=43&filter=typeid&typeid=908)"
|
||||
- label: "**我已尝试使用其他启动器,其他启动器没有出现问题。** 如果其他启动器也存在问题,证明是网络环境不佳[(解决方法)](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-0hDr),并非 PCL 的问题,请 **不要** 提交反馈。[下载其他启动器](https://www.mcbbs.net/forum.php?mod=forumdisplay&fid=43&filter=typeid&typeid=908)"
|
||||
required: false
|
||||
- label: "我知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题可能是 PCL 导致的,和网络环境无关。"
|
||||
required: true
|
||||
- label: "我已知晓大多数此类问题都是网络环境不佳导致的,但我确实认为我的问题是 PCL2 导致的,和我的网络环境无关。"
|
||||
required: true
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -8,9 +8,7 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未修复这个 Bug。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: false
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一 Bug 未被提交过。"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -8,9 +8,7 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未实现这项建议。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一建议未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一建议未被提交过。"
|
||||
required: true
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一建议未在投票列表中。"
|
||||
required: true
|
||||
|
||||
@@ -2,7 +2,10 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 帮助文档反馈
|
||||
url: https://github.com/LTCatt/PCL2Help/issues
|
||||
about: 提交 PCL2 帮助系统(更多 → 帮助)相关的反馈
|
||||
about: 提交与 PCL 帮助文档(更多 → 帮助)中的具体内容相关的反馈
|
||||
- name: 提问
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E6%8F%90%E9%97%AE
|
||||
about: 我不想提交反馈,只是想问一些问题
|
||||
about: 我想问一些 PCL 相关的问题……
|
||||
- name: 讨论
|
||||
url: https://github.com/Hex-Dragon/PCL2/discussions/new?category=%E8%AE%A8%E8%AE%BA
|
||||
about: 我想讨论一些 PCL 相关的事情……
|
||||
@@ -8,13 +8,11 @@ body:
|
||||
label: "检查项"
|
||||
description: "请逐个检查下列项目,并勾选确认。"
|
||||
options:
|
||||
- label: "我已点击 `设置 → 启动器 → 检查更新` 确认了启动器已为最新版,且最新版未实现这一功能。[怎样更新?](https://shimo.im/docs/qKPttVvXKqPD8YDC#anchor-oTgb)"
|
||||
required: true
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 中搜索,确认了这一提案未被提交过。"
|
||||
- label: "我已在 [Issues 页面](https://github.com/Hex-Dragon/PCL2/issues?q=is%3Aissue+) 和 [常见&难检反馈及问题列表](https://github.com/Hex-Dragon/PCL2/discussions/1930) 中搜索,确认了这一提案未被提交过。"
|
||||
required: true
|
||||
- label: "我已查看 [功能投票页面](https://github.com/Hex-Dragon/PCL2/discussions/categories/%E5%8A%9F%E8%83%BD%E6%8A%95%E7%A5%A8/),确认了这一提案未在投票列表中。"
|
||||
required: true
|
||||
- label: "我已知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……"
|
||||
- label: "我知晓还没做的新功能真的太多了,忙不过来,所以新功能提案几乎不会被处理,也不建议再提交新功能提案 qwq……"
|
||||
required: true
|
||||
- type: textarea
|
||||
id: "yml-2"
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<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>
|
||||
<DropShadowEffect Opacity="0.3" BlurRadius="4" ShadowDepth="2" Color="{DynamicResource ColorObject1}" />
|
||||
</Border.Effect>
|
||||
@@ -147,7 +147,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<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"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
@@ -466,9 +466,9 @@
|
||||
</Style>
|
||||
<Style TargetType="ComboBoxItem" x:Key="MyComboBoxItem">
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="ToolTipService.Placement" Value="Right" />
|
||||
<Setter Property="ToolTipService.Placement" Value="Relative" />
|
||||
<Setter Property="ToolTipService.VerticalOffset" Value="-2" />
|
||||
<Setter Property="ToolTipService.HorizontalOffset" Value="-250" />
|
||||
<Setter Property="ToolTipService.HorizontalOffset" Value="200" />
|
||||
<Setter Property="ToolTipService.InitialShowDelay" Value="150" />
|
||||
<Setter Property="ToolTipService.BetweenShowDelay" Value="150" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
|
||||
@@ -41,8 +41,11 @@ Public Class Application
|
||||
MsgBox(ex.Message, MsgBoxStyle.Critical, "内存优化失败")
|
||||
Environment.Exit(-1)
|
||||
End Try
|
||||
Ram = My.Computer.Info.AvailablePhysicalMemory - Ram
|
||||
Environment.Exit(Math.Max(0, Ram / 1024)) '返回清理的内存量(K)
|
||||
If My.Computer.Info.AvailablePhysicalMemory < Ram Then '避免 ULong 相减出现负数
|
||||
Environment.Exit(0)
|
||||
Else
|
||||
Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
|
||||
End If
|
||||
#If DEBUG Then
|
||||
ElseIf e.Args(0) = "--make" Then
|
||||
'制作更新包
|
||||
@@ -71,7 +74,8 @@ Public Class Application
|
||||
Directory.CreateDirectory(PathAppdata)
|
||||
'检测单例
|
||||
#If Not DEBUG Then
|
||||
Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher 2 ")
|
||||
Dim WindowHwnd As IntPtr = FindWindow(Nothing, "Plain Craft Launcher ")
|
||||
If WindowHwnd = IntPtr.Zero Then FindWindow(Nothing, "Plain Craft Launcher 2 ")
|
||||
If WindowHwnd <> IntPtr.Zero Then
|
||||
'将已有的 PCL 窗口拖出来
|
||||
ShowWindowToTop(WindowHwnd)
|
||||
@@ -243,4 +247,12 @@ Public Class Application
|
||||
End Select
|
||||
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
|
||||
@@ -134,23 +134,21 @@
|
||||
|
||||
'实现自定义事件
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
Log("[Control] 按下按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
If Not String.IsNullOrEmpty(Tag) Then
|
||||
If Tag.ToString.StartsWith("链接-") OrElse Tag.ToString.StartsWith("启动-") Then
|
||||
Hint("主页自定义按钮语法已更新,且不再兼容老版本语法,请查看新的自定义示例!")
|
||||
End If
|
||||
If Not IsMouseDown Then Exit Sub
|
||||
Log("[Control] 按下按钮:" & Text)
|
||||
RaiseEvent Click(sender, e)
|
||||
If Not String.IsNullOrEmpty(Tag) Then
|
||||
If Tag.ToString.StartsWith("链接-") OrElse Tag.ToString.StartsWith("启动-") Then
|
||||
Hint("主页自定义按钮语法已更新,且不再兼容老版本语法,请查看新的自定义示例!")
|
||||
End If
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End If
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
@@ -159,8 +157,7 @@
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -41,10 +41,6 @@
|
||||
End Sub
|
||||
Private IsLoad As Boolean = False
|
||||
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 Not Title = "" AndAlso IsNothing(MainTextBlock) Then
|
||||
@@ -113,7 +109,7 @@
|
||||
Case 6
|
||||
ForgeDownloadListItemPreload(Stack, Stack.Tag, AddressOf ForgeSave_Click, True)
|
||||
Case 8
|
||||
CompFilesCardPreload(Stack, Stack.Tag, AddressOf FrmDownloadMod.ProjectClick)
|
||||
CompFilesCardPreload(Stack, Stack.Tag)
|
||||
End Select
|
||||
'实现控件虚拟化
|
||||
For Each Data As Object In Stack.Tag
|
||||
@@ -166,7 +162,9 @@
|
||||
End Sub
|
||||
|
||||
'事件
|
||||
Public Property HasMouseAnimation As Boolean = True
|
||||
Private Sub MyCard_MouseEnter(sender As Object, e As MouseEventArgs) Handles Me.MouseEnter
|
||||
If Not HasMouseAnimation Then Exit Sub
|
||||
Dim AniList As New List(Of AniData)
|
||||
If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush2", 150))
|
||||
If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush2", 150))
|
||||
@@ -178,6 +176,7 @@
|
||||
AniStart(AniList, "MyCard Mouse " & Uuid)
|
||||
End Sub
|
||||
Private Sub MyCard_MouseLeave(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
|
||||
If Not HasMouseAnimation Then Exit Sub
|
||||
Dim AniList As New List(Of AniData)
|
||||
If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush1", 250))
|
||||
If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush1", 250))
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Opacity="0.2" />
|
||||
</Border.Effect>
|
||||
</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>
|
||||
<RectangleGeometry x:Name="RectProgress" Rect="0,0,40,25" />
|
||||
<RectangleGeometry x:Name="RectProgress" Rect="0,5,40,35" />
|
||||
</Border.Clip>
|
||||
</Border>
|
||||
<Path x:Name="Path" Stretch="Uniform" Margin="12" Fill="{DynamicResource ColorBrush8}" RenderTransformOrigin="0.5,0.5">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
PanProgress.Visibility = Visibility.Collapsed
|
||||
Else
|
||||
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 Set
|
||||
End Property
|
||||
@@ -102,7 +102,7 @@
|
||||
Get
|
||||
Return _CanRightClick
|
||||
End Get
|
||||
Set(ByVal value As Boolean)
|
||||
Set(value As Boolean)
|
||||
_CanRightClick = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -179,7 +179,7 @@
|
||||
|
||||
AniStop("MyExtraButton Color " & Uuid)
|
||||
If IsMouseOver Then
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush2")
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush4")
|
||||
Else
|
||||
PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3")
|
||||
End If
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
If _IsWarn = value Then Exit Property
|
||||
_IsWarn = value
|
||||
If _IsWarn Then
|
||||
BorderBrush = New MyColor("#99FF4444")
|
||||
Gradient1.Color = New MyColor("#88FFBBBB")
|
||||
Gradient2.Color = New MyColor("#88FF8888")
|
||||
BorderBrush = New MyColor("#CCFF4444")
|
||||
Gradient1.Color = New MyColor("#BBFFBBBB")
|
||||
Gradient2.Color = New MyColor("#BBFF8888")
|
||||
Path.Fill = New MyColor("#BF0000")
|
||||
LabText.Foreground = New MyColor("#BF0000")
|
||||
BtnClose.Foreground = New MyColor("#BF0000")
|
||||
Path.Data = (New GeometryConverter).ConvertFromString("F1 M 58.5832,55.4172L 17.4169,55.4171C 15.5619,53.5621 15.5619,50.5546 17.4168,48.6996L 35.201,15.8402C 37.056,13.9852 40.0635,13.9852 41.9185,15.8402L 58.5832,48.6997C 60.4382,50.5546 60.4382,53.5622 58.5832,55.4172 Z M 34.0417,25.7292L 36.0208,41.9584L 39.9791,41.9583L 41.9583,25.7292L 34.0417,25.7292 Z M 38,44.3333C 36.2511,44.3333 34.8333,45.7511 34.8333,47.5C 34.8333,49.2489 36.2511,50.6667 38,50.6667C 39.7489,50.6667 41.1666,49.2489 41.1666,47.5C 41.1666,45.7511 39.7489,44.3333 38,44.3333 Z ")
|
||||
Else
|
||||
BorderBrush = New MyColor("#994D76FF")
|
||||
Gradient1.Color = New MyColor("#88B0D0FF")
|
||||
Gradient2.Color = New MyColor("#889EBAFF")
|
||||
BorderBrush = New MyColor("#CC4D76FF")
|
||||
Gradient1.Color = New MyColor("#BBB0D0FF")
|
||||
Gradient2.Color = New MyColor("#BB9EBAFF")
|
||||
Path.Fill = New MyColor("#0062BF")
|
||||
LabText.Foreground = New MyColor("#0062BF")
|
||||
BtnClose.Foreground = New MyColor("#0062BF")
|
||||
@@ -34,7 +34,7 @@
|
||||
Get
|
||||
Return LabText.Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
LabText.Text = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -43,13 +43,12 @@
|
||||
Get
|
||||
Return BtnClose.Visibility = Visibility.Visible
|
||||
End Get
|
||||
Set(ByVal value As Boolean)
|
||||
Set(value As Boolean)
|
||||
BtnClose.Visibility = If(value, Visibility.Visible, Visibility.Collapsed)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
Public RelativeSetup As String = ""
|
||||
|
||||
Private Sub MyHint_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
If CanClose AndAlso Setup.Get(RelativeSetup) Then
|
||||
Visibility = Visibility.Collapsed
|
||||
@@ -60,6 +59,40 @@
|
||||
AniDispose(Me, False)
|
||||
End Sub
|
||||
|
||||
'触发点击事件
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub MyHint_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Return
|
||||
IsMouseDown = False
|
||||
Log("[Control] 按下提示条" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
e.Handled = True
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Private Sub MyHint_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
End Sub
|
||||
Private Sub MyHint_MouseLeave() Handles Me.MouseLeave
|
||||
IsMouseDown = False
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyHint), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyHint), New PropertyMetadata(Nothing))
|
||||
|
||||
End Class
|
||||
Partial Public Module ModAnimation
|
||||
Public Sub AniDispose(Control As MyHint, RemoveFromChildren As Boolean, Optional CallBack As ParameterizedThreadStart = Nothing)
|
||||
|
||||
@@ -50,13 +50,31 @@
|
||||
|
||||
'触发点击事件
|
||||
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
|
||||
If IsMouseDown Then
|
||||
Log("[Control] 按下图标按钮" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
Button_MouseUp()
|
||||
End If
|
||||
If Not IsMouseDown Then Return
|
||||
Log("[Control] 按下图标按钮" & If(String.IsNullOrEmpty(Name), "", ":" & Name))
|
||||
RaiseEvent Click(sender, e)
|
||||
e.Handled = True
|
||||
Button_MouseUp()
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyIconButton), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyIconButton), New PropertyMetadata(Nothing))
|
||||
|
||||
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
|
||||
Private IsMouseDown As Boolean = False
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<Border x:Name="PanBack" x:Class="MyIconTextButton"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Background="{StaticResource ColorBrushSemiTransparent}" CornerRadius="13.5" MinHeight="27" MaxHeight="27">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path x:Name="ShapeLogo" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="White" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="7,0,12,1" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -0,0 +1,186 @@
|
||||
Public Class MyIconTextButton
|
||||
|
||||
'基础
|
||||
|
||||
Public Uuid As Integer = GetUuid()
|
||||
Public Event Check(sender As Object, raiseByMouse As Boolean)
|
||||
Public Event Change(sender As Object, raiseByMouse As Boolean)
|
||||
Public Sub RaiseChange()
|
||||
RaiseEvent Change(Me, False)
|
||||
End Sub '使外部程序可以引发本控件的 Change 事件
|
||||
|
||||
'自定义属性
|
||||
|
||||
Public Property Logo As String
|
||||
Get
|
||||
Return ShapeLogo.Data.ToString
|
||||
End Get
|
||||
Set(value As String)
|
||||
ShapeLogo.Data = (New GeometryConverter).ConvertFromString(value)
|
||||
End Set
|
||||
End Property
|
||||
Private _LogoScale As Double = 1
|
||||
Public Property LogoScale() As Double
|
||||
Get
|
||||
Return _LogoScale
|
||||
End Get
|
||||
Set(value As Double)
|
||||
_LogoScale = value
|
||||
If Not IsNothing(ShapeLogo) Then ShapeLogo.RenderTransform = New ScaleTransform With {.ScaleX = LogoScale, .ScaleY = LogoScale}
|
||||
End Set
|
||||
End Property
|
||||
|
||||
Public Property Text As String
|
||||
Get
|
||||
Return GetValue(TextProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(TextProperty, value)
|
||||
End Set
|
||||
End Property '内容
|
||||
Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(New PropertyChangedCallback(
|
||||
Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
|
||||
If Not IsNothing(sender) Then CType(sender, MyIconTextButton).LabText.Text = e.NewValue
|
||||
End Sub)))
|
||||
Public Enum ColorState
|
||||
Black
|
||||
Highlight
|
||||
End Enum
|
||||
Private _ColorType As ColorState = ColorState.Black
|
||||
Public Property ColorType As ColorState
|
||||
Get
|
||||
Return _ColorType
|
||||
End Get
|
||||
Set(value As ColorState)
|
||||
_ColorType = value
|
||||
RefreshColor()
|
||||
End Set
|
||||
End Property '颜色类别
|
||||
Public Shared ReadOnly ColorTypeProperty As DependencyProperty =
|
||||
DependencyProperty.Register("ColorType", GetType(ColorState), GetType(MyIconTextButton), New PropertyMetadata())
|
||||
|
||||
'点击事件
|
||||
|
||||
Public Event Click(sender As Object, e As RouteEventArgs)
|
||||
Private IsMouseDown As Boolean = False
|
||||
Private Sub MyIconTextButton_MouseUp() Handles Me.MouseLeftButtonUp
|
||||
If Not IsMouseDown Then Exit Sub
|
||||
Log("[Control] 按下带图标按钮:" & Text)
|
||||
IsMouseDown = False
|
||||
RaiseEvent Click(Me, New RouteEventArgs(True))
|
||||
ModEvent.TryStartEvent(EventType, EventData)
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Private Sub MyIconTextButton_MouseDown() Handles Me.MouseLeftButtonDown
|
||||
IsMouseDown = True
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Private Sub MyIconTextButton_MouseLeave() Handles Me.MouseLeave
|
||||
IsMouseDown = False
|
||||
RefreshColor()
|
||||
End Sub
|
||||
Public Property EventType As String
|
||||
Get
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventTypeProperty As DependencyProperty = DependencyProperty.Register("EventType", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(Nothing))
|
||||
Public Property EventData As String
|
||||
Get
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
Public Shared ReadOnly EventDataProperty As DependencyProperty = DependencyProperty.Register("EventData", GetType(String), GetType(MyIconTextButton), New PropertyMetadata(Nothing))
|
||||
|
||||
'动画
|
||||
|
||||
Private Const AnimationTimeOfMouseIn As Integer = 100 '鼠标指向动画长度
|
||||
Private Const AnimationTimeOfMouseOut As Integer = 150 '鼠标移出动画长度
|
||||
Private Sub RefreshColor(Optional obj = Nothing, Optional e = Nothing) Handles Me.MouseEnter, Me.Loaded, Me.IsEnabledChanged
|
||||
Try
|
||||
If IsLoaded AndAlso AniControlEnabled = 0 AndAlso Not False.Equals(e) Then '防止默认属性变更触发动画,若强制不执行动画,则 e 为 False
|
||||
|
||||
Select Case ColorType
|
||||
Case ColorState.Black
|
||||
If IsMouseDown Then
|
||||
'按下
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrush6", 70), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsMouseOver Then
|
||||
'指向
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseIn),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseIn)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrushBg1", AnimationTimeOfMouseIn), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsEnabled Then
|
||||
'正常
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush1", AnimationTimeOfMouseOut),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush1", AnimationTimeOfMouseOut)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
Else
|
||||
'禁用
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrushGray5", 100),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrushGray5", 100)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
End If
|
||||
Case ColorState.Highlight
|
||||
If IsMouseDown Then
|
||||
'按下
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrush6", 70), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsMouseOver Then
|
||||
'指向
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseIn),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseIn)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, "ColorBrushBg1", AnimationTimeOfMouseIn), "MyIconTextButton Color " & Uuid)
|
||||
ElseIf IsEnabled Then
|
||||
'正常
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrush3", AnimationTimeOfMouseOut),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrush3", AnimationTimeOfMouseOut)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
Else
|
||||
'禁用
|
||||
AniStart({
|
||||
AaColor(ShapeLogo, Shapes.Path.FillProperty, "ColorBrushGray5", 100),
|
||||
AaColor(LabText, TextBlock.ForegroundProperty, "ColorBrushGray5", 100)
|
||||
}, "MyIconTextButton Checked " & Uuid)
|
||||
AniStart(AaColor(Me, BackgroundProperty, ColorSemiTransparent - Background, AnimationTimeOfMouseOut), "MyIconTextButton Color " & Uuid)
|
||||
End If
|
||||
End Select
|
||||
|
||||
Else
|
||||
|
||||
'不使用动画
|
||||
AniStop("MyIconTextButton Checked " & Uuid)
|
||||
AniStop("MyIconTextButton Color " & Uuid)
|
||||
Select Case ColorType
|
||||
Case ColorState.Black
|
||||
Background = ColorSemiTransparent
|
||||
ShapeLogo.SetResourceReference(Shapes.Path.FillProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray5"))
|
||||
LabText.SetResourceReference(TextBlock.ForegroundProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray5"))
|
||||
Case ColorState.Highlight
|
||||
Background = ColorSemiTransparent
|
||||
ShapeLogo.SetResourceReference(Shapes.Path.FillProperty, If(IsEnabled, "ColorBrush3", "ColorBrushGray5"))
|
||||
LabText.SetResourceReference(TextBlock.ForegroundProperty, If(IsEnabled, "ColorBrush3", "ColorBrushGray5"))
|
||||
End Select
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新带图标按钮颜色出错")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -15,7 +15,7 @@
|
||||
If _RectBack Is Nothing Then
|
||||
Dim Rect As New Border With {
|
||||
.Name = "RectBack",
|
||||
.CornerRadius = New CornerRadius(If(IsScaleAnimationEnabled, 6, 0)),
|
||||
.CornerRadius = New CornerRadius(If(IsScaleAnimationEnabled OrElse Height > 40, 6, 0)),
|
||||
.RenderTransform = If(IsScaleAnimationEnabled, New ScaleTransform(0.8, 0.8), Nothing),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
.BorderThickness = New Thickness(GetWPFSize(1)),
|
||||
@@ -225,6 +225,7 @@
|
||||
If _Logo.ToLower.StartsWith("http") Then
|
||||
'网络图片
|
||||
PathLogo = New Image With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable,
|
||||
.Source = New ImageSourceConverter().ConvertFromString(_Logo),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
@@ -235,6 +236,7 @@
|
||||
'位图
|
||||
Dim Bitmap = New MyBitmap(_Logo)
|
||||
PathLogo = New Canvas With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable,
|
||||
.Background = Bitmap,
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
@@ -252,6 +254,7 @@
|
||||
Else
|
||||
'矢量图
|
||||
PathLogo = New Shapes.Path With {
|
||||
.Tag = Me,
|
||||
.IsHitTestVisible = LogoClickable, .HorizontalAlignment = HorizontalAlignment.Center, .VerticalAlignment = VerticalAlignment.Center, .Stretch = Stretch.Uniform,
|
||||
.Data = (New GeometryConverter).ConvertFromString(_Logo),
|
||||
.RenderTransformOrigin = New Point(0.5, 0.5),
|
||||
@@ -267,7 +270,7 @@
|
||||
If LogoClickable Then
|
||||
AddHandler PathLogo.MouseLeave, Sub(sender, e) IsLogoDown = False
|
||||
AddHandler PathLogo.MouseLeftButtonDown, Sub(sender, e) IsLogoDown = True
|
||||
AddHandler PathLogo.MouseLeftButtonUp, Sub(sender, e) If IsLogoDown Then IsLogoDown = False : RaiseEvent LogoClick(sender, e)
|
||||
AddHandler PathLogo.MouseLeftButtonUp, Sub(sender, e) If IsLogoDown Then IsLogoDown = False : RaiseEvent LogoClick(sender.Tag, e)
|
||||
End If
|
||||
End If
|
||||
'改变行距
|
||||
@@ -286,6 +289,9 @@
|
||||
End Property
|
||||
|
||||
'图标的点击
|
||||
''' <summary>
|
||||
''' 该 Logo 是否可用点击触发事件。需要在 Logo 属性之前设置。
|
||||
''' </summary>
|
||||
Public Property LogoClickable As Boolean = False
|
||||
Private IsLogoDown As Boolean = False
|
||||
|
||||
@@ -447,7 +453,7 @@
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Center
|
||||
RectCheck.Margin = New Thickness(-1, 0, 0, 0)
|
||||
End If
|
||||
Anim.Add(AaColor(Me, ForegroundProperty, "ColorBrush3", 200))
|
||||
Anim.Add(AaColor(Me, ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"), 200))
|
||||
Else
|
||||
'由有变无
|
||||
If Not IsNothing(RectCheck) Then
|
||||
@@ -469,7 +475,7 @@
|
||||
RectCheck.Opacity = 1
|
||||
RectCheck.VerticalAlignment = VerticalAlignment.Stretch
|
||||
End If
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush3")
|
||||
SetResourceReference(ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"))
|
||||
Else
|
||||
If Not IsNothing(RectCheck) Then
|
||||
RectCheck.Height = 0
|
||||
@@ -615,12 +621,14 @@
|
||||
Else
|
||||
If ButtonStack IsNot Nothing Then Ani.Add(AaOpacity(ButtonStack, -ButtonStack.Opacity, Time * 0.5))
|
||||
Ani.Add(AaOpacity(RectBack, -RectBack.Opacity, Time))
|
||||
If IsScaleAnimationEnabled Then Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrush7"), Time),
|
||||
AaScaleTransform(Me, 1 - CType(Me.RenderTransform, ScaleTransform).ScaleX, Time * 3,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, -0.246, 1,,, True)
|
||||
})
|
||||
If IsScaleAnimationEnabled Then
|
||||
Ani.AddRange({
|
||||
AaColor(RectBack, Border.BackgroundProperty, If(IsMouseDown, "ColorBrush6", "ColorBrush7"), Time),
|
||||
AaScaleTransform(Me, 1 - CType(RenderTransform, ScaleTransform).ScaleX, Time * 3,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, 0.996 - CType(RectBack.RenderTransform, ScaleTransform).ScaleX, Time,, New AniEaseOutFluent),
|
||||
AaScaleTransform(RectBack, -0.246, 1,,, True)
|
||||
})
|
||||
End If
|
||||
End If
|
||||
AniStart(Ani, "ListItem Color " & Uuid)
|
||||
Else
|
||||
@@ -647,7 +655,7 @@
|
||||
|
||||
Private Sub MyListItem_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
If Checked Then
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush3")
|
||||
SetResourceReference(ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"))
|
||||
Else
|
||||
SetResourceReference(ForegroundProperty, "ColorBrush1")
|
||||
End If
|
||||
|
||||
@@ -46,7 +46,7 @@ Public Class MyLoading
|
||||
Get
|
||||
Return _Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
_Text = value
|
||||
RefreshText()
|
||||
End Set
|
||||
@@ -57,7 +57,7 @@ Public Class MyLoading
|
||||
Get
|
||||
Return _TextError
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
_TextError = value
|
||||
RefreshText()
|
||||
End Set
|
||||
@@ -78,7 +78,10 @@ Public Class MyLoading
|
||||
Ex = Ex.InnerException
|
||||
Loop
|
||||
LabText.Text = StrTrim(Ex.Message)
|
||||
If LabText.Text.Contains("远程主机强迫关闭了一个现有的连接") OrElse LabText.Text.Contains("操作已超时") OrElse LabText.Text.Contains("操作超时") OrElse LabText.Text.Contains("服务器超时") OrElse LabText.Text.Contains("连接超时") Then LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
|
||||
If {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
|
||||
"操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) LabText.Text.Contains(s)) Then
|
||||
LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
LabText.Text = TextError
|
||||
|
||||
@@ -29,21 +29,28 @@
|
||||
'逐个进入动画
|
||||
Dim AniList As New List(Of AniData)
|
||||
Dim Id As Integer = 0, Delay As Integer = 0
|
||||
Dim Controls = GetAllAnimControls()
|
||||
For Each Element As FrameworkElement In Controls
|
||||
Element.Opacity = 0
|
||||
Element.RenderTransform = New TranslateTransform(-25, 0)
|
||||
AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
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))
|
||||
For Each Element As FrameworkElement In GetAllAnimControls(True)
|
||||
If Element.Visibility = Visibility.Collapsed Then
|
||||
'还原之前的隐藏动画可能导致的改变(#2436)
|
||||
Element.Opacity = 1
|
||||
Element.RenderTransform = New TranslateTransform(0, 0)
|
||||
If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = True
|
||||
Else
|
||||
Element.Opacity = 0
|
||||
Element.RenderTransform = New TranslateTransform(-25, 0)
|
||||
If TypeOf Element Is MyListItem Then CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
|
||||
AniList.Add(AaOpacity(Element, If(TypeOf Element Is TextBlock, 0.6, 1), 200, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
|
||||
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
|
||||
Delay += Math.Max(8, 20 - Id) * 2.5
|
||||
Id += 1
|
||||
Next
|
||||
AniStart(AniList, "PageLeft PageChange " & Uuid)
|
||||
End If
|
||||
@@ -74,23 +81,22 @@
|
||||
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)
|
||||
GetAllAnimControls(AnimatedControl, AllControls)
|
||||
GetAllAnimControls(AnimatedControl, AllControls, IgnoreInvisibility)
|
||||
Return AllControls
|
||||
End Function
|
||||
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement))
|
||||
If Element.Visibility = Visibility.Collapsed Then Exit Sub
|
||||
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean)
|
||||
If Not IgnoreInvisibility AndAlso Element.Visibility = Visibility.Collapsed Then Exit Sub
|
||||
If TypeOf Element Is MyTextButton Then
|
||||
AllControls.Add(Element)
|
||||
ElseIf TypeOf Element Is MyListItem Then
|
||||
CType(Element, MyListItem).IsMouseOverAnimationEnabled = False
|
||||
AllControls.Add(Element)
|
||||
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
|
||||
For Each Element2 As FrameworkElement In CType(Element, Panel).Children
|
||||
GetAllAnimControls(Element2, AllControls)
|
||||
GetAllAnimControls(Element2, AllControls, IgnoreInvisibility)
|
||||
Next
|
||||
Else
|
||||
AllControls.Add(Element)
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
''' </summary>
|
||||
Public Sub PageOnExit()
|
||||
If ModeDebug Then Log("[UI] 已触发 PageOnExit")
|
||||
RaiseEvent PageExit()
|
||||
Select Case PageState
|
||||
Case PageStates.ContentEnter, PageStates.ContentStay
|
||||
PageState = PageStates.PageExit
|
||||
@@ -168,6 +169,7 @@
|
||||
Case PageStates.PageExit, PageStates.Empty
|
||||
End Select
|
||||
End Sub
|
||||
Public Event PageExit()
|
||||
''' <summary>
|
||||
''' 即将切换到其他页面,需要强制完成页面状态清理。
|
||||
''' 需要立即切换至 Empty。
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Background="{StaticResource ColorBrushSemiTransparent}" CornerRadius="13.5" MinHeight="27" MaxHeight="27">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path x:Name="ShapeLogo" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="White" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="8,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
<TextBlock x:Name="LabText" VerticalAlignment="Center" Grid.Column="1" Foreground="White" Margin="8,0,12,1" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
@@ -252,7 +252,7 @@
|
||||
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "刷新按钮颜色出错")
|
||||
Log(ex, "刷新单选按钮颜色出错")
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
'如果当前是在对有滚动条的下拉框或文本框执行,则不接管操作
|
||||
Exit Sub
|
||||
End If
|
||||
PerformVerticalOffsetDelta(-e.Delta)
|
||||
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
|
||||
Public Sub PerformVerticalOffsetDelta(Delta As Double)
|
||||
AniStart(AaDouble(Sub(AnimDelta As Double)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Class MySearchBox
|
||||
Inherits MyCard
|
||||
|
||||
Public Event TextChanged(ByVal sender As Object, ByVal e As EventArgs)
|
||||
Public Event TextChanged(sender As Object, e As EventArgs)
|
||||
Private Sub MySearchBox_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||||
TextBox.Focus()
|
||||
End Sub
|
||||
@@ -11,7 +11,7 @@
|
||||
Get
|
||||
Return TextBox.HintText
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
TextBox.HintText = value
|
||||
End Set
|
||||
End Property
|
||||
@@ -19,7 +19,7 @@
|
||||
Get
|
||||
Return TextBox.Text
|
||||
End Get
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
TextBox.Text = value
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
MinHeight="16" UseLayoutRounding="False" SnapsToDevicePixels="False" BorderBrush="{DynamicResource ColorBrushBg0}" Name="PanBack" Background="{StaticResource ColorBrushSemiTransparent}" Focusable="True">
|
||||
<Grid x:Name="PanMain" MinHeight="16" MaxHeight="16" IsHitTestVisible="False">
|
||||
<Line Height="4" Name="LineBack" Stroke="{DynamicResource ColorBrushBg0}" Opacity="0.3" StrokeThickness="1" X2="10000" Y1="2" Y2="2" HorizontalAlignment="Right" Width="0" SnapsToDevicePixels="True" Margin="0,0,1,0" />
|
||||
<Line Height="4" Name="LineFore" Stroke="{Binding ElementName=PanBack, Path=BorderBrush}" StrokeThickness="1" X2="10000" Y1="2" Y2="2" Width="0" HorizontalAlignment="Left" SnapsToDevicePixels="True" Margin="1,0,0,0" />
|
||||
<Line Height="4" Name="LineFore" Stroke="{Binding ElementName=PanBack, Path=BorderBrush}" StrokeThickness="2" X2="10000" Y1="2" Y2="2" Width="0" HorizontalAlignment="Left" SnapsToDevicePixels="True" Margin="1,0,0,0" />
|
||||
<Ellipse HorizontalAlignment="Left" Width="10" Height="10"
|
||||
Stroke="{Binding BorderBrush, ElementName=PanBack}" StrokeThickness="1.2"
|
||||
Fill="{DynamicResource ColorBrushBg0}" Name="ShapeDot" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
Return GetValue(EventTypeProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventTypeProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
@@ -101,7 +101,7 @@
|
||||
Return GetValue(EventDataProperty)
|
||||
End Get
|
||||
|
||||
Set(ByVal value As String)
|
||||
Set(value As String)
|
||||
SetValue(EventDataProperty, value)
|
||||
End Set
|
||||
End Property
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
xmlns:local="clr-namespace:PCL"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
|
||||
x:Name="WindMain" x:Class="FormMain" Style="{StaticResource MyWindow}"
|
||||
AllowDrop="True" Topmost="True" Title="Plain Craft Launcher 2 " AllowsTransparency="True" MinHeight="470" MinWidth="810" ScrollViewer.VerticalScrollBarVisibility="Disabled" Icon="/Plain Craft Launcher 2;component/Images/icon.ico" WindowStyle="None" ResizeMode="CanMinimize" Background="{x:Null}" d:DesignWidth="870" d:DesignHeight="520" Width="850" Height="500" RenderTransformOrigin="0.5,0.5">
|
||||
AllowDrop="True" Topmost="True" Title="Plain Craft Launcher " AllowsTransparency="True" MinHeight="470" MinWidth="810" ScrollViewer.VerticalScrollBarVisibility="Disabled" Icon="/Plain Craft Launcher 2;component/Images/icon.ico" WindowStyle="None" ResizeMode="CanMinimize" Background="{x:Null}" d:DesignWidth="870" d:DesignHeight="520" Width="850" Height="500" RenderTransformOrigin="0.5,0.5">
|
||||
<Window.TaskbarItemInfo>
|
||||
<TaskbarItemInfo ThumbnailClipMargin="{Binding Margin, ElementName=PanForm}" />
|
||||
</Window.TaskbarItemInfo>
|
||||
|
||||
@@ -10,6 +10,31 @@ Public Class FormMain
|
||||
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
|
||||
'统计更新日志条目
|
||||
#If BETA Then
|
||||
If LastVersion < 308 Then 'Release 2.6.10
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "为版本独立设置添加忽略 Java 兼容性警告选项"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复开始或结束游戏时可能报错的 Bug"))
|
||||
FeatureCount += 31
|
||||
BugCount += 37
|
||||
End If
|
||||
If LastVersion < 302 Then 'Release 2.6.7
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug"))
|
||||
BugCount += 1
|
||||
End If
|
||||
If LastVersion < 300 Then 'Release 2.6.6
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持选择多种预设的主页"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod 管理中允许多选 Mod 进行批量操作"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化崩溃分析,添加多种崩溃情况的判断"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化联网获取的主页的加载与缓存"))
|
||||
FeatureCount += 40
|
||||
BugCount += 34
|
||||
End If
|
||||
If LastVersion < 296 Then 'Release 2.6.3
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增内存优化功能,可以将所有程序的物理内存占用降低约 1/3"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "在选择 OptiFine 与 Fabric 后会自动选择 OptiFabric"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化崩溃分析,添加多种崩溃情况的判断"))
|
||||
FeatureCount += 21
|
||||
BugCount += 30
|
||||
End If
|
||||
If LastVersion < 293 Then 'Release 2.6.1
|
||||
BugCount += 1
|
||||
End If
|
||||
@@ -109,6 +134,44 @@ Public Class FormMain
|
||||
'3:BUG+ IMP* FEAT-
|
||||
'2:BUG* IMP-
|
||||
'1:BUG-
|
||||
If LastVersion < 307 Then 'Snapshot 2.6.10
|
||||
FeatureCount += 2
|
||||
BugCount += 3
|
||||
End If
|
||||
If LastVersion < 306 Then 'Snapshot 2.6.9
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "为版本独立设置添加忽略 Java 兼容性警告选项"))
|
||||
FeatureCount += 9
|
||||
BugCount += 11
|
||||
End If
|
||||
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
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "再次修复网络条件差时可能无法下载 MC 的 Bug"))
|
||||
BugCount += 1
|
||||
End If
|
||||
If LastVersion < 301 Then 'Snapshot 2.6.6
|
||||
FeatureCount += 4
|
||||
BugCount += 2
|
||||
End If
|
||||
If LastVersion < 299 Then 'Snapshot 2.6.5
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持选择多种预设的主页"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化联网获取的主页的加载与缓存"))
|
||||
FeatureCount += 18
|
||||
BugCount += 13
|
||||
End If
|
||||
If LastVersion < 298 Then 'Snapshot 2.6.4
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "Mod 管理中允许多选 Mod 进行批量操作"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化崩溃分析,添加多种崩溃情况的判断"))
|
||||
FeatureCount += 18
|
||||
BugCount += 19
|
||||
End If
|
||||
If LastVersion < 297 Then 'Snapshot 2.6.3
|
||||
FeatureCount += 12
|
||||
BugCount += 14
|
||||
End If
|
||||
If LastVersion < 294 Then 'Snapshot 2.6.2
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增内存优化功能,可以将所有程序的物理内存占用降低约 1/3"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "在选择 OptiFine 与 Fabric 后会自动选择 OptiFabric"))
|
||||
@@ -288,6 +351,7 @@ Public Class FormMain
|
||||
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
|
||||
''' <summary>
|
||||
''' 是否为联机提权后自动重启。
|
||||
@@ -366,8 +430,14 @@ Public Class FormMain
|
||||
End If
|
||||
'加载窗口
|
||||
ThemeRefreshMain()
|
||||
Height = ReadReg("WindowHeight", MinHeight + 50)
|
||||
Width = ReadReg("WindowWidth", MinWidth + 50)
|
||||
Try
|
||||
Height = ReadReg("WindowHeight", MinHeight + 50)
|
||||
Width = ReadReg("WindowWidth", MinWidth + 50)
|
||||
Catch ex As Exception '修复 #2019
|
||||
Log(ex, "读取窗口默认大小失败", LogLevel.Hint)
|
||||
Height = MinHeight + 50
|
||||
Width = MinWidth + 50
|
||||
End Try
|
||||
Topmost = False
|
||||
If FrmStart IsNot Nothing Then FrmStart.Close(New TimeSpan(0, 0, 0, 0, 400 / AniSpeed))
|
||||
'更改窗口
|
||||
@@ -393,6 +463,7 @@ Public Class FormMain
|
||||
TimerMainStartRun()
|
||||
'加载池
|
||||
RunInNewThread(Sub()
|
||||
'EULA 提示
|
||||
If Not Setup.Get("SystemEula") Then
|
||||
Select Case MyMsgBox("在使用 PCL 前,请同意 PCL 的用户协议与免责声明。", "协议授权", "同意", "拒绝", "查看用户协议与免责声明",
|
||||
Button3Action:=Sub() OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW"))
|
||||
@@ -402,6 +473,7 @@ Public Class FormMain
|
||||
EndProgram(False)
|
||||
End Select
|
||||
End If
|
||||
'启动加载器池
|
||||
Try
|
||||
JavaListInit() '延后到同意协议后再执行,避免在初次启动时进行进程操作
|
||||
Thread.Sleep(200)
|
||||
@@ -411,11 +483,22 @@ Public Class FormMain
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化加载池运行失败", LogLevel.Feedback)
|
||||
End Try
|
||||
'清理自动更新文件
|
||||
Try
|
||||
If File.Exists(Path & "PCL\Plain Craft Launcher 2.exe") Then File.Delete(Path & "PCL\Plain Craft Launcher 2.exe")
|
||||
Catch ex As Exception
|
||||
Log(ex, "清理自动更新文件失败")
|
||||
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)
|
||||
|
||||
Log("[Start] 第三阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
|
||||
@@ -585,6 +668,7 @@ Public Class FormMain
|
||||
Process.GetCurrentProcess.Kill()
|
||||
Else
|
||||
Environment.Exit(ReturnCode)
|
||||
Process.GetCurrentProcess.Kill()
|
||||
End If
|
||||
End Sub
|
||||
Private Sub BtnTitleClose_Click(sender As Object, e As RoutedEventArgs) Handles BtnTitleClose.Click
|
||||
@@ -612,6 +696,7 @@ Public Class FormMain
|
||||
PanForm.Height = BorderForm.ActualHeight + 0.001
|
||||
PanMain.Width = PanForm.Width
|
||||
PanMain.Height = Math.Max(0, PanForm.Height - PanTitle.ActualHeight)
|
||||
If WindowState = WindowState.Maximized Then WindowState = WindowState.Normal '修复 #1938
|
||||
End Sub
|
||||
|
||||
'最小化
|
||||
@@ -724,31 +809,32 @@ Public Class FormMain
|
||||
Hint($"输入的 Authlib 验证服务器不符合网址格式({AuthlibServer})!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
If McVersionCurrent Is Nothing Then
|
||||
Dim TargetVersion = If(PageCurrent = PageType.VersionSetup, PageVersionLeft.Version, McVersionCurrent)
|
||||
If TargetVersion Is Nothing Then
|
||||
Hint("请先下载游戏,再设置第三方登录!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
If AuthlibServer = "https://littleskin.cn/api/yggdrasil" Then
|
||||
'LittleSkin
|
||||
If MyMsgBox($"是否要在版本 {McVersionCurrent.Name} 中开启 LittleSkin 登录?" & vbCrLf &
|
||||
If MyMsgBox($"是否要在版本 {TargetVersion.Name} 中开启 LittleSkin 登录?" & vbCrLf &
|
||||
"你可以在 版本设置 → 设置 → 服务器选项 中修改登录方式。", "第三方登录开启确认", "确定", "取消") = 2 Then
|
||||
Exit Sub
|
||||
End If
|
||||
Setup.Set("VersionServerLogin", 4, Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthServer", "https://littleskin.cn/api/yggdrasil", Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthRegister", "https://littleskin.cn/auth/register", Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthName", "LittleSkin 登录", Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerLogin", 4, Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthServer", "https://littleskin.cn/api/yggdrasil", Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthRegister", "https://littleskin.cn/auth/register", Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthName", "LittleSkin 登录", Version:=TargetVersion)
|
||||
Else
|
||||
'第三方 Authlib 服务器
|
||||
If MyMsgBox($"是否要在版本 {McVersionCurrent.Name} 中开启第三方登录?" & vbCrLf &
|
||||
If MyMsgBox($"是否要在版本 {TargetVersion.Name} 中开启第三方登录?" & vbCrLf &
|
||||
$"登录服务器:{AuthlibServer}" & vbCrLf & vbCrLf &
|
||||
"你可以在 版本设置 → 设置 → 服务器选项 中修改登录方式。", "第三方登录开启确认", "确定", "取消") = 2 Then
|
||||
Exit Sub
|
||||
End If
|
||||
Setup.Set("VersionServerLogin", 4, Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthServer", AuthlibServer, Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthRegister", AuthlibServer.Replace("api/yggdrasil", "auth/register"), Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerAuthName", "", Version:=McVersionCurrent)
|
||||
Setup.Set("VersionServerLogin", 4, Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthServer", AuthlibServer, Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthRegister", AuthlibServer.Replace("api/yggdrasil", "auth/register"), Version:=TargetVersion)
|
||||
Setup.Set("VersionServerAuthName", "", Version:=TargetVersion)
|
||||
End If
|
||||
If PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionSetup Then
|
||||
'正在服务器选项页,需要刷新设置项显示
|
||||
@@ -764,7 +850,12 @@ Public Class FormMain
|
||||
End Try
|
||||
ElseIf e.Data.GetDataPresent(DataFormats.FileDrop) Then
|
||||
'获取文件并检查
|
||||
Dim FilePathList As New List(Of String)(CType(e.Data.GetData(DataFormats.FileDrop), Array))
|
||||
Dim FilePathRaw = e.Data.GetData(DataFormats.FileDrop)
|
||||
If FilePathRaw Is Nothing Then '#2690
|
||||
Hint("请将文件解压后再拖入!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim FilePathList As New List(Of String)(CType(FilePathRaw, IEnumerable(Of String)))
|
||||
e.Handled = True
|
||||
If Directory.Exists(FilePathList.First) AndAlso Not File.Exists(FilePathList.First) Then
|
||||
Hint("请拖入一个文件,而非文件夹!", HintType.Critical)
|
||||
@@ -798,6 +889,7 @@ Public Class FormMain
|
||||
RunInUi(Sub()
|
||||
Setup.Set("UiCustomType", 1)
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
Hint("已加载主页自定义文件!", HintType.Finish)
|
||||
End Sub)
|
||||
Exit Sub
|
||||
End If
|
||||
@@ -896,7 +988,7 @@ Install:
|
||||
Get
|
||||
Return _Hidden
|
||||
End Get
|
||||
Set(ByVal value As Boolean)
|
||||
Set(value As Boolean)
|
||||
If _Hidden = value Then Exit Property
|
||||
_Hidden = value
|
||||
If value Then
|
||||
@@ -943,6 +1035,7 @@ Install:
|
||||
#Region "切换页面"
|
||||
|
||||
'页面种类与属性
|
||||
'注意,这一枚举在 “切换页面” EventType 中调用,应视作公开 API 的一部分
|
||||
''' <summary>
|
||||
''' 页面种类。
|
||||
''' </summary>
|
||||
@@ -950,43 +1043,43 @@ Install:
|
||||
''' <summary>
|
||||
''' 启动。
|
||||
''' </summary>
|
||||
Launch
|
||||
Launch = 0
|
||||
''' <summary>
|
||||
''' 下载。
|
||||
''' </summary>
|
||||
Download
|
||||
Download = 1
|
||||
''' <summary>
|
||||
''' 联机。
|
||||
''' </summary>
|
||||
Link
|
||||
Link = 2
|
||||
''' <summary>
|
||||
''' 设置。
|
||||
''' </summary>
|
||||
Setup
|
||||
Setup = 3
|
||||
''' <summary>
|
||||
''' 更多。
|
||||
''' </summary>
|
||||
Other
|
||||
Other = 4
|
||||
''' <summary>
|
||||
''' 版本选择。这是一个副页面。
|
||||
''' </summary>
|
||||
VersionSelect
|
||||
VersionSelect = 5
|
||||
''' <summary>
|
||||
''' 下载管理。这是一个副页面。
|
||||
''' </summary>
|
||||
DownloadManager
|
||||
DownloadManager = 6
|
||||
''' <summary>
|
||||
''' 版本设置。这是一个副页面。
|
||||
''' </summary>
|
||||
VersionSetup
|
||||
VersionSetup = 7
|
||||
''' <summary>
|
||||
''' 资源工程详情。这是一个副页面。
|
||||
''' </summary>
|
||||
CompDetail
|
||||
CompDetail = 8
|
||||
''' <summary>
|
||||
''' 帮助详情。这是一个副页面。
|
||||
''' </summary>
|
||||
HelpDetail
|
||||
HelpDetail = 9
|
||||
End Enum
|
||||
''' <summary>
|
||||
''' 次要页面种类。其数值必须与 StackPanel 中的下标一致。
|
||||
@@ -1030,28 +1123,18 @@ Install:
|
||||
Case PageType.VersionSetup
|
||||
Return "版本设置 - " & If(PageVersionLeft.Version Is Nothing, "未知版本", PageVersionLeft.Version.Name)
|
||||
Case PageType.CompDetail
|
||||
If Stack.Additional Is Nothing Then
|
||||
Log("[Control] 资源工程详情页面未提供关键项", LogLevel.Feedback)
|
||||
Return "未知页面"
|
||||
Else
|
||||
Dim Project As CompProject = Stack.Additional
|
||||
Select Case Project.Type
|
||||
Case CompType.Mod
|
||||
Return "Mod 下载 - " & Project.TranslatedName
|
||||
Case CompType.ModPack
|
||||
Return "整合包下载 - " & Project.TranslatedName
|
||||
Case Else 'CompType.ResourcePack
|
||||
Return "资源包下载 - " & Project.TranslatedName
|
||||
End Select
|
||||
End If
|
||||
Dim Project As CompProject = Stack.Additional(0)
|
||||
Select Case Project.Type
|
||||
Case CompType.Mod
|
||||
Return "Mod 下载 - " & Project.TranslatedName
|
||||
Case CompType.ModPack
|
||||
Return "整合包下载 - " & Project.TranslatedName
|
||||
Case Else 'CompType.ResourcePack
|
||||
Return "资源包下载 - " & Project.TranslatedName
|
||||
End Select
|
||||
Case PageType.HelpDetail
|
||||
If Stack.Additional Is Nothing Then
|
||||
Log("[Control] 帮助详情页面未提供关键项", LogLevel.Msgbox)
|
||||
Return "未知页面"
|
||||
Else
|
||||
Dim Entry As HelpEntry = Stack.Additional(0)
|
||||
Return Entry.Title
|
||||
End If
|
||||
Dim Entry As HelpEntry = Stack.Additional(0)
|
||||
Return Entry.Title
|
||||
Case Else
|
||||
Return ""
|
||||
End Select
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Imports System.IO.Compression
|
||||
Imports System.Management
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports System.Security.Cryptography
|
||||
Imports System.Security.Principal
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports System.Windows.Markup
|
||||
Imports Newtonsoft.Json
|
||||
|
||||
@@ -11,12 +11,12 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.6.2" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.6.2." & VersionBranchCode '标准格式的四段式版本号
|
||||
Public Const VersionBaseName As String = "2.6.100" '不含分支前缀的显示用版本名
|
||||
Public Const VersionStandardCode As String = "2.6.100." & VersionBranchCode '标准格式的四段式版本号
|
||||
#If BETA Then
|
||||
Public Const VersionCode As Integer = 293 'Release
|
||||
Public Const VersionCode As Integer = 308 'Release
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 294 'Snapshot
|
||||
Public Const VersionCode As Integer = 307 'Snapshot
|
||||
#End If
|
||||
'自动生成的版本信息
|
||||
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
|
||||
Try
|
||||
Directory.CreateDirectory(DestDirectory)
|
||||
If CompressFilePath.EndsWith(".zip") OrElse CompressFilePath.EndsWith(".jar") OrElse CompressFilePath.EndsWith(".mrpack") 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
|
||||
If CompressFilePath.EndsWith(".gz") Then
|
||||
'以 gz 方式解压
|
||||
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)
|
||||
@@ -1271,7 +1255,19 @@ Re:
|
||||
stream.Close()
|
||||
Return True
|
||||
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
|
||||
Catch ex As Exception
|
||||
Log(ex, "尝试解压文件失败")
|
||||
@@ -1301,7 +1297,7 @@ Re:
|
||||
Next
|
||||
Temp = Directory.GetDirectories(Path)
|
||||
For Each str As String In Temp
|
||||
DeleteDirectory(str)
|
||||
DeleteDirectory(str, IgnoreIssue)
|
||||
Next
|
||||
Try
|
||||
Directory.Delete(Path, True)
|
||||
@@ -1578,19 +1574,19 @@ Re:
|
||||
''' 为字符串进行 XML 转义。
|
||||
''' </summary>
|
||||
Public Function EscapeXML(Str As String) As String
|
||||
Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """)
|
||||
Return Str.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("""", """).Replace(vbCrLf, "
")
|
||||
End Function
|
||||
|
||||
'正则
|
||||
''' <summary>
|
||||
''' 搜索字符串中的所有正则匹配项。
|
||||
''' </summary>
|
||||
Public Function RegexSearch(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As List(Of String)
|
||||
Public Function RegexSearch(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As List(Of String)
|
||||
Try
|
||||
RegexSearch = New List(Of String)
|
||||
Dim RegexSearchRes = New RegularExpressions.Regex(regex, options).Matches(str)
|
||||
Dim RegexSearchRes = New Regex(regex, options).Matches(str)
|
||||
If RegexSearchRes Is Nothing Then Return RegexSearch
|
||||
For Each item As RegularExpressions.Match In RegexSearchRes
|
||||
For Each item As Match In RegexSearchRes
|
||||
RegexSearch.Add(item.Value)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
@@ -1601,7 +1597,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 获取字符串中的第一个正则匹配项,若无匹配则返回 Nothing。
|
||||
''' </summary>
|
||||
Public Function RegexSeek(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Public Function RegexSeek(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Try
|
||||
Dim Result = RegularExpressions.Regex.Match(str, regex, options).Value
|
||||
Return If(Result = "", Nothing, Result)
|
||||
@@ -1613,7 +1609,7 @@ Re:
|
||||
''' <summary>
|
||||
''' 检查字符串是否匹配某正则模式。
|
||||
''' </summary>
|
||||
Public Function RegexCheck(str As String, regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As Boolean
|
||||
Public Function RegexCheck(str As String, regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As Boolean
|
||||
Try
|
||||
Return RegularExpressions.Regex.IsMatch(str, regex, options)
|
||||
Catch ex As Exception
|
||||
@@ -1624,7 +1620,13 @@ Re:
|
||||
''' <summary>
|
||||
''' 进行正则替换,会抛出错误。
|
||||
''' </summary>
|
||||
Public Function RegexReplace(Input As String, Replacement As String, Regex As String, Optional options As RegularExpressions.RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Public Function RegexReplace(Input As String, Replacement As String, Regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Return RegularExpressions.Regex.Replace(Input, Regex, Replacement, options)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 对每个正则匹配分别进行替换,会抛出错误。
|
||||
''' </summary>
|
||||
Public Function RegexReplaceEach(Input As String, Replacement As MatchEvaluator, Regex As String, Optional options As RegexOptions = RegularExpressions.RegexOptions.None) As String
|
||||
Return RegularExpressions.Regex.Replace(Input, Regex, Replacement, options)
|
||||
End Function
|
||||
|
||||
|
||||
@@ -437,10 +437,6 @@
|
||||
For Each Loader In Loaders
|
||||
Loader.Abort()
|
||||
Next
|
||||
'从列表删除
|
||||
SyncLock LoaderTaskbarLock
|
||||
If LoaderTaskbar.Contains(Me) Then LoaderTaskbar.Remove(Me)
|
||||
End SyncLock
|
||||
End Sub)
|
||||
End Sub
|
||||
|
||||
@@ -571,6 +567,7 @@ Restart:
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRemove(Loader)
|
||||
SyncLock LoaderTaskbarLock
|
||||
LoaderTaskbar.Add(Loader)
|
||||
Log($"[Taskbar] {Loader.Name} 已加入任务列表")
|
||||
End SyncLock
|
||||
End Sub
|
||||
Public Sub LoaderTaskbarProgressRefresh()
|
||||
@@ -585,16 +582,13 @@ Restart:
|
||||
If Loader.Show AndAlso Loader.State = LoadState.Loading Then IsAllDownloadTaskCompleted = False
|
||||
Next
|
||||
'若单个任务已中止或全部任务已完成,则刷新并移除
|
||||
Dim RemovedTask As New List(Of LoaderBase)
|
||||
For i = 0 To LoaderTaskbar.Count - 1
|
||||
If (IsAllDownloadTaskCompleted OrElse LoaderTaskbar(i).State = LoadState.Aborted) AndAlso LoaderTaskbar(i).Show Then
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(LoaderTaskbar(i))
|
||||
RemovedTask.Add(LoaderTaskbar(i))
|
||||
For Each Task In LoaderTaskbar.ToList()
|
||||
If (IsAllDownloadTaskCompleted OrElse Task.State = LoadState.Aborted OrElse Task.State = LoadState.Waiting) AndAlso Task.Show Then
|
||||
If FrmSpeedLeft IsNot Nothing Then FrmSpeedLeft.TaskRefresh(Task)
|
||||
LoaderTaskbar.Remove(Task)
|
||||
Log($"[Taskbar] {Task.Name} 已移出任务列表")
|
||||
End If
|
||||
Next
|
||||
For Each Task In RemovedTask
|
||||
LoaderTaskbar.Remove(Task)
|
||||
Next
|
||||
End SyncLock
|
||||
'更新平滑后的进度
|
||||
If NewProgress <= 0 OrElse NewProgress >= 1 OrElse LoaderTaskbarProgress > NewProgress Then
|
||||
@@ -625,8 +619,9 @@ Restart:
|
||||
Try
|
||||
Dim Total As Double = 0, Count As Integer = 0
|
||||
SyncLock LoaderTaskbarLock
|
||||
For i = 0 To LoaderTaskbar.Count - 1
|
||||
Total += LoaderTaskbar(i).Progress
|
||||
For Each Task In LoaderTaskbar.ToList
|
||||
If Not Task.Show Then Continue For
|
||||
Total += Task.Progress
|
||||
Count += 1 '避免多线程影响导致计数出错
|
||||
Next
|
||||
End SyncLock
|
||||
|
||||
@@ -106,15 +106,15 @@ Retry:
|
||||
Retry:
|
||||
Select Case RetryCount
|
||||
Case 0 '正常尝试
|
||||
Return NetGetCodeRequest(Url, Encode, 10000, IsJson, Accept)
|
||||
Return NetGetCodeByRequestOnce(Url, Encode, 10000, IsJson, Accept)
|
||||
Case 1 '慢速重试
|
||||
Thread.Sleep(500)
|
||||
Return NetGetCodeRequest(If(BackupUrl, Url), Encode, 30000, IsJson, Accept)
|
||||
Return NetGetCodeByRequestOnce(If(BackupUrl, Url), Encode, 30000, IsJson, Accept)
|
||||
Case Else '快速重试
|
||||
If GetTimeTick() - StartTime > 5500 Then
|
||||
'若前两次加载耗费 5 秒以上,才进行重试
|
||||
Thread.Sleep(500)
|
||||
Return NetGetCodeRequest(If(BackupUrl, Url), Encode, 4000, IsJson, Accept)
|
||||
Return NetGetCodeByRequestOnce(If(BackupUrl, Url), Encode, 4000, IsJson, Accept)
|
||||
Else
|
||||
Throw RetryException
|
||||
End If
|
||||
@@ -148,7 +148,7 @@ Retry:
|
||||
For i = 1 To 4
|
||||
Dim th As New Thread(Sub()
|
||||
Try
|
||||
RequestResult = NetGetCodeRequest(Url, Encode, 30000, IsJson, Accept)
|
||||
RequestResult = NetGetCodeByRequestOnce(Url, Encode, 30000, IsJson, Accept)
|
||||
Catch ex As Exception
|
||||
FailCount += 1
|
||||
RequestEx = ex
|
||||
@@ -182,10 +182,10 @@ RequestFinished:
|
||||
Loop
|
||||
Throw New Exception("未知错误")
|
||||
End Function
|
||||
Private Function NetGetCodeRequest(Url As String, Encode As Encoding, Timeout As Integer, IsJson As Boolean, Accept As String)
|
||||
Public Function NetGetCodeByRequestOnce(Url As String, Optional Encode As Encoding = Nothing, Optional Timeout As Integer = 30000, Optional IsJson As Boolean = False, Optional Accept As String = "")
|
||||
If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
|
||||
Url = SecretCdnSign(Url)
|
||||
Log("[Net] 获取网络结果:" & Url & ",最大超时 " & Timeout)
|
||||
Log($"[Net] 获取网络结果:{Url},超时 {Timeout}ms{If(IsJson, ",要求 json", "")}")
|
||||
Dim Request As HttpWebRequest = WebRequest.Create(Url)
|
||||
Dim Result As New List(Of Byte)
|
||||
Try
|
||||
@@ -269,6 +269,8 @@ RequestFinished:
|
||||
'下载
|
||||
Using Client As New WebClient
|
||||
Try
|
||||
Client.Headers(HttpRequestHeader.UserAgent) = "PCL2/" & VersionStandardCode & " Mozilla/5.0 AppleWebKit/537.36 Chrome/63.0.3239.132 Safari/537.36"
|
||||
Client.Headers(HttpRequestHeader.Referer) = "http://" & VersionCode & ".pcl2.server/"
|
||||
Client.DownloadFile(Url, LocalFile)
|
||||
Catch ex As Exception
|
||||
Throw New WebException("直接下载文件失败(" & Url & ")。", ex)
|
||||
@@ -328,7 +330,7 @@ Retry:
|
||||
''' <summary>
|
||||
''' 同时发送多个网络请求并要求返回内容。
|
||||
''' </summary>
|
||||
Public Function NetRequestMuity(Url As String, Method As String, Data As Object, ContentType As String, Optional RequestCount As Integer = 4, Optional Headers As Dictionary(Of String, String) = Nothing)
|
||||
Public Function NetRequestMulty(Url As String, Method As String, Data As Object, ContentType As String, Optional RequestCount As Integer = 4, Optional Headers As Dictionary(Of String, String) = Nothing)
|
||||
Dim Threads As New List(Of Thread)
|
||||
Dim RequestResult = Nothing
|
||||
Dim RequestEx As Exception = Nothing
|
||||
@@ -1276,14 +1278,6 @@ Wrong:
|
||||
Try
|
||||
Retry:
|
||||
SyncLock LockChain
|
||||
''大小检测
|
||||
'If DownloadDone <> FileSize AndAlso Not IsUnknownSize Then
|
||||
' For Each Th As NetThread In Threads
|
||||
' Log("[Download] " & "File size detail: ")
|
||||
' Log("[Download] " & Th.Uuid & "#, State " & GetStringFromEnum(CType(Th.State, [Enum])) & ", Range " & Th.DownloadStart & "~" & (Th.DownloadStart + Th.DownloadDone) & ", Left " & Th.DownloadUndone)
|
||||
' Next
|
||||
' Throw New Exception("文件大小应为 " & FileSize & " B,实际下载为 " & DownloadDone & " B。")
|
||||
'End If
|
||||
If ModeDebug Then Log("[Download] " & LocalName & ":正在合并文件")
|
||||
'创建文件夹
|
||||
If File.Exists(LocalPath) Then File.Delete(LocalPath)
|
||||
@@ -1297,7 +1291,7 @@ Retry:
|
||||
AddWriter.Write(SmailFileCache.ToArray)
|
||||
AddWriter.Dispose() : AddWriter = Nothing
|
||||
MergeFile.Dispose() : MergeFile = Nothing
|
||||
ElseIf Threads.DownloadDone = DownloadDone Then
|
||||
ElseIf Threads.DownloadDone = DownloadDone AndAlso Threads.Temp IsNot Nothing Then
|
||||
'仅有一个文件,直接复制
|
||||
CopyFile(Threads.Temp, LocalPath)
|
||||
Else
|
||||
@@ -1305,7 +1299,7 @@ Retry:
|
||||
MergeFile = New FileStream(LocalPath, FileMode.Create)
|
||||
AddWriter = New BinaryWriter(MergeFile)
|
||||
For Each Thread As NetThread In Threads
|
||||
If Thread.DownloadDone = 0 Then Continue For
|
||||
If Thread.DownloadDone = 0 OrElse Thread.Temp Is Nothing Then Continue For
|
||||
Using fs As New FileStream(Thread.Temp, FileMode.Open, FileAccess.Read, FileShare.Read)
|
||||
Dim TempReader As New BinaryReader(fs)
|
||||
AddWriter.Write(TempReader.ReadBytes(fs.Length))
|
||||
@@ -1326,9 +1320,9 @@ Retry:
|
||||
'检查文件
|
||||
Dim CheckResult As String = Check?.Check(LocalPath)
|
||||
If CheckResult IsNot Nothing Then
|
||||
Log("[Download] " & "File size detail of " & Uuid & "# :")
|
||||
Log("[Download] " & Uuid & "# 的下载线程细节:")
|
||||
For Each Th As NetThread In Threads
|
||||
Log("[Download] " & Th.Uuid & "#, State " & GetStringFromEnum(CType(Th.State, [Enum])) & ", Range " & Th.DownloadStart & "~" & (Th.DownloadStart + Th.DownloadDone) & ", Left " & Th.DownloadUndone)
|
||||
Log("[Download] " & Th.Uuid & "#,状态 " & GetStringFromEnum(CType(Th.State, [Enum])) & ",字节范围 " & Th.DownloadStart & "~" & (Th.DownloadStart + Th.DownloadDone) & ",字节剩余 " & Th.DownloadUndone)
|
||||
Next
|
||||
Throw New Exception(CheckResult)
|
||||
End If
|
||||
|
||||
@@ -28,7 +28,7 @@ Public Class MyBitmap
|
||||
Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource
|
||||
Dim Bitmap = Image.Pic
|
||||
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
|
||||
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)
|
||||
@@ -73,10 +73,10 @@ Public Class MyBitmap
|
||||
Pic = My.Application.TryFindResource(FilePathOrResourceName)
|
||||
If Pic Is Nothing Then
|
||||
Pic = New System.Drawing.Bitmap(1, 1)
|
||||
Log(ex, "加载位图失败(" & FilePathOrResourceName & ")")
|
||||
Log(ex, $"加载位图失败({FilePathOrResourceName})")
|
||||
Throw
|
||||
Else
|
||||
Log(ex, "指定类型有误的位图加载(" & FilePathOrResourceName & ")", LogLevel.Developer)
|
||||
Log(ex, $"指定类型有误的位图加载({FilePathOrResourceName})", LogLevel.Developer)
|
||||
Exit Try
|
||||
End If
|
||||
End Try
|
||||
@@ -104,45 +104,27 @@ Public Class MyBitmap
|
||||
End Using
|
||||
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>
|
||||
Public Function Clip(rect As System.Drawing.Rectangle) As System.Drawing.Bitmap
|
||||
Dim img As System.Drawing.Image = Pic
|
||||
Dim bmp As New System.Drawing.Bitmap(rect.Width, rect.Height, img.PixelFormat)
|
||||
bmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)
|
||||
Public Function Clip(X As Integer, Y As Integer, Width As Integer, Height As Integer) As MyBitmap
|
||||
Dim bmp As New System.Drawing.Bitmap(Width, Height, Pic.PixelFormat)
|
||||
bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
|
||||
Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
|
||||
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
|
||||
Return bmp
|
||||
End Function
|
||||
|
||||
''' <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)
|
||||
bmp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX)
|
||||
bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
|
||||
bmp.RotateFlip(Type)
|
||||
Return bmp
|
||||
End Function
|
||||
|
||||
@@ -152,7 +134,7 @@ Public Class MyBitmap
|
||||
Public Sub Save(FilePath As String)
|
||||
Dim encoder As BitmapEncoder = New PngBitmapEncoder()
|
||||
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)
|
||||
End Using
|
||||
End Sub
|
||||
|
||||
@@ -375,6 +375,7 @@
|
||||
End Select
|
||||
Next
|
||||
If Tags.Count = 0 Then Tags.Add("杂项")
|
||||
Tags.Sort()
|
||||
ModLoaders.Sort()
|
||||
#End Region
|
||||
End If
|
||||
@@ -384,7 +385,7 @@
|
||||
''' <summary>
|
||||
''' 将当前工程信息实例化为控件。
|
||||
''' </summary>
|
||||
Public Function ToCompItem(ShowMcVersionDesc As Boolean, ShowLoaderDesc As Boolean, Optional OnClick As MyCompItem.ClickEventHandler = Nothing) As MyCompItem
|
||||
Public Function ToCompItem(ShowMcVersionDesc As Boolean, ShowLoaderDesc As Boolean) As MyCompItem
|
||||
'获取版本描述
|
||||
Dim GameVersionDescription As String
|
||||
If GameVersions Is Nothing OrElse GameVersions.Count = 0 Then
|
||||
@@ -420,19 +421,36 @@
|
||||
Dim NewItem As New MyCompItem With {.Tag = Me}
|
||||
'整理 FinalName 与 ExNameList
|
||||
'检查下列代码时可以参考 #1567 的测试例
|
||||
Dim FinalName As String
|
||||
Dim FinalName As String = RawName
|
||||
Dim ExNameList As List(Of String)
|
||||
If TranslatedName = RawName Then
|
||||
'没有中文翻译
|
||||
FinalName = TranslatedName.Split(" | ").First.Split(" - ").First.Split("(").First.Split("[").First
|
||||
ExNameList = TranslatedName.Split({" | ", " - ", "(", ")", "[", "]", "{", "}"}, StringSplitOptions.RemoveEmptyEntries).
|
||||
Select(Function(s) s.Trim(" /".ToCharArray)).Where(Function(w) Not String.IsNullOrEmpty(w)).ToList
|
||||
ExNameList.RemoveAt(0)
|
||||
If Not ExNameList.Any(Function(s) s.ToLower.Contains("forge") OrElse s.ToLower.Contains("fabric") OrElse s.ToLower.Contains("quilt")) AndAlso '不是标注 XX 版
|
||||
Not (ExNameList.Count = 1 AndAlso ExNameList.First.ToUpper = ExNameList.First) Then '不是缩写
|
||||
FinalName = RawName '使用原名
|
||||
ExNameList.Clear()
|
||||
End If
|
||||
'将所有名称分段
|
||||
Dim NameLists = TranslatedName.Split({" | ", " - ", "(", ")", "[", "]", "{", "}"}, StringSplitOptions.RemoveEmptyEntries).
|
||||
Select(Function(s) s.Trim(" /\".ToCharArray)).Where(Function(w) Not String.IsNullOrEmpty(w)).ToList
|
||||
If NameLists.Count = 1 Then GoTo NoExName
|
||||
'查找其中的缩写、Forge/Fabric 等版本标记
|
||||
ExNameList = New List(Of String)
|
||||
Dim NormalNameList = New List(Of String)
|
||||
For Each Name In NameLists
|
||||
Dim LowerName As String = Name.ToLower
|
||||
If Name.ToUpper = Name AndAlso Name <> "FPS" AndAlso Name <> "HUD" Then
|
||||
'缩写
|
||||
ExNameList.Add(Name)
|
||||
ElseIf (LowerName.Contains("forge") OrElse LowerName.Contains("fabric") OrElse LowerName.Contains("quilt")) AndAlso
|
||||
Not RegexCheck(LowerName.Replace("forge", "").Replace("fabric", "").Replace("quilt", ""), "[a-z]+") Then '去掉关键词后没有其他字母
|
||||
'Forge/Fabric 等版本标记
|
||||
ExNameList.Add(Name)
|
||||
Else
|
||||
'其他部分
|
||||
NormalNameList.Add(Name)
|
||||
End If
|
||||
Next
|
||||
'根据分类后的结果处理
|
||||
If NormalNameList.Count = 0 OrElse ExNameList.Count = 0 Then GoTo NoExName
|
||||
'同时包含 NormalName 和 ExName
|
||||
FinalName = NormalNameList.Join(" - ")
|
||||
ExNameList = ExNameList
|
||||
Else
|
||||
'有中文翻译
|
||||
'尝试将文本分为三段:FinalName (EnglishName) - Suffix
|
||||
@@ -452,24 +470,27 @@
|
||||
ExNameList = New List(Of String) From {EnglishName} '使用原名
|
||||
End If
|
||||
'添加后缀
|
||||
If Suffix <> "" Then
|
||||
ExNameList.Add(Suffix)
|
||||
ExNameList = ExNameList.Distinct().ToList()
|
||||
End If
|
||||
If Suffix <> "" Then ExNameList.Add(Suffix)
|
||||
End If
|
||||
ExNameList = ExNameList.Distinct().ToList()
|
||||
'显示 FinalName 与 ExNameList
|
||||
NewItem.LabTitle.Text = FinalName
|
||||
If ExNameList.Count = 0 Then
|
||||
NoExName:
|
||||
NewItem.LabTitle.Text = FinalName
|
||||
CType(NewItem.LabTitleRaw.Parent, StackPanel).Children.Remove(NewItem.LabTitleRaw)
|
||||
Else
|
||||
NewItem.LabTitle.Text = FinalName
|
||||
Dim ExName As String = ""
|
||||
For Each Ex In ExNameList
|
||||
'去除 “Forge/Fabric” 这一无意义提示
|
||||
If Ex.Length < 19 AndAlso Ex.ToLower.Contains("fabric") AndAlso Ex.ToLower.Contains("forge") Then Continue For
|
||||
If Ex.Length < 16 AndAlso Ex.ToLower.Contains("fabric") AndAlso Ex.ToLower.Contains("forge") Then Continue For
|
||||
'将 “Forge” 等提示改为 “Forge 版”
|
||||
If (Ex.ToLower.Contains("forge") OrElse Ex.ToLower.Contains("fabric") OrElse Ex.ToLower.Contains("quilt")) AndAlso
|
||||
Not Ex.Contains("版") AndAlso Ex.Length < 19 Then Ex = Ex.Replace("Edition", "").Trim.Capitalize & " 版"
|
||||
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 = Ex.Replace("Edition", "").Replace("edition", "").Trim.Capitalize & " 版"
|
||||
End If
|
||||
'将 “forge” 等词语的首字母大写
|
||||
Ex = Ex.Replace("forge", "Forge").Replace("neo", "Neo").Replace("fabric", "Fabric").Replace("quilt", "Quilt")
|
||||
ExName &= " | " & Ex.Trim
|
||||
Next
|
||||
If ExName = "" Then GoTo NoExName
|
||||
@@ -546,12 +567,6 @@ NoExName:
|
||||
Else
|
||||
NewItem.Logo = LogoUrl
|
||||
End If
|
||||
If OnClick IsNot Nothing Then
|
||||
AddHandler NewItem.Click, OnClick
|
||||
NewItem.HasAnimation = True
|
||||
Else
|
||||
NewItem.HasAnimation = False
|
||||
End If
|
||||
Return NewItem
|
||||
End Function
|
||||
|
||||
@@ -914,7 +929,10 @@ Retry:
|
||||
Next
|
||||
'更新结果
|
||||
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
|
||||
Storage.ModrinthOffset += ProjectList.Count
|
||||
Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer)
|
||||
@@ -969,7 +987,7 @@ Retry:
|
||||
Not Storage.Results.Any(Function(b) r.IsLike(b))).ToList
|
||||
'加入列表
|
||||
RealResults.AddRange(RawResults)
|
||||
Log($"[Comp] 去重后累计新增结果 {RealResults.Count} 个")
|
||||
Log($"[Comp] 去重、筛选后累计新增结果 {RealResults.Count} 个")
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -1353,7 +1371,7 @@ Retry:
|
||||
''' <summary>
|
||||
''' 预载包含大量 CompFile 的卡片,添加必要的元素和前置 Mod 列表。
|
||||
''' </summary>
|
||||
Public Sub CompFilesCardPreload(Stack As StackPanel, Files As List(Of CompFile), OnClick As MyListItem.ClickEventHandler)
|
||||
Public Sub CompFilesCardPreload(Stack As StackPanel, Files As List(Of CompFile))
|
||||
'获取卡片对应的前置 ID
|
||||
'如果为整合包就不会有 Dependencies 信息,所以不用管
|
||||
Dim Deps As List(Of String) = Files.SelectMany(Function(f) f.Dependencies).Distinct.ToList()
|
||||
@@ -1367,7 +1385,7 @@ Retry:
|
||||
Stack.Children.Add(New TextBlock With {.Text = "前置 Mod", .FontSize = 14, .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 2, 0, 5)})
|
||||
'添加前置 Mod 列表
|
||||
For Each Dep In Deps
|
||||
Dim Item = CompProjectCache(Dep).ToCompItem(False, False, AddressOf FrmDownloadMod.ProjectClick)
|
||||
Dim Item = CompProjectCache(Dep).ToCompItem(False, False)
|
||||
Stack.Children.Add(Item)
|
||||
Next
|
||||
'添加结尾间隔
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
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
|
||||
If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For
|
||||
Dim Ext As String = TargetFile.Extension.ToLower
|
||||
@@ -137,6 +137,8 @@
|
||||
Else
|
||||
AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName, Encoding.UTF8).Split(vbCrLf.ToCharArray)))
|
||||
End If
|
||||
Else
|
||||
File.Delete(TargetFile.FullName)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "导入单个日志文件失败")
|
||||
@@ -165,37 +167,37 @@
|
||||
DirectFile = Nothing
|
||||
Dim TotalFiles As New List(Of KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String())))
|
||||
For Each LogFile In AnalyzeRawFiles
|
||||
Dim FileName As String = GetFileNameFromPath(LogFile.Key)
|
||||
Dim MatchName As String = GetFileNameFromPath(LogFile.Key).ToLower
|
||||
Dim TargetType As AnalyzeFileType
|
||||
If FileName.StartsWith("hs_err") Then
|
||||
If MatchName.StartsWith("hs_err") Then
|
||||
TargetType = AnalyzeFileType.HsErr
|
||||
DirectFile = LogFile
|
||||
ElseIf FileName.StartsWith("crash-") Then
|
||||
ElseIf MatchName.StartsWith("crash-") Then
|
||||
TargetType = AnalyzeFileType.CrashReport
|
||||
DirectFile = LogFile
|
||||
ElseIf FileName = "latest.log" OrElse FileName = "latest log.txt" OrElse
|
||||
FileName = "debug.log" OrElse FileName = "debug log.txt" OrElse
|
||||
FileName = "游戏崩溃前的输出.txt" OrElse FileName = "rawoutput.log" Then
|
||||
ElseIf MatchName = "latest.log" OrElse MatchName = "latest log.txt" OrElse
|
||||
MatchName = "debug.log" OrElse MatchName = "debug log.txt" OrElse
|
||||
MatchName = "游戏崩溃前的输出.txt" OrElse MatchName = "rawoutput.log" Then
|
||||
TargetType = AnalyzeFileType.MinecraftLog
|
||||
If DirectFile Is Nothing Then DirectFile = LogFile
|
||||
ElseIf FileName = "启动器日志.txt" OrElse FileName = "PCL2 启动器日志.txt" OrElse FileName = "PCL 启动器日志.txt" OrElse FileName = "log1.txt" Then
|
||||
ElseIf MatchName = "启动器日志.txt" OrElse MatchName = "PCL2 启动器日志.txt" OrElse MatchName = "PCL 启动器日志.txt" OrElse MatchName = "log1.txt" Then
|
||||
If LogFile.Value.Any(Function(s) s.Contains("以下为游戏输出的最后一段内容")) Then
|
||||
TargetType = AnalyzeFileType.MinecraftLog
|
||||
If DirectFile Is Nothing Then DirectFile = LogFile
|
||||
Else
|
||||
TargetType = AnalyzeFileType.ExtraLog
|
||||
End If
|
||||
ElseIf FileName.EndsWith(".log") OrElse FileName.EndsWith(".txt") Then
|
||||
ElseIf MatchName.EndsWith(".log") OrElse MatchName.EndsWith(".txt") Then
|
||||
TargetType = AnalyzeFileType.ExtraLog
|
||||
Else
|
||||
Log("[Crash] " & FileName & " 分类为 Ignore")
|
||||
Log("[Crash] " & MatchName & " 分类为 Ignore")
|
||||
Continue For
|
||||
End If
|
||||
If LogFile.Value.Count = 0 Then
|
||||
Log("[Crash] " & FileName & " 由于内容为空跳过")
|
||||
Log("[Crash] " & MatchName & " 由于内容为空跳过")
|
||||
Else
|
||||
TotalFiles.Add(New KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String()))(TargetType, LogFile))
|
||||
Log("[Crash] " & FileName & " 分类为 " & GetStringFromEnum(TargetType))
|
||||
Log("[Crash] " & MatchName & " 分类为 " & GetStringFromEnum(TargetType))
|
||||
End If
|
||||
Next
|
||||
|
||||
@@ -235,6 +237,7 @@
|
||||
End If
|
||||
Case AnalyzeFileType.MinecraftLog
|
||||
LogMc = ""
|
||||
LogMcDebug = ""
|
||||
'创建文件名词典
|
||||
Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String()))
|
||||
For Each SelectedFile In SelectedFiles
|
||||
@@ -272,11 +275,21 @@
|
||||
Exit For
|
||||
End If
|
||||
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
|
||||
LogMc = Nothing
|
||||
Throw New Exception("无法找到匹配的 Minecraft Log")
|
||||
End If
|
||||
If LogMcDebug = "" Then LogMcDebug = Nothing
|
||||
Case AnalyzeFileType.ExtraLog
|
||||
'全部丢过去
|
||||
For Each SelectedFile In SelectedFiles
|
||||
@@ -294,7 +307,7 @@
|
||||
If ResultCount = 0 Then
|
||||
Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志")
|
||||
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
|
||||
Return ResultCount
|
||||
|
||||
@@ -315,7 +328,7 @@
|
||||
End Function
|
||||
|
||||
'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 CrashReasons As New Dictionary(Of CrashReason, List(Of String))
|
||||
@@ -324,6 +337,7 @@
|
||||
''' </summary>
|
||||
Private Enum CrashReason
|
||||
Mod文件被解压
|
||||
MixinBootstrap缺失
|
||||
内存不足
|
||||
使用JDK
|
||||
显卡不支持OpenGL
|
||||
@@ -344,9 +358,8 @@
|
||||
ModMixin失败
|
||||
Mod加载器报错
|
||||
Mod初始化失败
|
||||
崩溃日志堆栈分析发现关键字
|
||||
崩溃日志堆栈分析发现Mod名称
|
||||
MC日志堆栈分析发现关键字
|
||||
堆栈分析发现关键字
|
||||
堆栈分析发现Mod名称
|
||||
OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
|
||||
特定方块导致崩溃
|
||||
特定实体导致崩溃
|
||||
@@ -364,56 +377,63 @@
|
||||
NightConfig的Bug
|
||||
ShadersMod与Optifine同时安装
|
||||
Forge安装不完整
|
||||
Mod需要Java11
|
||||
Mod缺少前置或MC版本错误
|
||||
End Enum
|
||||
''' <summary>
|
||||
''' 根据 AnalyzeLogs 与可能的版本信息分析崩溃原因。
|
||||
''' </summary>
|
||||
Public Sub Analyze(Optional Version As McVersion = Nothing)
|
||||
Log("[Crash] 步骤 3:分析崩溃原因")
|
||||
LogAll = (If(LogMc, "") & If(LogHs, "") & If(LogCrash, "")).ToLower
|
||||
LogAll = (If(LogMc, "") & If(LogMcDebug, "") & If(LogHs, "") & If(LogCrash, ""))
|
||||
|
||||
'1. 精准日志匹配 1
|
||||
'1. 精准日志匹配,中/高优先级
|
||||
AnalyzeCrit1()
|
||||
If CrashReasons.Count > 0 Then GoTo Done
|
||||
AnalyzeCrit2()
|
||||
If CrashReasons.Count > 0 Then GoTo Done
|
||||
|
||||
'2. 堆栈分析
|
||||
If LogAll.Contains("forge") OrElse LogAll.Contains("fabric") OrElse LogAll.Contains("liteloader") Then
|
||||
'来源崩溃日志的分析
|
||||
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
|
||||
Log("[Crash] 开始进行崩溃日志堆栈分析")
|
||||
Dim StackLogs As String = LogCrash.Split("System Details").First
|
||||
Dim Keywords = 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
|
||||
Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
|
||||
End If
|
||||
'来自 Minecraft 日志的分析
|
||||
'Minecraft 日志
|
||||
If LogMc IsNot Nothing Then
|
||||
Dim Fatals = RegexSearch(LogMc, "/FATAL] [\w\W]+?(?=[\n]+\[)")
|
||||
Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项")
|
||||
If Fatals.Count > 0 Then
|
||||
Dim Keywords As New List(Of String)
|
||||
For Each Fatal In Fatals
|
||||
Keywords.AddRange(AnalyzeStackKeyword(Fatal))
|
||||
Next
|
||||
If Keywords.Count > 0 Then
|
||||
AppendReason(CrashReason.MC日志堆栈分析发现关键字, Keywords.Distinct.ToList)
|
||||
GoTo Done
|
||||
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
|
||||
Log("[Crash] 可能并未安装 Mod,不进行堆栈分析")
|
||||
End If
|
||||
|
||||
'3. 精准日志匹配 2
|
||||
AnalyzeCrit2()
|
||||
'3. 精准日志匹配,低优先级
|
||||
AnalyzeCrit3()
|
||||
|
||||
'输出到日志
|
||||
Done:
|
||||
@@ -468,11 +488,12 @@ Done:
|
||||
If LogMc.Contains("java.lang.ClassCastException: java.base/jdk") Then AppendReason(CrashReason.使用JDK)
|
||||
If LogMc.Contains("java.lang.ClassCastException: class jdk.") Then AppendReason(CrashReason.使用JDK)
|
||||
If LogMc.Contains("TRANSFORMER/net.optifine/net.optifine.reflect.Reflector.<clinit>(Reflector.java") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.<init>(net.minecraft.resources.ResourceLocation, ") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.<init>(int[], int, net.minecraft.core.Direction, net.minecraft.client.renderer.texture.TextureAtlasSprite, boolean, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager.addRegionTicket(net.minecraft.server.level.TicketType, net.minecraft.world.level.ChunkPos, int, java.lang.Object, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager.removeRegionTicket(net.minecraft.server.level.TicketType, net.minecraft.world.level.ChunkPos, int, java.lang.Object, boolean)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'net.minecraft.network.chat.FormattedText net.minecraft.client.gui.Font.ellipsize(net.minecraft.network.chat.FormattedText, int)'") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'java.lang.String com.mojang.blaze3d.systems.RenderSystem.getBackendDescription") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraftforge.client.gui.overlay.ForgeGui.renderSelectedItemName") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("java.lang.NoSuchMethodError: 'net.minecraft.network.chat.FormattedText net.minecraft.client.gui.Font.ellipsize") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
|
||||
If LogMc.Contains("Open J9 is not supported") OrElse LogMc.Contains("OpenJ9 is incompatible") OrElse LogMc.Contains(".J9VMInternals.") Then AppendReason(CrashReason.使用OpenJ9)
|
||||
If LogMc.Contains("java.lang.NoSuchFieldException: ucp") Then AppendReason(CrashReason.Java版本过高)
|
||||
If LogMc.Contains("because module java.base does not export") Then AppendReason(CrashReason.Java版本过高)
|
||||
@@ -480,6 +501,7 @@ Done:
|
||||
If LogMc.Contains("java.lang.ClassNotFoundException: java.lang.invoke.LambdaMetafactory") Then AppendReason(CrashReason.Java版本过高)
|
||||
If LogMc.Contains("The directories below appear to be extracted jar files. Fix this before you continue.") Then AppendReason(CrashReason.Mod文件被解压)
|
||||
If LogMc.Contains("Extracted mod jars found, loading will NOT continue") Then AppendReason(CrashReason.Mod文件被解压)
|
||||
If LogMc.Contains("java.lang.ClassNotFoundException: org.spongepowered.asm.launch.MixinTweaker") Then AppendReason(CrashReason.MixinBootstrap缺失)
|
||||
If LogMc.Contains("Couldn't set pixel format") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式)
|
||||
If LogMc.Contains("java.lang.OutOfMemoryError") Then AppendReason(CrashReason.内存不足)
|
||||
If LogMc.Contains("java.lang.RuntimeException: Shaders Mod detected. Please remove it, OptiFine has built-in support for shaders.") Then AppendReason(CrashReason.ShadersMod与Optifine同时安装)
|
||||
@@ -492,7 +514,11 @@ Done:
|
||||
If LogMc.Contains("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") Then AppendReason(CrashReason.NightConfig的Bug)
|
||||
If LogMc.Contains("Cannot find launch target fmlclient, unable to launch") Then AppendReason(CrashReason.Forge安装不完整)
|
||||
If LogMc.Contains("Invalid module name: '' is not a Java identifier") Then AppendReason(CrashReason.Mod名称包含特殊字符)
|
||||
If LogMc.Contains("java.lang.UnsupportedClassVersionError: net/fabricmc/loader/impl/launch/knot/KnotClient : Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("java.lang.RuntimeException: java.lang.NoSuchMethodException: no such method: sun.misc.Unsafe.defineAnonymousClass(Class,byte[],Object[])Class/invokeVirtual") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("java.lang.IllegalArgumentException: The requested compatibility level JAVA_11 could not be set. Level is not supported by the active JRE or ASM version") Then AppendReason(CrashReason.Mod需要Java11)
|
||||
If LogMc.Contains("Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
|
||||
If LogMc.Contains("Invalid maximum heap size") Then AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
|
||||
If LogMc.Contains("Could not reserve enough space") Then
|
||||
If LogMc.Contains("for 1048576KB object heap") Then
|
||||
AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
|
||||
@@ -500,32 +526,17 @@ Done:
|
||||
AppendReason(CrashReason.内存不足)
|
||||
End If
|
||||
End If
|
||||
'确定的 Mod 导致崩溃
|
||||
If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
'Mod 重复安装
|
||||
If LogMc.Contains("DuplicateModsFoundException") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=\n\t[\w]+ : [A-Z]{1}:[^\n]+(/|\\))[^/\\\n]+?.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
If LogMc.Contains("Found a duplicate mod") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "Found a duplicate mod[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
|
||||
'Mod 导致的崩溃
|
||||
If LogMc.Contains("Mixin prepare failed ") OrElse LogMc.Contains("Mixin apply failed ") 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, "(?<= in callback )[^./ ]+(?=.mixins.json:)")
|
||||
If ModId Is Nothing Then ModId = RegexSeek(LogMc, "(?<=from mod )[^\] ]+(?=\] from phase)")
|
||||
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("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray)))
|
||||
'仅当没有其他分析结果时显示
|
||||
If CrashReasons.Count = 0 Then
|
||||
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))
|
||||
'Mod 缺少前置
|
||||
If LogMc.Contains("Missing or unsupported mandatory dependencies:") Then
|
||||
AppendReason(CrashReason.Mod缺少前置或MC版本错误,
|
||||
RegexSearch(LogMc, "(?<=Missing or unsupported mandatory dependencies:)([\n\r]+\t(.*))+", RegularExpressions.RegexOptions.IgnoreCase).
|
||||
Select(Function(s) s.Trim((vbCrLf & vbTab & " ").ToCharArray)).Distinct().ToList())
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -563,14 +574,45 @@ Done:
|
||||
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。
|
||||
''' 进行精准日志匹配。匹配优先级高于堆栈分析的崩溃,但低于上面的。
|
||||
''' 如果第一步已经找到了原因则不执行该检测。
|
||||
''' </summary>
|
||||
Private Sub AnalyzeCrit2()
|
||||
|
||||
'游戏日志分析
|
||||
If LogMc IsNot Nothing Then
|
||||
If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso
|
||||
LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then
|
||||
'常规信息
|
||||
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))
|
||||
End If
|
||||
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。
|
||||
''' </summary>
|
||||
Private Sub AnalyzeCrit3()
|
||||
|
||||
'游戏日志分析
|
||||
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
|
||||
AppendReason(CrashReason.极短的程序输出, LogMc)
|
||||
End If
|
||||
'Mixin 失败可以导致大量 Mod 实例创建失败
|
||||
@@ -622,11 +664,12 @@ NextStack:
|
||||
Dim Word As String = Splited(i)
|
||||
If Word.Length <= 2 OrElse Word.StartsWith("func_") Then Continue For
|
||||
If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", "top",
|
||||
"core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base",
|
||||
"core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base", "oshi",
|
||||
"forge", "setup", "block", "model", "mixin", "event", "unimi", "netty",
|
||||
"common", "server", "config", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe",
|
||||
"preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content",
|
||||
"fastutil", "optifine", "scheduler", "minecraft", "transformers", "universal", "internal", "multipart", "minecraftforge", "override", "blockentity"
|
||||
"preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules",
|
||||
"fastutil", "optifine", "internal", "platform", "override",
|
||||
"listeners", "scheduler", "minecraft", "transformers", "universal", "multipart", "minecraftforge", "blockentity"
|
||||
}.Contains(Word.ToLower) Then Continue For
|
||||
PossibleWords.Add(Word.Trim)
|
||||
Next
|
||||
@@ -643,7 +686,7 @@ NextStack:
|
||||
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 根据 Mod 关键词与详细信息(崩溃报告第二部分)与,获取实际的 Mod 名称。
|
||||
''' 根据 Mod 关键词尝试获取实际的 Mod 名称。
|
||||
''' 若失败则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String)
|
||||
@@ -658,60 +701,95 @@ NextStack:
|
||||
Next
|
||||
Keywords = RealKeywords
|
||||
|
||||
'获取崩溃报告对应部分
|
||||
If LogCrash Is Nothing Then Return Nothing
|
||||
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 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
|
||||
'从崩溃报告获取 Mod 信息
|
||||
If LogCrash IsNot Nothing AndAlso LogCrash.Contains("A detailed walkthrough of the error") Then
|
||||
Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨")
|
||||
Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
|
||||
If IsFabricDetail Then
|
||||
Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)")
|
||||
Else
|
||||
Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
|
||||
Details = Details.Replace("Fabric Mods", "¨")
|
||||
Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式")
|
||||
End If
|
||||
If Name IsNot Nothing Then ModFileNames.Add(Name)
|
||||
Next
|
||||
ModFileNames = ModFileNames.Distinct.ToList
|
||||
Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
|
||||
For Each ModFileName As String In ModFileNames
|
||||
Log("[Crash] - " & ModFileName)
|
||||
Next
|
||||
Details = Details.Split("¨").Last
|
||||
|
||||
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
|
||||
''' <summary>
|
||||
''' 尝试获取 Mod 名称,若失败则返回原关键字。
|
||||
@@ -791,9 +869,7 @@ NextStack:
|
||||
If IsHandAnalyze Then
|
||||
Return "很抱歉,PCL 无法确定错误原因。"
|
||||
Else
|
||||
Return "很抱歉,你的游戏出现了一些问题……" & vbCrLf &
|
||||
"如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" & vbCrLf &
|
||||
"你也可以查看错误报告,其中可能会有出错的原因。"
|
||||
Return $"很抱歉,你的游戏出现了一些问题……{vbCrLf}如果要寻求帮助,请导出错误报告并发给他人,而不是发送这个窗口的截图。"
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -805,7 +881,7 @@ NextStack:
|
||||
Case CrashReason.Mod文件被解压
|
||||
Results.Add("由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。")
|
||||
Case CrashReason.内存不足
|
||||
Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是由于你为游戏分配的内存不足,或是游戏的配置要求过高。\n\n你可以在启动设置中增加为游戏分配的内存,删除配置要求较高的材质、Mod、光影。\n如果这依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h")
|
||||
Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是因为电脑内存不足、游戏分配的内存不足,或是配置要求过高。\n\n你可以尝试在 更多 → 百宝箱 中选择 内存优化,然后再启动游戏。\n如果还是不行,请在启动设置中增加为游戏分配的内存,并删除配置要求较高的材质、Mod、光影。\n如果依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h")
|
||||
Case CrashReason.使用OpenJ9
|
||||
Results.Add("游戏因为使用 Open J9 而崩溃了。\n请在启动设置的 Java 选择一项中改用非 OpenJ9 的 Java,然后再启动游戏。")
|
||||
Case CrashReason.使用JDK
|
||||
@@ -816,19 +892,27 @@ NextStack:
|
||||
Results.Add("游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。")
|
||||
Case CrashReason.Mod名称包含特殊字符
|
||||
Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。")
|
||||
Case CrashReason.MixinBootstrap缺失
|
||||
Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap。若安装后依然崩溃,可以尝试在文件名前添加英文感叹号。")
|
||||
Case CrashReason.使用32位Java导致JVM无法分配足够多的内存
|
||||
If Environment.Is64BitOperatingSystem Then
|
||||
Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
|
||||
Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
|
||||
Else
|
||||
Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用 1GB 以上的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
|
||||
Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
|
||||
End If
|
||||
Case CrashReason.崩溃日志堆栈分析发现关键字, CrashReason.MC日志堆栈分析发现关键字
|
||||
Case CrashReason.Mod缺少前置或MC版本错误
|
||||
If Additional.Count > 0 Then
|
||||
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n未满足的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
|
||||
Else
|
||||
Results.Add("由于未满足 Mod 的依赖项,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
|
||||
End If
|
||||
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名称
|
||||
Case CrashReason.堆栈分析发现Mod名称
|
||||
If Additional.Count = 1 Then
|
||||
Results.Add("名为 " & Additional.First & " 的 Mod 可能导致了游戏出错。\n\e\h")
|
||||
Else
|
||||
@@ -881,11 +965,13 @@ NextStack:
|
||||
Case CrashReason.ShadersMod与Optifine同时安装
|
||||
Results.Add("无需同时安装 Optifine 和 Shaders Mod,Optifine 已经集成了 Shaders Mod 的功能。\n在删除 Shaders Mod 后,游戏即可正常运行。")
|
||||
Case CrashReason.低版本Forge与高版本Java不兼容
|
||||
Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 8.0.320 的 Java")
|
||||
Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 1.8.0.320 的 Java")
|
||||
Case CrashReason.版本Json中存在多个Forge
|
||||
Results.Add("可能由于使用其他启动器修改了 Forge 版本,当前版本的文件存在异常,导致了游戏崩溃。\n请尝试重新全新安装 Forge,而非使用其他启动器修改 Forge 版本。")
|
||||
Case CrashReason.玩家手动触发调试崩溃
|
||||
Results.Add("* 事实上,你的游戏没有任何问题,这是你自己触发的崩溃。\n* 你难道没有更重要的事要做吗?")
|
||||
Case CrashReason.Mod需要Java11
|
||||
Results.Add("你所安装的部分 Mod 似乎需要使用 Java 11 启动。\n请在启动设置的 Java 选择一项中改用 Java 11,然后再启动游戏。\n如果你没有安装 Java 11,你可以从网络中下载、安装一个。")
|
||||
Case CrashReason.极短的程序输出
|
||||
Results.Add($"程序返回了以下信息:\n{Additional.First}\n\h")
|
||||
Case CrashReason.OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
|
||||
@@ -898,7 +984,7 @@ NextStack:
|
||||
End If
|
||||
Case CrashReason.材质过大或显卡配置不足
|
||||
Results.Add("你所使用的材质分辨率过高,或显卡配置不足,导致游戏无法继续运行。\n\n如果你正在使用高清材质,请将它移除。\n如果你没有使用材质,那么你可能需要更新显卡驱动,或者换个更好的显卡……\h")
|
||||
Case CrashReason.材质过大或显卡配置不足
|
||||
Case CrashReason.NightConfig的Bug
|
||||
Results.Add("由于 Night Config 存在问题,导致了游戏崩溃。\n你可以尝试安装 Night Config Fixes 模组,这或许能解决此问题。\h")
|
||||
Case CrashReason.光影或资源包导致OpenGL1282错误
|
||||
Results.Add("你所使用的光影或材质导致游戏出现了一些问题……\n\n请尝试删除你所添加的这些额外资源。\h")
|
||||
@@ -947,8 +1033,10 @@ NextStack:
|
||||
Replace("\e", If(IsHandAnalyze, "", vbCrLf & "你可以查看错误报告了解错误具体是如何发生的。")).
|
||||
Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf).
|
||||
Trim(vbCrLf.ToCharArray) &
|
||||
If(Results.Any(Function(r) r.EndsWith("\h")),
|
||||
If(IsHandAnalyze, "", vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。"), "")
|
||||
If(Not Results.Any(Function(r) r.EndsWith("\h")) OrElse IsHandAnalyze, "",
|
||||
vbCrLf & "如果要寻求帮助,请向他人发送错误报告文件,而不是发送这个窗口的截图。" &
|
||||
If(If(PageSetupSystem.IsLauncherNewest(), True), "",
|
||||
vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新到最新版可能会修复这个问题。" & vbCrLf & "你可以点击 设置 → 启动器 → 检查更新 来更新 PCL。"))
|
||||
End Function
|
||||
|
||||
End Class
|
||||
@@ -19,14 +19,7 @@
|
||||
Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1"))
|
||||
If ReturnNothingOnFileUseable Then
|
||||
'是否跳过
|
||||
Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
|
||||
Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
|
||||
Case 0 '使用全局设置
|
||||
Case 1 '开启
|
||||
IsSetupSkip = False
|
||||
Case 2 '关闭
|
||||
IsSetupSkip = True
|
||||
End Select
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip AndAlso File.Exists(Version.Path & Version.Name & ".jar") Then Return Nothing '跳过校验
|
||||
If Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
|
||||
End If
|
||||
@@ -52,7 +45,7 @@
|
||||
If IndexUrl = "" Then
|
||||
Return Nothing
|
||||
Else
|
||||
Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl, False), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
|
||||
Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -72,17 +65,7 @@
|
||||
#End Region
|
||||
|
||||
#Region "下载资源文件"
|
||||
Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
|
||||
Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
|
||||
Case 0
|
||||
'使用全局设置
|
||||
Case 1
|
||||
'开启
|
||||
IsSetupSkip = False
|
||||
Case 2
|
||||
'关闭
|
||||
IsSetupSkip = True
|
||||
End Select
|
||||
Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
|
||||
If IsSetupSkip Then Log("[Download] 已跳过 Assets 下载")
|
||||
If (Not SkipAssetsDownloadWhileSetupRequired) OrElse Not IsSetupSkip Then
|
||||
Dim LoadersAssets As New List(Of LoaderBase)
|
||||
@@ -960,34 +943,27 @@
|
||||
|
||||
Public Function DlSourceResourceGet(MojangBase As String) As String()
|
||||
MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
|
||||
Return {MojangBase.Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
|
||||
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/assets").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/assets").Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
|
||||
}
|
||||
End Function
|
||||
|
||||
Public Function DlSourceLibraryGet(MojangBase As String) As String()
|
||||
Return {MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"),
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
|
||||
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/maven").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/maven").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"),
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
|
||||
MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||
}
|
||||
End Function
|
||||
|
||||
Public Function DlSourceLauncherOrMetaGet(MojangBase As String, Optional IsStatic As Boolean = True) As String()
|
||||
Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
|
||||
If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
|
||||
If IsStatic Then
|
||||
Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
}
|
||||
Else
|
||||
Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
}
|
||||
End If
|
||||
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net").Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
|
||||
MojangBase,
|
||||
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
|
||||
}
|
||||
End Function
|
||||
|
||||
Private Sub DlSourceLoader(Of InputType, OutputType)(MainLoader As LoaderTask(Of InputType, OutputType),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Public Module ModJava
|
||||
Public JavaListCacheVersion As Integer = 4
|
||||
Public JavaListCacheVersion As Integer = 5
|
||||
|
||||
''' <summary>
|
||||
''' 目前所有可用的 Java。
|
||||
@@ -111,8 +111,9 @@
|
||||
IsJre = Not File.Exists(PathFolder & "javac.exe")
|
||||
'运行 -version
|
||||
Output = ShellAndGetOutput(PathFolder & "java.exe", "-version", 15000).ToLower
|
||||
If Output = "" Then Throw New Exception("尝试运行该 Java 失败")
|
||||
If Output = "" Then Throw New ApplicationException("尝试运行该 Java 失败")
|
||||
If ModeDebug Then Log("[Java] Java 检查输出:" & PathFolder & "java.exe" & vbCrLf & Output)
|
||||
If Output.Contains("/lib/ext exists") Then Throw New ApplicationException("无法运行该 Java,请在删除 Java 文件夹中的 /lib/ext 文件夹后再试")
|
||||
'获取详细信息
|
||||
Dim VersionString = If(RegexSeek(Output, "(?<=version "")[^""]+"), If(RegexSeek(Output, "(?<=openjdk )[0-9]+"), "")).Replace("_", ".").Split("-").First
|
||||
Do While VersionString.Split(".").Count < 4
|
||||
@@ -122,6 +123,7 @@
|
||||
VersionString = "1." & VersionString
|
||||
End If
|
||||
Loop
|
||||
If VersionString = "" Then Throw New ApplicationException($"未找到该 Java 的版本号{If(Output.Length < 500, $"{vbCrLf}输出为:{vbCrLf}{Output}", "")}")
|
||||
Version = New Version(VersionString)
|
||||
If Version.Minor = 0 Then
|
||||
Log("[Java] 疑似 X.0.X.X 格式版本号:" & Version.ToString)
|
||||
@@ -129,8 +131,10 @@
|
||||
End If
|
||||
Is64Bit = Output.Contains("64-bit")
|
||||
If Version.Minor <= 4 OrElse Version.Minor >= 25 Then Throw New Exception("分析详细信息失败,获取的版本为 " & Version.ToString)
|
||||
'无论如何不允许使用 JRE 16,它完全是炸的,安 OptiFine、Forge 和启动 MC 都会炸
|
||||
If IsJre AndAlso VersionCode = 16 Then Throw New Exception("由于 JRE 16 对 Minecraft 的兼容性很差,因此不再允许使用")
|
||||
'基于 #2249 发现 JRE 17 似乎也导致了 Forge 安装失败,干脆禁用更多版本的 JRE
|
||||
If IsJre AndAlso VersionCode >= 16 Then Throw New Exception("由于高版本 JRE 对游戏的兼容性很差,因此不再允许使用。你可以使用对应版本的 JDK,而非 JRE!")
|
||||
Catch ex As ApplicationException
|
||||
Throw ex
|
||||
Catch ex As Exception
|
||||
Log("[Java] 检查失败的 Java 输出:" & PathFolder & "java.exe" & vbCrLf & If(Output, "无程序输出"))
|
||||
Throw New Exception("检查 Java 失败(" & If(PathJavaw, "Nothing") & ")", ex)
|
||||
@@ -182,7 +186,8 @@
|
||||
Log("[Java] 初始化未找到可用的 Java,将自动触发搜索", LogLevel.Developer)
|
||||
JavaSearchLoader.Start(0)
|
||||
Else
|
||||
Log("[Java] 缓存中有 " & JavaList.Count & " 个可用的 Java")
|
||||
Log("[Java] 缓存中有 " & JavaList.Count & " 个可用的 Java:")
|
||||
JavaList.ForEach(Sub(j) Log($"[Java] - {j}"))
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化 Java 列表失败", LogLevel.Feedback)
|
||||
@@ -201,7 +206,9 @@
|
||||
|
||||
'添加特定的 Java
|
||||
Dim JavaPreList As New Dictionary(Of String, Boolean)
|
||||
If PathMcFolder.Split("\").Count > 3 Then JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
|
||||
If PathMcFolder.Split("\").Count > 3 Then
|
||||
JavaSearchFolder(GetPathFromFullPath(PathMcFolder), JavaPreList, False, True) 'Minecraft 文件夹的父文件夹(如果不是根目录的话)
|
||||
End If
|
||||
JavaSearchFolder(PathMcFolder, JavaPreList, False, True) 'Minecraft 文件夹
|
||||
If RelatedVersion IsNot Nothing Then JavaSearchFolder(RelatedVersion.Path, JavaPreList, False, True) '所选版本文件夹
|
||||
Dim TargetJavaList As New List(Of JavaEntry)
|
||||
@@ -209,6 +216,15 @@
|
||||
TargetJavaList.Add(New JavaEntry(Entry.Key, Entry.Value))
|
||||
Next
|
||||
|
||||
'检查特定的 Java
|
||||
If TargetJavaList.Count > 0 Then
|
||||
TargetJavaList = JavaCheckList(TargetJavaList)
|
||||
Log("[Java] 检查后找到 " & TargetJavaList.Count & " 个特定路径下的 Java:")
|
||||
For Each Java In TargetJavaList
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
End If
|
||||
|
||||
#Region "添加用户指定的 Java,储存到 UserJava 中"
|
||||
|
||||
Dim UserJava As JavaEntry = Nothing
|
||||
@@ -217,13 +233,9 @@
|
||||
If RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion).ToString.StartsWith("{") Then
|
||||
Try
|
||||
UserJava = JavaEntry.FromJson(GetJson(Setup.Get("VersionArgumentJavaSelect", Version:=RelatedVersion)))
|
||||
'确保版本独立设置中指定的 Java 在 Java 列表中(#978)
|
||||
If Not JavaList.Any(Function(j) j.PathFolder = UserJava.PathFolder) Then
|
||||
UserJava = Nothing
|
||||
Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
|
||||
Log("版本独立设置中指定的 Java 不在主 Java 列表中,此设置已重置", LogLevel.Debug)
|
||||
Exit Try
|
||||
End If
|
||||
UserJava.Check()
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
UserJava = Nothing
|
||||
Setup.Reset("VersionArgumentJavaSelect", Version:=RelatedVersion)
|
||||
@@ -235,10 +247,13 @@
|
||||
If UserJava Is Nothing AndAlso Setup.Get("LaunchArgumentJavaSelect") <> "" Then
|
||||
Try
|
||||
UserJava = JavaEntry.FromJson(GetJson(Setup.Get("LaunchArgumentJavaSelect")))
|
||||
UserJava.Check()
|
||||
Catch ex As ThreadInterruptedException
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
UserJava = Nothing
|
||||
Setup.Reset("LaunchArgumentJavaSelect")
|
||||
Log(ex, "设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
|
||||
Log(ex, "全局设置中指定的 Java 已无法使用,此设置已重置", LogLevel.Hint)
|
||||
End Try
|
||||
End If
|
||||
|
||||
@@ -250,12 +265,6 @@
|
||||
|
||||
#End Region
|
||||
|
||||
'检查特定的 Java
|
||||
If TargetJavaList.Count > 0 Then
|
||||
TargetJavaList = JavaCheckList(TargetJavaList)
|
||||
Log("[Java] 检查后找到 " & TargetJavaList.Count & " 个特定的 Java")
|
||||
End If
|
||||
|
||||
RetryGet:
|
||||
'等待进行中的搜索结束
|
||||
If JavaSearchLoader.State <> LoadState.Finished AndAlso JavaSearchLoader.State <> LoadState.Waiting Then JavaSearchLoader.WaitForExit()
|
||||
@@ -298,8 +307,9 @@ RetryGet:
|
||||
|
||||
'指定的 Java 不可用,弹窗要求选择
|
||||
Log("[Java] 发现用户指定的不兼容 Java:" & UserJava.ToString)
|
||||
Log($"[Java] 目前实际可用的 Java 列表:")
|
||||
For Each Java In AllowedJavaList
|
||||
Log($"[Java] 实际可用的 Java:{Java.ToString}")
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
Dim Requirement As String = ""
|
||||
Dim ShowRevision As Boolean = False
|
||||
@@ -316,9 +326,9 @@ RetryGet:
|
||||
Requirement = "需要 Java " & If(Left = Right, Left, Left & " ~ " & Right)
|
||||
End If
|
||||
Dim JavaCurrent As String = UserJava.VersionCode & If(ShowRevision, "." & UserJava.Version.MajorRevision & "." & UserJava.Version.MinorRevision, "")
|
||||
If Setup.Get("LaunchAdvanceJava") Then
|
||||
If Setup.Get("LaunchAdvanceJava") OrElse (RelatedVersion IsNot Nothing AndAlso Setup.Get("VersionAdvanceJava", RelatedVersion)) Then
|
||||
'直接跳过弹窗
|
||||
Hint("设置中指定了使用 Java " & JavaCurrent & ",但当前版本" & Requirement & ",这可能会导致游戏崩溃!", HintType.Critical)
|
||||
Log("[Java] 设置中指定了使用 Java " & JavaCurrent & ",但当前版本" & Requirement & ",这可能会导致游戏崩溃!", LogLevel.Debug)
|
||||
AllowedJavaList = New List(Of JavaEntry) From {UserJava}
|
||||
Else
|
||||
Select Case MyMsgBox("你在设置中手动指定了使用 Java " & JavaCurrent & ",但当前" & Requirement & "。" & vbCrLf &
|
||||
@@ -344,10 +354,12 @@ ExitUserJavaCheck:
|
||||
|
||||
'优先使用特定目录下的 Java
|
||||
For Each Java In AllowedJavaList
|
||||
'如果在官启文件夹启动,会将官启自带 Java 错误视作 MC 文件夹指定 Java,导致了 #2054 的第二例
|
||||
If Java.PathFolder.Contains(".minecraft\cache\java") Then Continue For
|
||||
If TargetJavaList.Contains(Java) Then
|
||||
'直接使用指定的 Java
|
||||
AllowedJavaList = New List(Of JavaEntry) From {Java}
|
||||
Log("[Java] 优先使用特定的 Java:" & Java.ToString)
|
||||
Log("[Java] 优先使用特定路径下的 Java:" & Java.ToString)
|
||||
GoTo UserPass
|
||||
End If
|
||||
Next
|
||||
@@ -355,6 +367,10 @@ UserPass:
|
||||
|
||||
'对适合的 Java 进行排序
|
||||
AllowedJavaList = Sort(AllowedJavaList, AddressOf JavaSorter)
|
||||
Log($"[Java] 排序后的 Java 优先顺序:")
|
||||
For Each Java In AllowedJavaList
|
||||
Log($"[Java] - {Java}")
|
||||
Next
|
||||
|
||||
'检查选定的 Java,若测试失败则尝试进行搜索
|
||||
Dim SelectedJava = AllowedJavaList.First
|
||||
@@ -364,14 +380,14 @@ UserPass:
|
||||
Throw
|
||||
Catch ex As Exception
|
||||
If ex.InnerException IsNot Nothing AndAlso TypeOf (ex.InnerException) Is ThreadInterruptedException Then Throw ex.InnerException
|
||||
Log(ex, "找到的 Java 已无法使用,尝试进行搜索")
|
||||
Log(ex, "最终选定的 Java 已无法使用,尝试进行搜索")
|
||||
AllowedJavaList = New List(Of JavaEntry)
|
||||
JavaSearchLoader.Start(IsForceRestart:=True)
|
||||
GoTo RetryGet
|
||||
End Try
|
||||
|
||||
'返回
|
||||
Log("[Java] 选定的 Java:" & SelectedJava.ToString)
|
||||
Log("[Java] 最终选定的 Java:" & AllowedJavaList.First.ToString)
|
||||
Return SelectedJava
|
||||
|
||||
Catch ex As ThreadInterruptedException
|
||||
@@ -442,7 +458,7 @@ NoUserJava:
|
||||
'4. Java 大版本
|
||||
If Left.VersionCode <> Right.VersionCode Then
|
||||
' Java 7 8 9 10 11 12 13 14 15 16 17 18 19 20...
|
||||
Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 29, 10, 11, 12, 13, 9, 8, 7, 15, 31, 30, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}
|
||||
Dim Weight = {0, 1, 2, 3, 4, 5, 6, 14, 30, 10, 12, 15, 13, 9, 8, 7, 11, 31, 29, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28}
|
||||
Return Weight.ElementAtOrDefault(Left.VersionCode) >= Weight.ElementAtOrDefault(Right.VersionCode)
|
||||
End If
|
||||
'5. 最次级版本号更接近 51
|
||||
@@ -522,10 +538,10 @@ NoUserJava:
|
||||
For Each Pair In JavaPreList
|
||||
If Pair.Key.Contains("javapath_target_") OrElse Pair.Key.Contains("javatmp") Then
|
||||
Log("[Java] 位于 " & Pair.Key & " 的 Java 包含特殊引用")
|
||||
Continue For
|
||||
Else
|
||||
Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
|
||||
JavaWithoutInherit.Add(Pair.Key, Pair.Value)
|
||||
End If
|
||||
Log("[Java] 位于 " & Pair.Key & " 的 Java 不含特殊引用")
|
||||
JavaWithoutInherit.Add(Pair.Key, Pair.Value)
|
||||
Next
|
||||
If JavaWithoutInherit.Count > 0 Then JavaPreList = JavaWithoutInherit
|
||||
|
||||
@@ -585,7 +601,7 @@ NoUserJava:
|
||||
Dim CheckThread As New Thread(Sub()
|
||||
Try
|
||||
Entry.Check()
|
||||
Log("[Java] " & Entry.ToString)
|
||||
If ModeDebug Then Log("[Java] - " & Entry.ToString)
|
||||
SyncLock ListLock
|
||||
JavaCheckList.Add(Entry)
|
||||
End SyncLock
|
||||
@@ -645,7 +661,7 @@ Wait:
|
||||
SearchEntry.Contains("java") OrElse SearchEntry.Contains("jdk") OrElse SearchEntry.Contains("env") OrElse
|
||||
SearchEntry.Contains("环境") OrElse SearchEntry.Contains("run") OrElse SearchEntry.Contains("软件") OrElse
|
||||
SearchEntry.Contains("jre") OrElse SearchEntry = "bin" OrElse SearchEntry.Contains("mc") OrElse
|
||||
SearchEntry.Contains("software") OrElse SearchEntry.Contains("cache") OrElse SearchEntry.Contains("temp") OrElse
|
||||
SearchEntry.Contains("soft") OrElse SearchEntry.Contains("cache") OrElse SearchEntry.Contains("temp") OrElse
|
||||
SearchEntry.Contains("corretto") OrElse SearchEntry.Contains("roaming") OrElse SearchEntry.Contains("users") OrElse
|
||||
SearchEntry.Contains("craft") OrElse SearchEntry.Contains("program") OrElse SearchEntry.Contains("世界") OrElse
|
||||
SearchEntry.Contains("net") OrElse SearchEntry.Contains("游戏") OrElse SearchEntry.Contains("oracle") OrElse
|
||||
@@ -661,7 +677,8 @@ Wait:
|
||||
SearchEntry.Contains("mod") OrElse SearchEntry.Contains("高清") OrElse SearchEntry.Contains("download") OrElse
|
||||
SearchEntry.Contains("launch") OrElse SearchEntry.Contains("程序") OrElse SearchEntry.Contains("path") OrElse
|
||||
SearchEntry.Contains("version") OrElse SearchEntry.Contains("baka") OrElse SearchEntry.Contains("pcl") OrElse
|
||||
SearchEntry.Contains("local") OrElse SearchEntry.Contains("packages") OrElse SearchEntry.Contains("4297127D64EC6") OrElse '官启文件夹
|
||||
SearchEntry.Contains("zulu") OrElse
|
||||
SearchEntry.Contains("local") OrElse SearchEntry.Contains("packages") OrElse SearchEntry.Contains("4297127d64ec6") OrElse '官启文件夹
|
||||
SearchEntry.Contains("国服") OrElse SearchEntry.Contains("网易") OrElse SearchEntry.Contains("ext") OrElse '网易 Java 文件夹名
|
||||
SearchEntry.Contains("netease") OrElse SearchEntry.Contains("1.") OrElse SearchEntry.Contains("启动") Then
|
||||
JavaSearchFolder(FolderInfo, Results, Source)
|
||||
@@ -680,18 +697,17 @@ Wait:
|
||||
|
||||
''' <summary>
|
||||
''' 提示 Java 缺失,并弹窗确认是否自动下载。返回玩家选择是否下载。
|
||||
''' 支持下载 Java 8、14、17,输入其他 VersionCode 会要求玩家自行下载。
|
||||
''' </summary>
|
||||
Public Function JavaDownloadConfirm(VersionCode As String) As Boolean
|
||||
If VersionCode = 8 OrElse VersionCode = 14 OrElse VersionCode = 17 Then
|
||||
Return MyMsgBox($"PCL 未找到 Java {VersionCode},是否需要 PCL 自动下载?" & vbCrLf &
|
||||
$"如果你已经安装了 Java {VersionCode},请在 设置 → 启动选项 → 游戏 Java 中手动导入。",
|
||||
"未找到 Java", "自动下载", "取消") = 1
|
||||
Else
|
||||
MyMsgBox($"PCL 未找到 Java {VersionCode}。" & vbCrLf &
|
||||
$"请自行搜索并安装 Java {VersionCode},安装后在 设置 → 启动选项 → 游戏 Java 中重新搜索或导入。",
|
||||
Public Function JavaDownloadConfirm(VersionDescription As String, Optional ForcedManualDownload As Boolean = False) As Boolean
|
||||
If ForcedManualDownload Then
|
||||
MyMsgBox($"PCL 未找到 {VersionDescription}。" & vbCrLf &
|
||||
$"请自行搜索并安装 {VersionDescription},安装后在 设置 → 启动选项 → 游戏 Java 中重新搜索或导入。",
|
||||
"未找到 Java")
|
||||
Return False
|
||||
Else
|
||||
Return MyMsgBox($"PCL 未找到 {VersionDescription},是否需要 PCL 自动下载?" & vbCrLf &
|
||||
$"如果你已经安装了 {VersionDescription},请在 设置 → 启动选项 → 游戏 Java 中手动导入。",
|
||||
"未找到 Java", "自动下载", "取消") = 1
|
||||
End If
|
||||
End Function
|
||||
|
||||
|
||||
@@ -881,7 +881,7 @@ SystemBrowser:
|
||||
End If
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMuity("https://login.live.com/oauth20_token.srf", "POST", Request, "application/x-www-form-urlencoded", 1)
|
||||
Result = NetRequestMulty("https://login.live.com/oauth20_token.srf", "POST", Request, "application/x-www-form-urlencoded", 1)
|
||||
Catch ex As Exception
|
||||
If ex.Message.Contains("must sign in again") OrElse ex.Message.Contains("invalid_grant") Then '#269
|
||||
Return {"Relogin", ""}
|
||||
@@ -908,7 +908,7 @@ SystemBrowser:
|
||||
""RelyingParty"": ""http://auth.xboxlive.com"",
|
||||
""TokenType"": ""JWT""
|
||||
}"
|
||||
Dim Result As String = NetRequestMuity("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
|
||||
Dim Result As String = NetRequestMulty("https://user.auth.xboxlive.com/user/authenticate", "POST", Request, "application/json", 3)
|
||||
|
||||
Dim ResultJson As JObject = GetJson(Result)
|
||||
Dim XBLToken As String = ResultJson("Token").ToString
|
||||
@@ -930,10 +930,10 @@ SystemBrowser:
|
||||
}"
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMuity("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
|
||||
Result = NetRequestMulty("https://xsts.auth.xboxlive.com/xsts/authorize", "POST", Request, "application/json", 3)
|
||||
Catch ex As Net.WebException
|
||||
If ex.Message.Contains("2148916233") Then
|
||||
If MyMsgBox("该微软账号尚未购买 Minecraft Java 版,或尚未注册 XBox 账户。" & vbCrLf &
|
||||
If MyMsgBox("该微软账号尚未购买 Minecraft Java 版,或尚未注册 Xbox 账户。" & vbCrLf &
|
||||
"如果你确定该账号完成了上述步骤,请先在游戏官网登录一次,然后再在启动器登录。", "登录提示", "打开游戏官网", "取消") = 1 Then
|
||||
OpenWebsite("https://www.minecraft.net/")
|
||||
End If
|
||||
@@ -967,7 +967,7 @@ SystemBrowser:
|
||||
Dim Request As String = "{""identityToken"": ""XBL3.0 x=" & Tokens(1) & ";" & Tokens(0) & """}"
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMuity("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json", 2)
|
||||
Result = NetRequestMulty("https://api.minecraftservices.com/authentication/login_with_xbox", "POST", Request, "application/json", 2)
|
||||
Catch ex As Net.WebException
|
||||
Dim Message As String = GetExceptionSummary(ex)
|
||||
If Message.Contains("(429)") Then
|
||||
@@ -991,7 +991,7 @@ SystemBrowser:
|
||||
|
||||
Dim Result As String
|
||||
Try
|
||||
Result = NetRequestMuity("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Result = NetRequestMulty("https://api.minecraftservices.com/minecraft/profile", "GET", "", "application/json", 2, New Dictionary(Of String, String) From {{"Authorization", "Bearer " & AccessToken}})
|
||||
Catch ex As Net.WebException
|
||||
Dim Message As String = GetExceptionSummary(ex)
|
||||
If Message.Contains("(429)") Then
|
||||
@@ -1000,7 +1000,7 @@ SystemBrowser:
|
||||
ElseIf Message.Contains("(404)") Then
|
||||
Log(ex, "微软登录第 6 步汇报 404")
|
||||
RunInNewThread(Sub()
|
||||
Select Case MyMsgBox("你可能没有在 Minecraft 官网创建档案,XBox Game Pass 已到期,或者还没有购买 Minecraft。" & vbCrLf &
|
||||
Select Case MyMsgBox("你可能没有在 Minecraft 官网创建档案,Xbox Game Pass 已到期,或者还没有购买 Minecraft。" & vbCrLf &
|
||||
"如果你确认拥有 Minecraft,请在官网上创建档案后再试。", "登录失败", "创建档案", "购买 Minecraft", "取消")
|
||||
Case 1
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/msaprofile/mygames/editprofile")
|
||||
@@ -1115,11 +1115,14 @@ SystemBrowser:
|
||||
'OptiFine 检测
|
||||
If McVersionCurrent.Version.HasOptiFine Then
|
||||
If McVersionCurrent.Version.McCodeMain <= 7 AndAlso McVersionCurrent.Version.McCodeMain > 0 Then
|
||||
'<1.7:至多 Java 8(Java 9 - 10 未测试)
|
||||
'<1.7:至多 Java 8
|
||||
MaxVer = New Version(1, 8, 999, 999)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain >= 8 AndAlso McVersionCurrent.Version.McCodeMain <= 11 Then
|
||||
'1.8 - 1.11:必须恰好 Java 8(Java 9 - 10 未测试)
|
||||
'1.8 - 1.11:必须恰好 Java 8
|
||||
MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 8, 999, 999)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain = 12 Then
|
||||
'1.12:最高 Java 8
|
||||
MaxVer = New Version(1, 8, 999, 999)
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -1128,24 +1131,21 @@ SystemBrowser:
|
||||
If McVersionCurrent.Version.McName = "1.7.2" Then
|
||||
'1.7.2:必须 Java 7
|
||||
MinVer = New Version(1, 7, 0, 0) : MaxVer = New Version(1, 7, 999, 999)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain <= 12 AndAlso McVersionCurrent.Version.McCodeMain > 0 AndAlso VersionSortBoolean("14.23.5.2855", McVersionCurrent.Version.ForgeVersion) Then
|
||||
'<1.12,Forge 14.23.5.2855 及更低:Java 8
|
||||
ElseIf McVersionCurrent.Version.McCodeMain <= 12 AndAlso McVersionCurrent.Version.McCodeMain > 0 Then
|
||||
'<=1.12:Java 8
|
||||
MaxVer = New Version(1, 8, 999, 999)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain <= 14 AndAlso McVersionCurrent.Version.McCodeMain > 0 AndAlso VersionSortBoolean("28.2.23", McVersionCurrent.Version.ForgeVersion) Then
|
||||
'1.13 - 1.14,Forge 28.2.23 及更低:Java 8 - 10
|
||||
ElseIf McVersionCurrent.Version.McCodeMain <= 14 AndAlso McVersionCurrent.Version.McCodeMain >= 13 Then
|
||||
'1.13 - 1.14:Java 8 - 10
|
||||
MinVer = New Version(1, 8, 0, 0) : MaxVer = New Version(1, 10, 999, 999)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain <= 15 AndAlso McVersionCurrent.Version.McCodeMain > 0 Then
|
||||
ElseIf McVersionCurrent.Version.McCodeMain = 15 Then
|
||||
'1.15:Java 8 - 15
|
||||
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
|
||||
'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)
|
||||
ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 99 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305
|
||||
'1.18+:若安装了 OptiFine,最高 Java 18
|
||||
ElseIf McVersionCurrent.Version.McCodeMain >= 18 AndAlso McVersionCurrent.Version.McCodeMain < 19 AndAlso McVersionCurrent.Version.HasOptiFine Then '#305
|
||||
'1.18:若安装了 OptiFine,最高 Java 18
|
||||
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
|
||||
|
||||
@@ -1179,17 +1179,27 @@ SystemBrowser:
|
||||
If Task.IsAborted Then Exit Sub '中断加载会导致 JavaSelect 异常地返回空值,误判找不到 Java
|
||||
McLaunchLog("无合适的 Java,需要确认是否自动下载")
|
||||
Dim JavaCode As String
|
||||
If MinVer >= New Version(1, 16, 0, 0) Then
|
||||
If MinVer >= New Version(1, 9, 0, 0) Then
|
||||
JavaCode = 17
|
||||
If Not JavaDownloadConfirm("Java 17") Then Throw New Exception("$$")
|
||||
ElseIf MaxVer < New Version(1, 8, 0, 0) Then
|
||||
JavaCode = 7
|
||||
ElseIf MinVer > New Version(1, 8, 0, 52) Then
|
||||
'缺少 Java 8 较新版(统一通行证)
|
||||
If Not JavaDownloadConfirm("Java 7") Then Throw New Exception("$$")
|
||||
ElseIf MinVer > New Version(1, 8, 0, 100) AndAlso MaxVer < New Version(1, 8, 0, 321) Then
|
||||
JavaCode = "8u101"
|
||||
JavaDownloadConfirm("Java 8.0.101 ~ 8.0.320", True)
|
||||
Throw New Exception("$$")
|
||||
ElseIf MinVer > New Version(1, 8, 0, 100) Then
|
||||
JavaCode = "8u101"
|
||||
JavaDownloadConfirm("Java 8.0.101 或更高版本的 Java 8", True)
|
||||
Throw New Exception("$$")
|
||||
ElseIf MaxVer < New Version(1, 8, 0, 321) Then
|
||||
JavaCode = 8
|
||||
If Not JavaDownloadConfirm("Java 8.0.320 或更低版本的 Java 8") Then Throw New Exception("$$")
|
||||
Else
|
||||
JavaCode = 8
|
||||
If Not JavaDownloadConfirm("Java 8") Then Throw New Exception("$$")
|
||||
End If
|
||||
If Not JavaDownloadConfirm(JavaCode) Then Throw New Exception("$$")
|
||||
|
||||
'开始自动下载
|
||||
Dim JavaLoader = JavaFixLoaders(JavaCode)
|
||||
@@ -1347,7 +1357,7 @@ SystemBrowser:
|
||||
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
If McLaunchJavaSelected.VersionCode >= 9 Then DataList.Add("--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED")
|
||||
DataList.Insert(0, "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-jar """ & ExtractJavaWrapper() & """")
|
||||
|
||||
'添加 MainClass
|
||||
@@ -1414,7 +1424,7 @@ NextVersion:
|
||||
|
||||
'添加 Java Wrapper 作为主 Jar
|
||||
If McLaunchJavaSelected.VersionCode >= 9 Then DataList.Add("--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED")
|
||||
DataList.Insert(0, "-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-Doolloo.jlw.tmpdir=""" & GetJavaWrapperDir() & """")
|
||||
DataList.Add("-jar """ & ExtractJavaWrapper() & """")
|
||||
|
||||
'将 "-XXX" 与后面 "XXX" 合并到一起
|
||||
@@ -1740,6 +1750,15 @@ NextVersion:
|
||||
|
||||
'更新 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
|
||||
'语言
|
||||
'1.0- :没有语言选项
|
||||
@@ -1760,11 +1779,11 @@ NextVersion:
|
||||
WriteIni(SetupFileAddress, "lang", RequiredLang)
|
||||
McLaunchLog($"已将语言从 {CurrentLang} 修改为 {RequiredLang}")
|
||||
End If
|
||||
'如果是初次设置,一并修改 forceUnicodeFont
|
||||
If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then
|
||||
WriteIni(SetupFileAddress, "forceUnicodeFont", "true")
|
||||
McLaunchLog("已开启 forceUnicodeFont")
|
||||
End If
|
||||
''如果是初次设置,一并修改 forceUnicodeFont
|
||||
'If Setup.Get("ToolHelpChinese") AndAlso (CurrentLang = "none" OrElse Not Directory.Exists(McVersionCurrent.PathIndie & "saves")) Then
|
||||
' WriteIni(SetupFileAddress, "forceUnicodeFont", "true")
|
||||
' McLaunchLog("已开启 forceUnicodeFont")
|
||||
'End If
|
||||
'窗口
|
||||
Select Case Setup.Get("LaunchArgumentWindowType")
|
||||
Case 0 '全屏
|
||||
@@ -1830,10 +1849,14 @@ NextVersion:
|
||||
Else
|
||||
PackFormat = 12
|
||||
End If
|
||||
Case 20, 99 '99 是快照版
|
||||
PackFormat = 15
|
||||
Case Else
|
||||
PackFormat = 16
|
||||
Case 20
|
||||
If McVersionCurrent.Version.McCodeSub <= 1 Then
|
||||
PackFormat = 15
|
||||
Else
|
||||
PackFormat = 17
|
||||
End If
|
||||
Case Else '快照版是 99
|
||||
PackFormat = 17
|
||||
'https://minecraft.fandom.com/zh/wiki/数据包#数据包版本
|
||||
End Select
|
||||
McLaunchLog("正在构建自定义皮肤资源包,格式为:" & PackFormat)
|
||||
@@ -1844,7 +1867,7 @@ NextVersion:
|
||||
Dim Skin As New MyBitmap(PathAppdata & "CustomSkin.png")
|
||||
If (McVersionCurrent.Version.McCodeMain = 6 OrElse McVersionCurrent.Version.McCodeMain = 7) AndAlso Skin.Pic.Height = 64 Then
|
||||
McLaunchLog("该 Minecraft 版本不支持双层皮肤,已进行裁剪")
|
||||
Skin = Skin.Clip(New System.Drawing.Rectangle(0, 0, 64, 32))
|
||||
Skin = Skin.Clip(0, 0, 64, 32)
|
||||
End If
|
||||
Skin.Save(Path & "PCL\CustomSkin_Cliped.png")
|
||||
'构建压缩文件
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,907 @@
|
||||
Imports System.IO.Compression
|
||||
|
||||
Public Module ModMod
|
||||
|
||||
Public Class McMod
|
||||
|
||||
#Region "基础"
|
||||
|
||||
''' <summary>
|
||||
''' Mod 文件的地址。
|
||||
''' </summary>
|
||||
Public ReadOnly Path As String
|
||||
Public Sub New(Path As String)
|
||||
Me.Path = If(Path, "")
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的完整文件名。
|
||||
''' </summary>
|
||||
Public ReadOnly Property FileName As String
|
||||
Get
|
||||
Return GetFileNameFromPath(Path)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的完整文件名,去除最后的 .disabled。
|
||||
''' </summary>
|
||||
Public ReadOnly Property RawFileName As String
|
||||
Get
|
||||
Return FileName.Replace(".disabled", "")
|
||||
End Get
|
||||
End Property
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的状态。
|
||||
''' </summary>
|
||||
Public ReadOnly Property State As McModState
|
||||
Get
|
||||
Load()
|
||||
If Not IsFileAvailable Then
|
||||
Return McModState.Unavaliable
|
||||
ElseIf Path.EndsWith(".disabled") Then
|
||||
Return McModState.Disabled
|
||||
Else
|
||||
Return McModState.Fine
|
||||
End If
|
||||
End Get
|
||||
End Property
|
||||
Public Enum McModState As Integer
|
||||
Fine = 0
|
||||
Disabled = 1
|
||||
Unavaliable = 2
|
||||
End Enum
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "信息项"
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的名称。若不可用则为 ModID 或无扩展的文件名。
|
||||
''' </summary>
|
||||
Public Property Name As String
|
||||
Get
|
||||
If _Name Is Nothing Then Load()
|
||||
If _Name Is Nothing Then _Name = _ModId
|
||||
If _Name Is Nothing Then _Name = GetFileNameWithoutExtentionFromPath(Path)
|
||||
Return _Name
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Name Is Nothing AndAlso value IsNot Nothing AndAlso
|
||||
value.ToLower <> "name" AndAlso value.Count > 1 AndAlso Val(value).ToString <> value Then
|
||||
_Name = value
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private _Name As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的描述信息。
|
||||
''' </summary>
|
||||
Public Property Description As String
|
||||
Get
|
||||
If _Description Is Nothing Then Load()
|
||||
If _Description Is Nothing AndAlso FileUnavailableReason IsNot Nothing Then _Description = FileUnavailableReason.Message
|
||||
'If _Description Is Nothing Then _Description = Path
|
||||
Return _Description
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Description Is Nothing AndAlso value IsNot Nothing AndAlso value.Count > 2 Then
|
||||
_Description = value.ToString.Trim(vbLf)
|
||||
'优化显示:若以 [a-zA-Z0-9] 结尾,加上小数点句号
|
||||
If _Description.ToLower.LastIndexOfAny("qwertyuiopasdfghjklzxcvbnm0123456789") = _Description.Count - 1 Then _Description += "."
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private _Description As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的版本,不保证符合版本格式规范。
|
||||
''' </summary>
|
||||
Public Property Version As String
|
||||
Get
|
||||
If _Version Is Nothing Then Load()
|
||||
Return _Version
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Version IsNot Nothing AndAlso (_Version.Contains(".") OrElse _Version.Contains("-")) Then Exit Property
|
||||
If value IsNot Nothing AndAlso value.ToLower.Contains("version") Then value = "version" '需要修改的标识
|
||||
_Version = value
|
||||
End Set
|
||||
End Property
|
||||
Private _Version As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' 用于依赖检查的 ModID。
|
||||
''' </summary>
|
||||
Public Property ModId As String
|
||||
Get
|
||||
If _ModId Is Nothing Then Load()
|
||||
Return _ModId
|
||||
End Get
|
||||
Set(value As String)
|
||||
If value Is Nothing Then Exit Property
|
||||
value = RegexSeek(value.ToLower, "[0-9a-z_-]+")
|
||||
If value IsNot Nothing AndAlso value.ToLower <> "name" AndAlso value.Count > 1 AndAlso Val(value).ToString <> value Then
|
||||
If Not PossibleModId.Contains(value) Then PossibleModId.Add(value)
|
||||
If _ModId Is Nothing Then _ModId = value
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private _ModId As String = Nothing
|
||||
''' <summary>
|
||||
''' 其他可能的 ModID。
|
||||
''' </summary>
|
||||
Public PossibleModId As New List(Of String)
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的主页。
|
||||
''' </summary>
|
||||
Public Property Url As String
|
||||
Get
|
||||
If _Url Is Nothing Then Load()
|
||||
Return _Url
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Url Is Nothing AndAlso value IsNot Nothing AndAlso value.StartsWith("http") Then
|
||||
_Url = value
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private _Url As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' Mod 的作者列表。
|
||||
''' </summary>
|
||||
Public Property Authors As String
|
||||
Get
|
||||
If _Authors Is Nothing Then Load()
|
||||
Return _Authors
|
||||
End Get
|
||||
Set(value As String)
|
||||
If _Authors Is Nothing AndAlso Not String.IsNullOrWhiteSpace(value) Then
|
||||
_Authors = value
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private _Authors As String = Nothing
|
||||
|
||||
''' <summary>
|
||||
''' 依赖项,其中包括了 Minecraft 的版本要求。格式为 ModID - VersionRequirement,若无版本要求则为 Nothing。
|
||||
''' </summary>
|
||||
Public ReadOnly Property Dependencies As Dictionary(Of String, String)
|
||||
Get
|
||||
Load()
|
||||
Return _Dependencies
|
||||
End Get
|
||||
End Property
|
||||
Private _Dependencies As New Dictionary(Of String, String)
|
||||
Private Sub AddDependency(ModID As String, Optional VersionRequirement As String = Nothing)
|
||||
'确保信息正确
|
||||
If ModID Is Nothing OrElse ModID.Count < 2 Then Exit Sub
|
||||
ModID = ModID.ToLower
|
||||
If ModID = "name" OrElse Val(ModID).ToString = ModID Then Exit Sub '跳过 name 与纯数字 id
|
||||
If VersionRequirement Is Nothing OrElse ((Not VersionRequirement.Contains(".")) AndAlso (Not VersionRequirement.Contains("-"))) OrElse VersionRequirement.Contains("$") Then
|
||||
VersionRequirement = Nothing
|
||||
Else
|
||||
If (Not VersionRequirement.StartsWith("[")) AndAlso (Not VersionRequirement.StartsWith("(")) AndAlso (Not VersionRequirement.EndsWith("]")) AndAlso (Not VersionRequirement.EndsWith(")")) Then VersionRequirement = "[" & VersionRequirement & ",)"
|
||||
End If
|
||||
'向依赖项中添加
|
||||
If _Dependencies.ContainsKey(ModID) Then
|
||||
If _Dependencies(ModID) Is Nothing Then _Dependencies(ModID) = VersionRequirement
|
||||
Else
|
||||
_Dependencies.Add(ModID, VersionRequirement)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "加载步骤标记"
|
||||
|
||||
'1. 进行文件可用性检查
|
||||
' 成功:继续第二步。
|
||||
' 失败:标记 FileUnavailableReason, 并停止后续加载。
|
||||
''' <summary>
|
||||
''' 是否已进行 Mod 文件的基础加载。(这包括第一步和第二步)
|
||||
''' </summary>
|
||||
Private IsLoaded As Boolean = False
|
||||
''' <summary>
|
||||
''' Mod 文件是否可被正常读取。
|
||||
''' </summary>
|
||||
Public ReadOnly Property IsFileAvailable As Boolean
|
||||
Get
|
||||
Load()
|
||||
Return FileUnavailableReason Is Nothing
|
||||
End Get
|
||||
End Property
|
||||
''' <summary>
|
||||
''' Mod 文件出错的原因。若无错误,则为 Nothing。
|
||||
''' </summary>
|
||||
Public ReadOnly Property FileUnavailableReason As Exception
|
||||
Get
|
||||
Load()
|
||||
Return _FileUnavailableReason
|
||||
End Get
|
||||
End Property
|
||||
Private _FileUnavailableReason As Exception = Nothing
|
||||
|
||||
'2. 进行 .class 以外的信息获取
|
||||
' 成功:标记 IsInfoWithoutClassAvailable。
|
||||
' 失败:什么也不干。如果需要补充信息的话,检测到 IsInfoWithoutClassAvailable 为 False,会自动继续加载。
|
||||
''' <summary>
|
||||
''' 是否已在不获取 .class 文件的前提下完成了所需信息的加载。
|
||||
''' </summary>
|
||||
Private IsInfoWithoutClassAvailable As Boolean = False
|
||||
|
||||
'3. 尝试从 .class 文件中获取信息
|
||||
' 成功:标记 IsInfoWithClassAvailable。
|
||||
' 失败:什么也不干。
|
||||
''' <summary>
|
||||
''' 是否已进行 .class 文件的信息获取。
|
||||
''' </summary>
|
||||
Private IsInfoWithClassLoaded As Boolean = False
|
||||
''' <summary>
|
||||
''' 是否已在 .class 文件中完成了所需信息的加载。
|
||||
''' </summary>
|
||||
Private IsInfoWithClassAvailable As Boolean = False
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "加载"
|
||||
|
||||
''' <summary>
|
||||
''' 初始化所有数据。
|
||||
''' </summary>
|
||||
Private Sub Init()
|
||||
_Name = Nothing
|
||||
_Description = Nothing
|
||||
_Version = Nothing
|
||||
_ModId = Nothing
|
||||
PossibleModId = New List(Of String)
|
||||
_Dependencies = New Dictionary(Of String, String)
|
||||
IsLoaded = False
|
||||
_FileUnavailableReason = Nothing
|
||||
IsInfoWithoutClassAvailable = False
|
||||
IsInfoWithClassLoaded = False
|
||||
IsInfoWithClassAvailable = False
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 进行文件可用性检查与 .class 以外的信息获取。
|
||||
''' </summary>
|
||||
Public Sub Load(Optional ForceReload As Boolean = False)
|
||||
If IsLoaded AndAlso Not ForceReload Then Exit Sub
|
||||
'初始化
|
||||
Init()
|
||||
'阶段 1:基础可用性检查
|
||||
Dim Jar As ZipArchive
|
||||
Try
|
||||
'打开 Jar 文件
|
||||
If Path.Length < 2 Then Throw New FileNotFoundException("错误的 Mod 文件路径(" & If(Path, "null") & ")")
|
||||
If Not File.Exists(Path) Then Throw New FileNotFoundException("未找到 Mod 文件(" & Path & ")")
|
||||
Jar = New ZipArchive(New FileStream(Path, FileMode.Open))
|
||||
If Jar.Entries.Count = 0 Then Throw New FileFormatException("文件内容为空")
|
||||
Catch ex As UnauthorizedAccessException
|
||||
Log(ex, "Mod 文件由于无权限无法打开(" & Path & ")", LogLevel.Developer)
|
||||
_FileUnavailableReason = New UnauthorizedAccessException("没有读取此文件的权限,请尝试右键以管理员身份运行 PCL", ex)
|
||||
Jar = Nothing
|
||||
Catch ex As Exception
|
||||
Log(ex, "Mod 文件无法打开(" & Path & ")", LogLevel.Developer)
|
||||
_FileUnavailableReason = ex
|
||||
Jar = Nothing
|
||||
End Try
|
||||
'阶段 2:信息获取
|
||||
If Jar IsNot Nothing Then LoadWithoutClass(Jar)
|
||||
'阶段 3: Class 信息获取
|
||||
If Jar IsNot Nothing AndAlso Not IsInfoWithoutClassAvailable Then LoadWithClass(Jar)
|
||||
'释放文件
|
||||
Try
|
||||
If Jar IsNot Nothing Then Jar.Dispose()
|
||||
Catch
|
||||
End Try
|
||||
'完成标记
|
||||
IsLoaded = True
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 进行不使用 .class 文件的信息获取。
|
||||
''' </summary>
|
||||
Private Sub LoadWithoutClass(Jar As ZipArchive)
|
||||
|
||||
#Region "尝试使用 mcmod.info"
|
||||
Try
|
||||
'获取信息文件
|
||||
Dim InfoEntry As ZipArchiveEntry = Jar.GetEntry("mcmod.info")
|
||||
Dim InfoString As String = Nothing
|
||||
If InfoEntry IsNot Nothing Then
|
||||
InfoString = ReadFile(InfoEntry.Open())
|
||||
If InfoString.Length < 15 Then InfoString = Nothing
|
||||
End If
|
||||
If InfoString Is Nothing Then Exit Try
|
||||
'获取可用 Json 项
|
||||
Dim InfoObject As JObject
|
||||
Dim JsonObject = GetJson(InfoString)
|
||||
If JsonObject.Type = JTokenType.Array Then
|
||||
InfoObject = JsonObject(0)
|
||||
Else
|
||||
InfoObject = JsonObject("modList")(0)
|
||||
End If
|
||||
'从文件中获取 Mod 信息项
|
||||
Name = InfoObject("name")
|
||||
Description = InfoObject("description")
|
||||
Version = InfoObject("version")
|
||||
Url = InfoObject("url")
|
||||
ModId = InfoObject("modid")
|
||||
Dim AuthorJson As JArray = InfoObject("authorList")
|
||||
If AuthorJson IsNot Nothing Then
|
||||
Dim Author As New List(Of String)
|
||||
For Each Token In AuthorJson
|
||||
Author.Add(Token.ToString)
|
||||
Next
|
||||
If Author.Count > 0 Then Authors = Join(Author, ", ")
|
||||
End If
|
||||
Dim Reqs As JArray = InfoObject("requiredMods")
|
||||
If Reqs IsNot Nothing Then
|
||||
For Each Token As String In Reqs
|
||||
If Not String.IsNullOrEmpty(Token) Then
|
||||
Token = Token.Substring(Token.IndexOf(":") + 1)
|
||||
If Token.Contains("@") Then
|
||||
AddDependency(Token.Split("@")(0), Token.Split("@")(1))
|
||||
Else
|
||||
AddDependency(Token)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Reqs = InfoObject("dependancies")
|
||||
If Reqs IsNot Nothing Then
|
||||
For Each Token As String In Reqs
|
||||
If Not String.IsNullOrEmpty(Token) Then
|
||||
Token = Token.Substring(Token.IndexOf(":") + 1)
|
||||
If Token.Contains("@") Then
|
||||
AddDependency(Token.Split("@")(0), Token.Split("@")(1))
|
||||
Else
|
||||
AddDependency(Token)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取 mcmod.info 时出现未知错误(" & Path & ")", LogLevel.Developer)
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 mods.toml"
|
||||
Try
|
||||
'获取 mods.toml 文件
|
||||
Dim TomlEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/mods.toml")
|
||||
Dim TomlText As String = Nothing
|
||||
If TomlEntry IsNot Nothing Then
|
||||
TomlText = ReadFile(TomlEntry.Open())
|
||||
If TomlText.Length < 15 Then TomlText = Nothing
|
||||
End If
|
||||
If TomlText Is Nothing Then Exit Try
|
||||
'文件标准化:统一换行符为 vbLf,去除注释、头尾的空格、空行
|
||||
Dim Lines As New List(Of String)
|
||||
For Each Line In TomlText.Replace(vbCrLf, vbLf).Replace(vbCr, vbLf).Split(vbLf) '统一换行符
|
||||
If Line.StartsWith("#") Then '去除注释
|
||||
Continue For
|
||||
ElseIf Line.Contains("#") Then
|
||||
Line = Line.Substring(0, Line.IndexOf("#"))
|
||||
End If
|
||||
Line = Line.Trim(New Char() {" "c, " "c, " "c}) '去除头尾的空格
|
||||
If Line.Count > 0 Then Lines.Add(Line) '去除空行
|
||||
Next
|
||||
'读取文件数据
|
||||
Dim TomlData As New List(Of KeyValuePair(Of String, Dictionary(Of String, Object))) From {New KeyValuePair(Of String, Dictionary(Of String, Object))("", New Dictionary(Of String, Object))}
|
||||
For i = 0 To Lines.Count - 1
|
||||
Dim Line As String = Lines(i)
|
||||
If Line.StartsWith("[") AndAlso Line.EndsWith("]") Then
|
||||
'段落标记
|
||||
Dim Header = Line.Trim("[]".ToCharArray)
|
||||
TomlData.Add(New KeyValuePair(Of String, Dictionary(Of String, Object))(Header, New Dictionary(Of String, Object)))
|
||||
ElseIf Line.Contains("=") Then
|
||||
'字段标记
|
||||
Dim Key As String = Line.Substring(0, Line.IndexOf("=")).TrimEnd(New Char() {" "c, " "c, " "c})
|
||||
Dim RawValue As String = Line.Substring(Line.IndexOf("=") + 1).TrimStart(New Char() {" "c, " "c, " "c})
|
||||
Dim Value As Object
|
||||
If RawValue.StartsWith("""") AndAlso RawValue.EndsWith("""") Then
|
||||
'单行字符串
|
||||
Value = RawValue.Trim("""")
|
||||
ElseIf RawValue.StartsWith("'''") Then
|
||||
'多行字符串
|
||||
Dim ValueLines As New List(Of String) From {RawValue.TrimStart("'")}
|
||||
If ValueLines(0).EndsWith("'''") Then '把多行字符串按单行写法写的错误处理(#2732)
|
||||
ValueLines(0) = ValueLines(0).TrimEnd("'")
|
||||
Else
|
||||
Do Until i >= Lines.Count - 1
|
||||
i += 1
|
||||
Dim ValueLine As String = Lines(i)
|
||||
If ValueLine.EndsWith("'''") Then
|
||||
ValueLines.Add(ValueLine.TrimEnd("'"))
|
||||
Exit Do
|
||||
Else
|
||||
ValueLines.Add(ValueLine)
|
||||
End If
|
||||
Loop
|
||||
End If
|
||||
Value = Join(ValueLines, vbLf).Trim(vbLf).Replace(vbLf, vbCrLf)
|
||||
ElseIf RawValue.ToLower = "true" OrElse RawValue.ToLower = "false" Then
|
||||
'布尔型
|
||||
Value = (RawValue.ToLower = "true")
|
||||
ElseIf Val(RawValue).ToString = RawValue Then
|
||||
'数字型
|
||||
Value = Val(RawValue)
|
||||
Else
|
||||
'不知道是个啥玩意儿,直接存储
|
||||
Value = RawValue
|
||||
End If
|
||||
TomlData.Last.Value(Key) = Value
|
||||
Else
|
||||
'不知道是个啥玩意儿
|
||||
Exit Try
|
||||
End If
|
||||
Next
|
||||
'从文件数据中获取信息
|
||||
Dim ModEntry As Dictionary(Of String, Object) = Nothing
|
||||
For Each TomlSubData In TomlData
|
||||
If TomlSubData.Key = "mods" Then
|
||||
ModEntry = TomlSubData.Value
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
If ModEntry Is Nothing OrElse Not ModEntry.ContainsKey("modId") Then Exit Try
|
||||
ModId = ModEntry("modId")
|
||||
If _ModId Is Nothing Then Exit Try '设置了无效的 ModID
|
||||
If ModEntry.ContainsKey("displayName") Then Name = ModEntry("displayName")
|
||||
If ModEntry.ContainsKey("description") Then Description = ModEntry("description")
|
||||
If ModEntry.ContainsKey("version") Then Version = ModEntry("version")
|
||||
If TomlData(0).Value.ContainsKey("displayURL") Then Url = TomlData(0).Value("displayURL")
|
||||
If TomlData(0).Value.ContainsKey("authors") Then Authors = TomlData(0).Value("authors")
|
||||
For Each TomlSubData In TomlData
|
||||
If TomlSubData.Key.ToLower = $"dependencies.{ModId.ToLower}" Then
|
||||
Dim DepEntry As Dictionary(Of String, Object) = TomlSubData.Value
|
||||
If DepEntry.ContainsKey("modId") AndAlso DepEntry.ContainsKey("mandatory") AndAlso DepEntry("mandatory") AndAlso
|
||||
DepEntry.ContainsKey("side") AndAlso Not DepEntry("side").ToString.ToLower = "server" Then
|
||||
AddDependency(DepEntry("modId"), If(DepEntry.ContainsKey("versionRange"), DepEntry("versionRange"), Nothing))
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
'加载成功
|
||||
GoTo Finished
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取 mods.toml 时出现未知错误(" & Path & ")", LogLevel.Developer)
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 fabric.mod.json"
|
||||
Try
|
||||
'获取 fabric.mod.json 文件
|
||||
Dim FabricEntry As ZipArchiveEntry = Jar.GetEntry("fabric.mod.json")
|
||||
Dim FabricText As String = Nothing
|
||||
If FabricEntry IsNot Nothing Then
|
||||
FabricText = ReadFile(FabricEntry.Open(), Encoding.UTF8)
|
||||
If Not FabricText.Contains("schemaVersion") Then FabricText = Nothing
|
||||
End If
|
||||
If FabricText Is Nothing Then Exit Try
|
||||
Dim FabricObject As JObject = GetJson(FabricText)
|
||||
GotFabric:
|
||||
'从文件中获取 Mod 信息项
|
||||
If FabricObject.ContainsKey("name") Then Name = FabricObject("name")
|
||||
If FabricObject.ContainsKey("version") Then Version = FabricObject("version")
|
||||
If FabricObject.ContainsKey("description") Then Description = FabricObject("description")
|
||||
If FabricObject.ContainsKey("id") Then ModId = FabricObject("id")
|
||||
If FabricObject.ContainsKey("contact") Then Url = If(FabricObject("contact")("homepage"), "")
|
||||
Dim AuthorJson As JArray = FabricObject("authors")
|
||||
If AuthorJson IsNot Nothing Then
|
||||
Dim Author As New List(Of String)
|
||||
For Each Token In AuthorJson
|
||||
Author.Add(Token.ToString)
|
||||
Next
|
||||
If Author.Count > 0 Then Authors = Join(Author, ", ")
|
||||
End If
|
||||
'If (Not FabricObject.ContainsKey("serverSideOnly")) OrElse FabricObject("serverSideOnly")("value").ToObject(Of Boolean) = False Then
|
||||
' '添加 Minecraft 依赖
|
||||
' Dim DepMinecraft As String = If(If(FabricObject("acceptedMinecraftVersions") IsNot Nothing, FabricObject("acceptedMinecraftVersions")("value"), ""), "")
|
||||
' If DepMinecraft <> "" Then AddDependency("minecraft", DepMinecraft)
|
||||
' '添加其他依赖
|
||||
' Dim Deps As String = If(If(FabricObject("dependencies") IsNot Nothing, FabricObject("dependencies")("value"), ""), "")
|
||||
' If Deps <> "" Then
|
||||
' For Each Dep In Deps.Split(";")
|
||||
' If Dep = "" OrElse Not Dep.StartsWith("required-") Then Continue For
|
||||
' Dep = Dep.Substring(Dep.IndexOf(":") + 1)
|
||||
' If Dep.Contains("@") Then
|
||||
' AddDependency(Dep.Split("@")(0), Dep.Split("@")(1))
|
||||
' Else
|
||||
' AddDependency(Dep)
|
||||
' End If
|
||||
' Next
|
||||
' End If
|
||||
'End If
|
||||
'加载成功
|
||||
GoTo Finished
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取 fabric.mod.json 时出现未知错误(" & Path & ")", LogLevel.Developer)
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 fml_cache_annotation.json"
|
||||
Try
|
||||
'获取 fml_cache_annotation.json 文件
|
||||
Dim FmlEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/fml_cache_annotation.json")
|
||||
Dim FmlText As String = Nothing
|
||||
If FmlEntry IsNot Nothing Then
|
||||
FmlText = ReadFile(FmlEntry.Open(), Encoding.UTF8)
|
||||
If Not FmlText.Contains("Lnet/minecraftforge/fml/common/Mod;") Then FmlText = Nothing
|
||||
End If
|
||||
If FmlText Is Nothing Then Exit Try
|
||||
Dim FmlJson As JObject = GetJson(FmlText)
|
||||
'获取可用 Json 项
|
||||
Dim FmlObject As JObject = Nothing
|
||||
For Each ModFilePair In FmlJson
|
||||
Dim ModFileAnnos As JArray = ModFilePair.Value("annotations")
|
||||
If ModFileAnnos IsNot Nothing Then
|
||||
'先获取 Mod
|
||||
For Each ModFileAnno In ModFileAnnos
|
||||
Dim Name As String = If(ModFileAnno("name"), "")
|
||||
If Name = "Lnet/minecraftforge/fml/common/Mod;" Then
|
||||
FmlObject = ModFileAnno("values")
|
||||
GoTo Got
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Next
|
||||
Exit Try
|
||||
Got:
|
||||
'从文件中获取 Mod 信息项
|
||||
If FmlObject.ContainsKey("useMetadata") AndAlso If(FmlObject("useMetadata")("value"), "").ToString.ToLower = "true" Then
|
||||
'要求使用 mcmod.info 中的信息
|
||||
Dim value As String = FmlObject("modid")("value")
|
||||
If value Is Nothing Then Exit Try
|
||||
value = RegexSeek(value.ToLower, "[0-9a-z_]+")
|
||||
If value IsNot Nothing AndAlso value.ToLower <> "name" AndAlso value.Count > 1 AndAlso Val(value).ToString <> value Then
|
||||
If Not PossibleModId.Contains(value) Then PossibleModId.Add(value)
|
||||
End If
|
||||
Exit Try
|
||||
End If
|
||||
If FmlObject.ContainsKey("name") Then Name = FmlObject("name")("value")
|
||||
If FmlObject.ContainsKey("version") Then Version = FmlObject("version")("value")
|
||||
If FmlObject.ContainsKey("modid") Then ModId = FmlObject("modid")("value")
|
||||
If (Not FmlObject.ContainsKey("serverSideOnly")) OrElse FmlObject("serverSideOnly")("value").ToObject(Of Boolean) = False Then
|
||||
'添加 Minecraft 依赖
|
||||
Dim DepMinecraft As String = If(If(FmlObject("acceptedMinecraftVersions") IsNot Nothing, FmlObject("acceptedMinecraftVersions")("value"), ""), "")
|
||||
If DepMinecraft <> "" Then AddDependency("minecraft", DepMinecraft)
|
||||
'添加其他依赖
|
||||
Dim Deps As String = If(If(FmlObject("dependencies") IsNot Nothing, FmlObject("dependencies")("value"), ""), "")
|
||||
If Deps <> "" Then
|
||||
For Each Dep In Deps.Split(";")
|
||||
If Dep = "" OrElse Not Dep.StartsWith("required-") Then Continue For
|
||||
Dep = Dep.Substring(Dep.IndexOf(":") + 1)
|
||||
If Dep.Contains("@") Then
|
||||
AddDependency(Dep.Split("@")(0), Dep.Split("@")(1))
|
||||
Else
|
||||
AddDependency(Dep)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "读取 fml_cache_annotation.json 时出现未知错误(" & Path & ")", LogLevel.Developer)
|
||||
End Try
|
||||
#End Region
|
||||
Finished:
|
||||
#Region "将 Version 代号转换为 META-INF 中的版本"
|
||||
If _Version = "version" Then
|
||||
Try
|
||||
Dim MetaEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/MANIFEST.MF")
|
||||
If MetaEntry IsNot Nothing Then
|
||||
Dim MetaString As String = ReadFile(MetaEntry.Open()).Replace(" :", ":").Replace(": ", ":")
|
||||
If MetaString.Contains("Implementation-Version:") Then
|
||||
MetaString = MetaString.Substring(MetaString.IndexOf("Implementation-Version:") + "Implementation-Version:".Count)
|
||||
MetaString = MetaString.Substring(0, MetaString.IndexOfAny(vbCrLf.ToCharArray)).Trim
|
||||
Version = MetaString
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log("获取 META-INF 中的版本信息失败(" & Path & ")", LogLevel.Developer)
|
||||
Version = Nothing
|
||||
End Try
|
||||
End If
|
||||
If _Version IsNot Nothing AndAlso Not (_Version.Contains(".") OrElse _Version.Contains("-")) Then Version = Nothing
|
||||
#End Region
|
||||
|
||||
IsInfoWithoutClassAvailable = _ModId IsNot Nothing AndAlso _Version IsNot Nothing
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 进行使用 .class 文件的信息获取。
|
||||
''' </summary>
|
||||
Private Sub LoadWithClass(Jar As ZipArchive)
|
||||
Try
|
||||
'查找入口点文件
|
||||
Dim ModClass As String = Nothing
|
||||
Dim ModClassNotBest As String = Nothing '非完美匹配
|
||||
For Each Entry In Jar.Entries
|
||||
If Entry.Name.EndsWith(".class") Then
|
||||
Dim Temp As String = ReadFile(Entry.Open()).ToLower
|
||||
If Temp.Contains("#lnet/minecraftforge/fml/common/mod;") Then
|
||||
ModClass = Temp
|
||||
Exit For
|
||||
ElseIf Temp.Contains("modid") Then
|
||||
ModClassNotBest = Temp
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
If ModClass Is Nothing Then ModClass = ModClassNotBest
|
||||
If ModClass Is Nothing Then Throw New FileNotFoundException("未找到 Mod 入口点")
|
||||
ModClass = ModClass.Replace("ljava/lang/string;", "")
|
||||
If ModClass.Count > 3000 Then ModClass = ModClass.Substring(0, 3000) '如果文件过大,截取前 3000 Byte
|
||||
Dim IndexHead As Integer, IndexTail As Integer, IncreaseCount As Integer
|
||||
'获取 ModID
|
||||
If _ModId Is Nothing Then
|
||||
IndexHead = ModClass.IndexOf("modid") + "modid".Length + 1
|
||||
IncreaseCount = 0
|
||||
Do While Convert.ToInt32(ModClass(IndexHead)) < 32
|
||||
IndexHead += 1
|
||||
IncreaseCount += 1
|
||||
If IncreaseCount > 10 Then Throw New Exception("ModID 头匹配失败")
|
||||
Loop
|
||||
IndexTail = IndexHead + 1
|
||||
IncreaseCount = 0
|
||||
Do Until Convert.ToInt32(ModClass(IndexTail)) < 32
|
||||
IndexTail += 1
|
||||
IncreaseCount += 1
|
||||
If IncreaseCount > 50 Then Throw New Exception("ModID 尾匹配失败")
|
||||
Loop
|
||||
ModId = ModClass.Substring(IndexHead, IndexTail - IndexHead)
|
||||
End If
|
||||
'获取 Version
|
||||
If _Version Is Nothing AndAlso ModClass.Contains("version") Then
|
||||
IndexHead = ModClass.IndexOf("version") + "version".Length + 1
|
||||
IncreaseCount = 0
|
||||
Do While Convert.ToInt32(ModClass(IndexHead)) < 32
|
||||
IndexHead += 1
|
||||
IncreaseCount += 1
|
||||
If IncreaseCount > 10 Then GoTo VersionFindFail
|
||||
Loop
|
||||
IndexTail = IndexHead + 1
|
||||
IncreaseCount = 0
|
||||
Do While ModClass(IndexTail) = "."c OrElse ModClass(IndexTail) = "-"c OrElse
|
||||
(ModClass(IndexTail) >= "0"c AndAlso ModClass(IndexTail) <= "9"c) OrElse
|
||||
(ModClass(IndexTail) >= "a"c AndAlso ModClass(IndexTail) <= "z"c)
|
||||
IndexTail += 1
|
||||
IncreaseCount += 1
|
||||
If IncreaseCount > 50 Then GoTo VersionFindFail
|
||||
Loop
|
||||
_Version = ModClass.Substring(IndexHead, IndexTail - IndexHead)
|
||||
End If
|
||||
VersionFindFail:
|
||||
'获取 Dependencies
|
||||
IndexHead = ModClass.IndexOf("dependencies")
|
||||
If IndexHead > 0 Then
|
||||
If ModClass.Count >= IndexHead + 300 Then ModClass = ModClass.Substring(IndexHead, 299)
|
||||
Dim Deps As List(Of String) = RegexSearch(ModClass, "(?<=required-((before|after|before-client|after-client)?):)[0-9a-z]+(@[\(\[]{1}[0-9.,]+[\)\]]{1})?")
|
||||
For Each Token As String In Deps
|
||||
If Not String.IsNullOrEmpty(Token) Then
|
||||
If Token.Contains("@") Then
|
||||
AddDependency(Token.Split("@")(0), Token.Split("@")(1))
|
||||
Else
|
||||
AddDependency(Token)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "Mod Class 信息不可用(" & If(Path, "null") & ")", LogLevel.Normal)
|
||||
End Try
|
||||
IsInfoWithClassAvailable = _ModId IsNot Nothing AndAlso _Version IsNot Nothing
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
''' <summary>
|
||||
''' 是否可能为前置 Mod。
|
||||
''' </summary>
|
||||
Public Function IsPresetMod() As Boolean
|
||||
Return Dependencies.Count = 0 AndAlso (Name IsNot Nothing) AndAlso (Name.ToLower.Contains("core") OrElse Name.ToLower.Contains("lib"))
|
||||
End Function
|
||||
|
||||
''' <summary>
|
||||
''' 根据完整文件路径的文件扩展名判断是否为 Mod 文件。
|
||||
''' </summary>
|
||||
Public Shared Function IsModFile(Path As String)
|
||||
If Path Is Nothing OrElse Not Path.Contains(".") Then Return False
|
||||
Path = Path.ToLower
|
||||
If Path.EndsWith(".jar") OrElse Path.EndsWith(".zip") OrElse Path.EndsWith(".litemod") OrElse
|
||||
Path.EndsWith(".jar.disabled") OrElse Path.EndsWith(".zip.disabled") OrElse Path.EndsWith(".litemod.disabled") Then Return True
|
||||
Return False
|
||||
End Function
|
||||
|
||||
End Class
|
||||
|
||||
'加载 Mod 列表
|
||||
Public McModLoader As New LoaderTask(Of String, List(Of McMod))("Mod List Loader", AddressOf McModLoad)
|
||||
Private Sub McModLoad(Loader As LoaderTask(Of String, List(Of McMod)))
|
||||
Try
|
||||
RunInUiWait(Sub() If FrmVersionMod IsNot Nothing Then FrmVersionMod.Load.ShowProgress = False)
|
||||
|
||||
'获取 Mod 文件夹下的可用文件列表
|
||||
Dim ModFileList As New List(Of FileInfo)
|
||||
If Directory.Exists(Loader.Input) Then
|
||||
Dim RawName As String = Loader.Input.ToLower
|
||||
For Each File As FileInfo In EnumerateFiles(Loader.Input)
|
||||
If File.DirectoryName.ToLower & "\" <> RawName Then
|
||||
'仅当 Forge 1.13- 且文件夹名与版本号相同时,才加载该子文件夹下的 Mod
|
||||
If Not (PageVersionLeft.Version IsNot Nothing AndAlso PageVersionLeft.Version.Version.HasForge AndAlso
|
||||
PageVersionLeft.Version.Version.McCodeMain < 13 AndAlso
|
||||
File.Directory.Name = "1." & PageVersionLeft.Version.Version.McCodeMain & "." & PageVersionLeft.Version.Version.McCodeSub) Then
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
If McMod.IsModFile(File.FullName) Then ModFileList.Add(File)
|
||||
Next
|
||||
End If
|
||||
|
||||
'确定是否显示进度
|
||||
Loader.Progress = 0.03
|
||||
If ModFileList.Count > 100 Then RunInUi(Sub() If FrmVersionMod IsNot Nothing Then FrmVersionMod.Load.ShowProgress = True)
|
||||
|
||||
'加载为 Mod 列表
|
||||
Dim ModList As New List(Of McMod)
|
||||
For Each ModFile As FileInfo In ModFileList
|
||||
Dim ModEntity As New McMod(ModFile.FullName)
|
||||
ModEntity.Load()
|
||||
ModList.Add(ModEntity)
|
||||
Loader.Progress += 0.9 / ModFileList.Count
|
||||
Next
|
||||
Loader.Progress = 0.93
|
||||
|
||||
'排序
|
||||
ModList = Sort(ModList, Function(Left As McMod, Right As McMod) As Boolean
|
||||
If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
|
||||
Return Left.State = McMod.McModState.Unavaliable
|
||||
Else
|
||||
Return Not Right.FileName.CompareTo(Left.FileName)
|
||||
End If
|
||||
End Function)
|
||||
Loader.Progress = 0.98
|
||||
|
||||
'回设
|
||||
If Loader.IsAborted Then Exit Sub
|
||||
Loader.Output = ModList
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "Mod 列表加载失败", LogLevel.Debug)
|
||||
Throw
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
#If DEBUG Then
|
||||
''' <summary>
|
||||
''' 检查 Mod 列表中存在的错误,返回错误信息的集合。
|
||||
''' </summary>
|
||||
Public Function McModCheck(Version As McVersion, Mods As List(Of McMod)) As List(Of String)
|
||||
Dim Result As New List(Of String)
|
||||
'令所有 Mod 进行基础检查,并归纳需要检查的 Mod
|
||||
Dim CurrentModList As New List(Of McMod)
|
||||
For Each ModEntity In Mods
|
||||
If Not ModEntity.IsFileAvailable Then
|
||||
Result.Add("无法读取的 Mod 文件。" & vbCrLf & " - " & ModEntity.Path)
|
||||
Continue For
|
||||
End If
|
||||
If ModEntity.State = McMod.McModState.Fine AndAlso ModEntity.ModId IsNot Nothing Then CurrentModList.Add(ModEntity)
|
||||
Next
|
||||
'添加默认依赖
|
||||
Dim CurrentDependencies As New Dictionary(Of String, String()) '{DependencyVersion, Path}
|
||||
If Version.State = McVersionState.Forge Then CurrentDependencies.Add("forge", {Version.Version.ForgeVersion, "Forge"})
|
||||
CurrentDependencies.Add("minecraft", {Version.Version.McName, "Minecraft"})
|
||||
'检查重复的 Mod,并添加对应的依赖
|
||||
For Each ModEntity In CurrentModList
|
||||
For Each PossibleModId In ModEntity.PossibleModId
|
||||
If CurrentDependencies.ContainsKey(PossibleModId) Then
|
||||
If CurrentDependencies(PossibleModId)(2) = 1 Then
|
||||
Result.Add("重复添加了相同的 Mod,请尝试删除其中一个(ModID:" & PossibleModId & ")。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & vbCrLf &
|
||||
" - " & CurrentDependencies(PossibleModId)(1))
|
||||
Else
|
||||
Log("[Minecraft] 由于可能有多个 ModID,跳过疑似的重复项(ModID:" & PossibleModId & ")。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & vbCrLf &
|
||||
" - " & CurrentDependencies(PossibleModId)(1), LogLevel.Developer)
|
||||
End If
|
||||
Else
|
||||
CurrentDependencies.Add(PossibleModId, {ModEntity.Version, ModEntity.FileName, ModEntity.PossibleModId.Count})
|
||||
End If
|
||||
Next
|
||||
Next
|
||||
'检查依赖
|
||||
For Each ModEntity In CurrentModList
|
||||
Try
|
||||
For Each Dependency In ModEntity.Dependencies
|
||||
Dim ReqId As String = Dependency.Key
|
||||
If ReqId.Count < 2 Then Continue For '确保正常
|
||||
If ReqId = ModEntity.ModId Then Continue For '跳过自体引用
|
||||
If ReqId = "forgemultipartcbe" Then Continue For '跳过莫名其妙的引用
|
||||
If Dependency.Value IsNot Nothing Then
|
||||
'获取分段后的详细版本信息
|
||||
Dim ReqVersion As String = Dependency.Value
|
||||
Dim ReqVersionHeadCanEqual As Boolean = ReqVersion.StartsWith("[")
|
||||
Dim ReqVersionTailCanEqual As Boolean = ReqVersion.EndsWith("]")
|
||||
Dim ReqVersionHead As String
|
||||
Dim ReqVersionTail As String
|
||||
If ReqVersion.Contains(",") Then
|
||||
ReqVersionHead = ReqVersion.Split(",")(0).Trim("([ ".ToCharArray())
|
||||
ReqVersionTail = ReqVersion.Split(",")(1).Trim("]) ".ToCharArray())
|
||||
Else
|
||||
ReqVersionHead = ReqVersion.Trim("([]) ".ToCharArray())
|
||||
ReqVersionTail = ReqVersionHead
|
||||
If ReqId = "minecraft" AndAlso ReqVersionHead.Split(".").Count = 2 Then
|
||||
ReqVersionTail = ReqVersionHead.Split(".")(0) & "." & (Val(ReqVersionHead.Split(".")(1)) + 1)
|
||||
ReqVersionTailCanEqual = False
|
||||
End If
|
||||
End If
|
||||
If ReqVersionHead.StartsWith("1.") AndAlso ReqVersionHead.Contains("-") Then ReqVersionHead = ReqVersionHead.Substring(ReqVersionHead.LastIndexOf("-") + 1)
|
||||
If ReqVersionTail.StartsWith("1.") AndAlso ReqVersionTail.Contains("-") Then ReqVersionTail = ReqVersionTail.Substring(ReqVersionTail.LastIndexOf("-") + 1)
|
||||
'获取报错描述文本
|
||||
Dim VersionRequire As String
|
||||
If ReqVersionHead = ReqVersionTail Then
|
||||
VersionRequire = "应为 " & ReqVersionHead
|
||||
ElseIf ReqVersionHead.Contains(".") AndAlso ReqVersionTail.Contains(".") Then
|
||||
VersionRequire = "应为 " & ReqVersionHead & " 至 " & ReqVersionTail
|
||||
ElseIf ReqVersionHead.Contains(".") Then
|
||||
If ReqVersionHeadCanEqual Then
|
||||
VersionRequire = "最低应为 " & ReqVersionHead
|
||||
Else
|
||||
VersionRequire = "应高于 " & ReqVersionHead
|
||||
End If
|
||||
ElseIf ReqVersionTail.Contains(".") Then
|
||||
If ReqVersionTailCanEqual Then
|
||||
VersionRequire = "最高应为 " & ReqVersionHead
|
||||
Else
|
||||
VersionRequire = "应低于 " & ReqVersionHead
|
||||
End If
|
||||
Else
|
||||
VersionRequire = ""
|
||||
End If
|
||||
'检查前置 Mod 是否存在,并获取其版本
|
||||
If Not CurrentDependencies.ContainsKey(ReqId) Then
|
||||
Result.Add("缺少前置 Mod:" & ReqId & If(VersionRequire = "", "", ",其版本" & VersionRequire) & "。" & vbCrLf & " - " & ModEntity.FileName)
|
||||
Continue For
|
||||
End If
|
||||
Dim CurrentVersion As String = If(CurrentDependencies(ReqId)(0), "0.0")
|
||||
If CurrentVersion.StartsWith("1.") AndAlso CurrentVersion.Contains("-") Then CurrentVersion = CurrentVersion.Substring(CurrentVersion.LastIndexOf("-") + 1)
|
||||
'对比前置 Mod 头部版本
|
||||
If ReqVersionHead.Contains(".") Then
|
||||
If VersionSortInteger(ReqVersionHead, CurrentVersion) > If(ReqVersionHeadCanEqual, 0, -1) Then
|
||||
Result.Add(ReqId.Substring(0, 1).ToUpper & ReqId.Substring(1) & " 版本过低,其版本" & VersionRequire & ",而当前版本为 " & CurrentVersion & "。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", vbCrLf & " - 前置:" & CurrentDependencies(ReqId)(1), ""))
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
'对比前置 Mod 尾部版本
|
||||
If ReqVersionTail.Contains(".") Then
|
||||
If VersionSortInteger(CurrentVersion, ReqVersionTail) > If(ReqVersionTailCanEqual, 0, -1) Then
|
||||
Result.Add(ReqId.Substring(0, 1).ToUpper & ReqId.Substring(1) & " 版本过高,其版本" & VersionRequire & ",而当前版本为 " & CurrentVersion & "。" & vbCrLf &
|
||||
" - " & ModEntity.FileName & If(ReqId <> "minecraft" AndAlso ReqId <> "forge", vbCrLf & " - 前置:" & CurrentDependencies(ReqId)(1), ""))
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
If Not CurrentDependencies.ContainsKey(Dependency.Key) Then
|
||||
Result.Add("缺少前置 Mod:" & Dependency.Key & "。" & vbCrLf & " - " & ModEntity.FileName)
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Result.Add("检查 Mod 时出错:" & GetExceptionSummary(ex) & vbCrLf & " - " & ModEntity.FileName)
|
||||
Log(ex, "检查 Mod 时出错")
|
||||
End Try
|
||||
Next
|
||||
If Result.Count = 0 Then
|
||||
Log("[Minecraft] Mod 检查未发现异常")
|
||||
Else
|
||||
Log("[Minecraft] Mod 检查异常结果:" & vbCrLf & Join(Result, vbCrLf))
|
||||
End If
|
||||
Return Result
|
||||
End Function
|
||||
#End If
|
||||
|
||||
End Module
|
||||
@@ -1,4 +1,6 @@
|
||||
Public Module ModModpack
|
||||
Imports System.IO.Compression
|
||||
|
||||
Public Module ModModpack
|
||||
|
||||
'触发整合包安装的外部接口
|
||||
''' <summary>
|
||||
@@ -14,13 +16,13 @@
|
||||
''' </summary>
|
||||
Public Function ModpackInstall(File As String, Optional VersionName As String = Nothing, Optional ShowHint As Boolean = True, Optional Logo As String = Nothing) As Boolean
|
||||
Log("[ModPack] 整合包安装请求:" & If(File, "null"))
|
||||
Dim Archive As Compression.ZipArchive = Nothing
|
||||
Dim Archive As ZipArchive = Nothing
|
||||
Dim ArchiveBaseFolder As String = ""
|
||||
Try
|
||||
'获取整合包种类与关键 Json
|
||||
Dim PackType As Integer = -1
|
||||
Try
|
||||
Archive = New Compression.ZipArchive(New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
Archive = New ZipArchive(New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
'从根目录判断整合包类型
|
||||
If Archive.GetEntry("mcbbs.packmeta") IsNot Nothing Then PackType = 3 : Exit Try 'MCBBS 整合包(优先于 manifest.json 判断)
|
||||
If Archive.GetEntry("modrinth.index.json") IsNot Nothing Then PackType = 4 : Exit Try 'Modrinth 整合包
|
||||
@@ -109,7 +111,7 @@
|
||||
'整合包缓存清理
|
||||
Private IsInstallCacheCleared 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
|
||||
IsInstallCacheCleared = True
|
||||
@@ -135,9 +137,8 @@
|
||||
Try
|
||||
Retry:
|
||||
'完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试
|
||||
Directory.CreateDirectory(InstallTemp)
|
||||
DeleteDirectory(InstallTemp)
|
||||
Compression.ZipFile.ExtractToDirectory(FileAddress, InstallTemp, Encode)
|
||||
ExtractFile(FileAddress, InstallTemp, Encode)
|
||||
Catch ex As Exception
|
||||
Log(ex, "第 " & RetryCount & " 次解压尝试失败")
|
||||
If TypeOf ex Is ArgumentException Then
|
||||
@@ -146,6 +147,7 @@ Retry:
|
||||
End If
|
||||
If RetryCount < 5 Then
|
||||
Thread.Sleep(RetryCount * 2000)
|
||||
If Loader IsNot Nothing AndAlso Loader.LoadingState <> MyLoading.MyLoadingState.Run Then Exit Sub
|
||||
RetryCount += 1
|
||||
GoTo Retry
|
||||
Else
|
||||
@@ -215,13 +217,15 @@ Retry:
|
||||
If OverrideHome <> "" Then
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
Dim OverridePath As String = InstallTemp & ArchiveBaseFolder & OverrideHome
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & OverrideHome) Then
|
||||
CopyDirectory(InstallTemp & ArchiveBaseFolder & OverrideHome, PathMcFolder & "versions\" & VersionName)
|
||||
If Directory.Exists(OverridePath) Then
|
||||
CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName)
|
||||
Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}")
|
||||
Else
|
||||
Log("[ModPack] 整合包中未找到 override 目录,已跳过")
|
||||
Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}")
|
||||
End If
|
||||
Task.Progress = 0.9
|
||||
'开启版本隔离
|
||||
@@ -230,54 +234,67 @@ Retry:
|
||||
.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
End If
|
||||
'获取 Mod 列表
|
||||
Dim ModFileList As New List(Of Integer)
|
||||
Dim ModList As New List(Of Integer)
|
||||
Dim ModOptionalList As New List(Of Integer)
|
||||
For Each ModEntry In If(Json("files"), {})
|
||||
If ModEntry("projectID") Is Nothing OrElse ModEntry("fileID") Is Nothing Then
|
||||
Hint("某项 Mod 缺少必要信息,已跳过:" & ModEntry.ToString)
|
||||
Continue For
|
||||
End If
|
||||
If ModEntry("required") IsNot Nothing AndAlso Not ModEntry("required").ToObject(Of Boolean) Then Continue For
|
||||
ModFileList.Add(ModEntry("fileID"))
|
||||
ModList.Add(ModEntry("fileID"))
|
||||
If ModEntry("required") IsNot Nothing AndAlso Not ModEntry("required").ToObject(Of Boolean) Then ModOptionalList.Add(ModEntry("fileID"))
|
||||
Next
|
||||
If ModFileList.Count > 0 Then
|
||||
If ModList.Count > 0 Then
|
||||
'获取 Mod 下载信息
|
||||
InstallLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of Integer, JArray))
|
||||
Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModFileList, ",") & "]}", "application/json"))("data")
|
||||
Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModList, ",") & "]}", "application/json"))("data")
|
||||
'如果文件已被删除,则 API 会跳过那一项
|
||||
If ModFileList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
|
||||
End Sub) With {.ProgressWeight = ModFileList.Count / 10}) '每 10 Mod 需要 1s
|
||||
If ModList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 10}) '每 10 Mod 需要 1s
|
||||
'构造 NetFile
|
||||
InstallLoaders.Add(New LoaderTask(Of JArray, List(Of NetFile))("构造 Mod 下载信息",
|
||||
Sub(Task As LoaderTask(Of JArray, List(Of NetFile)))
|
||||
Dim FileList As New Dictionary(Of Integer, NetFile)
|
||||
For Each ModJson In Task.Input
|
||||
Dim Id As Integer = ModJson("id").ToObject(Of Integer)
|
||||
'跳过重复的 Mod(疑似 CurseForge Bug)
|
||||
If FileList.ContainsKey(ModJson("id").ToObject(Of Integer)) Then Continue For
|
||||
'根据 modules 判断是资源包还是 Mod
|
||||
If FileList.ContainsKey(Id) Then Continue For
|
||||
'可选 Mod 提示
|
||||
If ModOptionalList.Contains(Id) Then
|
||||
If MyMsgBox("是否要下载整合包中的可选文件 " & ModJson("displayName").ToString & "?",
|
||||
"下载可选文件", "是", "否") = 2 Then
|
||||
Continue For
|
||||
End If
|
||||
End If
|
||||
'建立 CompFile
|
||||
Dim File As New CompFile(ModJson, CompType.Mod)
|
||||
If Not File.Available Then Continue For
|
||||
'根据 modules 和文件名后缀判断是资源包还是 Mod
|
||||
Dim IsResourcePack As Boolean
|
||||
If ModJson("modules").Any Then 'modules 可能返回 null(#1006)
|
||||
Dim ModuleNames = CType(ModJson("modules"), JArray).Select(Function(l) l("name").ToString).ToList
|
||||
IsResourcePack = Not ModuleNames.Contains("META-INF") AndAlso ModuleNames.Contains("pack.mcmeta")
|
||||
IsResourcePack =
|
||||
(Not ModuleNames.Contains("META-INF")) AndAlso (Not ModuleNames.Contains("mcmod.info")) AndAlso '不包含 META-INF 或 mcmod.info
|
||||
ModuleNames.Contains("pack.mcmeta") AndAlso '包含 pack.mcmeta
|
||||
(Not File.FileName.EndsWith(".jar")) '文件后缀不是 .jar
|
||||
Else
|
||||
IsResourcePack = False
|
||||
End If
|
||||
'实际的添加
|
||||
Dim File As New CompFile(ModJson, CompType.Mod)
|
||||
If Not File.Available Then Continue For
|
||||
FileList.Add(ModJson("id"),
|
||||
File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{If(IsResourcePack, "resourcepacks", "mods")}\"))
|
||||
Task.Progress += 1 / (1 + ModFileList.Count)
|
||||
FileList.Add(Id, File.ToNetFile($"{PathMcFolder}versions\{VersionName}\{If(IsResourcePack, "resourcepacks", "mods")}\"))
|
||||
Task.Progress += 1 / (1 + ModList.Count)
|
||||
Next
|
||||
Task.Output = FileList.Values.ToList
|
||||
End Sub) With {.ProgressWeight = ModFileList.Count / 200, .Show = False}) '每 200 Mod 需要 1s
|
||||
End Sub) With {.ProgressWeight = ModList.Count / 200, .Show = False}) '每 200 Mod 需要 1s
|
||||
'下载 Mod 文件
|
||||
InstallLoaders.Add(New LoaderDownload("下载 Mod", New List(Of NetFile)) With {.ProgressWeight = ModFileList.Count * 1.5}) '每个 Mod 需要 1.5s
|
||||
InstallLoaders.Add(New LoaderDownload("下载 Mod", New List(Of NetFile)) With {.ProgressWeight = ModList.Count * 1.5}) '每个 Mod 需要 1.5s
|
||||
End If
|
||||
|
||||
'构造加载器
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Json("minecraft")("version").ToString,
|
||||
.ForgeVersion = ForgeVersion,
|
||||
.FabricVersion = FabricVersion
|
||||
@@ -333,7 +350,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
If ShowRibble Then FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -388,7 +405,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制 overrides 文件夹和 client-overrides 文件夹
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
|
||||
@@ -420,7 +437,7 @@ Retry:
|
||||
End Select
|
||||
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,
|
||||
New FileChecker(ActualSize:=File("fileSize").ToObject(Of Long), Hash:=File("hashes")("sha1").ToString)))
|
||||
Next
|
||||
@@ -431,6 +448,7 @@ Retry:
|
||||
'构造加载器
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = MinecraftVersion,
|
||||
.ForgeVersion = ForgeVersion,
|
||||
.FabricVersion = FabricVersion
|
||||
@@ -486,7 +504,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
If ShowRibble Then FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -514,7 +532,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
|
||||
@@ -533,6 +551,7 @@ Retry:
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Json("gameVersion").ToString
|
||||
}
|
||||
Dim InstallExpectTime As Double = 0
|
||||
@@ -584,7 +603,7 @@ Retry:
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -607,12 +626,13 @@ Retry:
|
||||
If Validate.Validate(PackName) <> "" Then PackName = ""
|
||||
Dim VersionName As String = MyMsgBoxInput("输入版本名称", "", PackName, New ObjectModel.Collection(Of Validate) From {Validate})
|
||||
If VersionName Is Nothing Then Exit Sub
|
||||
'解压与配置文件
|
||||
Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
|
||||
'解压、配置设置文件
|
||||
Dim InstallTemp As String = $"{PathTemp}PackInstall\{RandomInteger(0, 100000)}\"
|
||||
Dim SetupFile As String = $"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini"
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then
|
||||
@@ -622,14 +642,52 @@ Retry:
|
||||
End If
|
||||
Task.Progress = 0.9
|
||||
'开启版本隔离
|
||||
WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
|
||||
WriteIni(SetupFile, "VersionArgumentIndie", 1)
|
||||
'读取 MMC 设置文件(#2655)
|
||||
Try
|
||||
Dim MMCSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
|
||||
If File.Exists(MMCSetupFile) Then
|
||||
'将其中的等号替换为冒号,以符合 ini 文件格式
|
||||
WriteFile(MMCSetupFile, ReadFile(MMCSetupFile).Replace("=", ":"))
|
||||
If ReadIni(MMCSetupFile, "OverrideCommands", False) Then
|
||||
Dim PreLaunchCommand As String = ReadIni(MMCSetupFile, "PreLaunchCommand")
|
||||
If PreLaunchCommand <> "" Then
|
||||
PreLaunchCommand = PreLaunchCommand.Replace("\""", """").
|
||||
Replace("$INST_JAVA", "{java}javaw.exe").
|
||||
Replace("$INST_MC_DIR\", "{minecraft}").Replace("$INST_MC_DIR", "{minecraft}").
|
||||
Replace("$INST_DIR\", "{verpath}").Replace("$INST_DIR", "{verpath}").
|
||||
Replace("$INST_ID", "{name}").Replace("$INST_NAME", "{name}")
|
||||
WriteIni(SetupFile, "VersionAdvanceRun", PreLaunchCommand)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:启动前执行命令:" & PreLaunchCommand)
|
||||
End If
|
||||
End If
|
||||
If ReadIni(MMCSetupFile, "JoinServerOnLaunch", False) Then
|
||||
Dim ServerAddress As String = ReadIni(MMCSetupFile, "JoinServerOnLaunchAddress").Replace("\""", """")
|
||||
WriteIni(SetupFile, "VersionServerEnter", ServerAddress)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:自动进入服务器:" & ServerAddress)
|
||||
End If
|
||||
If ReadIni(MMCSetupFile, "IgnoreJavaCompatibility", False) Then
|
||||
WriteIni(SetupFile, "VersionAdvanceJava", True)
|
||||
Log("[ModPack] 迁移 MultiMC 版本独立设置:忽略 Java 兼容性警告")
|
||||
End If
|
||||
Dim Logo As String = ReadIni(MMCSetupFile, "iconKey", "")
|
||||
If Logo <> "" AndAlso File.Exists($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png") Then
|
||||
WriteIni(SetupFile, "LogoCustom", True)
|
||||
WriteIni(SetupFile, "Logo", "PCL\Logo.png")
|
||||
CopyFile($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png", $"{PathMcFolder}versions\{VersionName}\PCL\Logo.png")
|
||||
Log($"[ModPack] 迁移 MultiMC 版本独立设置:版本图标({Logo}.png)")
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, $"读取 MMC 配置文件失败({InstallTemp}{ArchiveBaseFolder}instance.cfg)")
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
|
||||
'构造版本安装请求
|
||||
If PackJson("components") Is Nothing Then
|
||||
Hint("该整合包未提供游戏版本信息,无法安装!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {.TargetVersionName = VersionName}
|
||||
Dim Request As New McInstallRequest With {.TargetVersionName = VersionName, .TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\"}
|
||||
For Each Component In PackJson("components")
|
||||
Select Case If(Component("uid"), "").ToString
|
||||
Case "org.lwjgl"
|
||||
@@ -640,6 +698,9 @@ Retry:
|
||||
Request.ForgeVersion = Component("version")
|
||||
Case "net.fabricmc.fabric-loader"
|
||||
Request.FabricVersion = Component("version")
|
||||
Case "org.quiltmc.quilt-loader" 'eg. 1.0.0
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Exit Sub
|
||||
End Select
|
||||
Next
|
||||
'构造加载器
|
||||
@@ -676,7 +737,7 @@ Retry:
|
||||
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -704,7 +765,7 @@ Retry:
|
||||
Dim InstallLoaders As New List(Of LoaderBase)
|
||||
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
|
||||
Sub(Task As LoaderTask(Of String, Integer))
|
||||
UnpackFiles(InstallTemp, FileAddress)
|
||||
UnpackFiles(InstallTemp, FileAddress, Task)
|
||||
Task.Progress = 0.5
|
||||
'复制结果
|
||||
If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
|
||||
@@ -729,8 +790,13 @@ Retry:
|
||||
Hint("该整合包未提供游戏版本信息,无法安装!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
If Addons.ContainsKey("quilt") Then
|
||||
Hint("PCL 暂不支持安装需要 Quilt 的整合包!", HintType.Critical)
|
||||
Exit Sub
|
||||
End If
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = VersionName,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{VersionName}\",
|
||||
.MinecraftName = Addons("game"),
|
||||
.OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing),
|
||||
.ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing),
|
||||
@@ -770,7 +836,7 @@ Retry:
|
||||
'启动
|
||||
Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
|
||||
Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -790,7 +856,7 @@ Retry:
|
||||
If String.IsNullOrWhiteSpace(NewName) Then Exit Sub
|
||||
'解压
|
||||
Hint("正在解压压缩包……")
|
||||
UnpackFiles(TargetFolder, FileAddress)
|
||||
UnpackFiles(TargetFolder, FileAddress, Nothing)
|
||||
'加入文件夹列表
|
||||
PageSelectLeft.AddFolder(TargetFolder, NewName, False)
|
||||
Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish)
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
Get
|
||||
Return _State
|
||||
End Get
|
||||
Set(ByVal value As MinecraftState)
|
||||
Set(value As MinecraftState)
|
||||
If _State = value Then Exit Property
|
||||
_State = value
|
||||
WatcherStateChanged()
|
||||
@@ -272,7 +272,7 @@
|
||||
If Not MinecraftWindowName.StartsWith("FML") Then
|
||||
'已找到 Minecraft 窗口
|
||||
WindowHandle = MinecraftWindowHandle
|
||||
WatcherLog("Minecraft 窗口已加载:" & MinecraftWindowName & "(" & MinecraftWindowHandle.ToInt64 & ")")
|
||||
WatcherLog($"Minecraft 窗口已加载:{MinecraftWindowName}({MinecraftWindowHandle.ToInt64})")
|
||||
IsWindowFinished = True
|
||||
'最大化
|
||||
If Setup.Get("LaunchArgumentWindowType") = 4 Then
|
||||
@@ -282,6 +282,7 @@
|
||||
'……虽然我很想这样说,但总有人反馈,算了
|
||||
Thread.Sleep(2000)
|
||||
ShowWindow(WindowHandle, 3)
|
||||
WatcherLog($"已最大化 Minecraft 窗口:{MinecraftWindowHandle.ToInt64}")
|
||||
Catch ex As Exception
|
||||
Log(ex, "最大化 Minecraft 窗口时出现错误")
|
||||
End Try
|
||||
|
||||
@@ -94,6 +94,18 @@ RetryStart:
|
||||
Log("[Control] 按下资源工程列表项:" & LabTitle.Text)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ProjectClick(sender As MyCompItem, e As EventArgs) Handles Me.Click
|
||||
'记录当前展开的卡片标题(#2712)
|
||||
Dim Titles As New List(Of String)
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.CompDetail Then
|
||||
For Each Card As MyCard In FrmDownloadCompDetail.PanMain.Children
|
||||
If Card.Title <> "" AndAlso Not Card.IsSwaped Then Titles.Add(Card.Title)
|
||||
Next
|
||||
Log("[Comp] 记录当前已展开的卡片:" & String.Join("、", Titles))
|
||||
FrmMain.PageCurrent.Additional(1) = Titles
|
||||
End If
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, .Additional = {sender.Tag, New List(Of String)}})
|
||||
End Sub
|
||||
|
||||
'鼠标点击判定
|
||||
Private IsMouseDown As Boolean = False
|
||||
|
||||
@@ -13,21 +13,19 @@
|
||||
|
||||
Case "打开网页"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not (Data(0).StartsWith("http://") OrElse Data(0).StartsWith("https://")) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "如果想要启动程序,请将 EventType 改为 打开文件。", "事件执行失败")
|
||||
If Not Data(0).Contains("://") OrElse Data(0).ToLower.StartsWith("file") Then '为了支持更多协议(#2200)
|
||||
MyMsgBox("EventData 必须为一个网址。" & vbCrLf & "如果想要启动程序,请将 EventType 改为 打开文件。", "事件执行失败")
|
||||
Exit Sub
|
||||
End If
|
||||
Hint("正在打开网页,请稍候……")
|
||||
Hint("正在开启中,请稍候……")
|
||||
OpenWebsite(Data(0))
|
||||
|
||||
Case "打开文件", "打开帮助"
|
||||
RunInThread(Sub()
|
||||
Try
|
||||
|
||||
'确认实际路径
|
||||
Dim ActualPaths = GetEventAbsoluteUrls(Data(0), Type)
|
||||
Dim Location = ActualPaths(0), WorkingDir = ActualPaths(1)
|
||||
|
||||
'执行
|
||||
If Type = "打开文件" Then
|
||||
Dim Info As New ProcessStartInfo With {
|
||||
@@ -39,13 +37,14 @@
|
||||
Else '打开帮助
|
||||
PageOtherHelp.EnterHelpPage(Location)
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "执行打开类自定义事件失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub)
|
||||
|
||||
Case "启动游戏"
|
||||
If McLaunchStart(New McLaunchOptions With {.ServerIp = If(Data.Length >= 2, Data(1), Nothing), .Version = New McVersion(Data(0))}) Then
|
||||
If McLaunchStart(New McLaunchOptions With
|
||||
{.ServerIp = If(Data.Length >= 2, Data(1), Nothing), .Version = New McVersion(Data(0))}) Then
|
||||
Hint("正在启动 " & Data(0) & "……")
|
||||
End If
|
||||
|
||||
@@ -54,6 +53,7 @@
|
||||
|
||||
Case "刷新主页"
|
||||
FrmLaunchRight.ForceRefresh()
|
||||
If Data(0) = "" Then Hint("已刷新主页!", HintType.Finish)
|
||||
|
||||
Case "刷新帮助"
|
||||
PageOtherLeft.RefreshHelp()
|
||||
@@ -65,15 +65,21 @@
|
||||
RunInThread(Sub() PageOtherTest.MemoryOptimize(True))
|
||||
|
||||
Case "清理垃圾"
|
||||
PageOtherTest.RubbishClear()
|
||||
RunInThread(Sub() PageOtherTest.RubbishClear())
|
||||
|
||||
Case "弹出窗口"
|
||||
MyMsgBox(Data(1).Replace("\n", vbCrLf), Data(0).Replace("\n", vbCrLf))
|
||||
|
||||
Case "切换页面"
|
||||
FrmMain.PageChange(Val(Data(0)), Val(Data(1)))
|
||||
|
||||
Case "导入整合包"
|
||||
RunInThread(Sub() ModpackInstall())
|
||||
|
||||
Case "下载文件"
|
||||
Data(0) = Data(0).Replace("\", "/")
|
||||
If Not (Data(0).StartsWith("http://") OrElse Data(0).StartsWith("https://")) Then
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的协议。", "事件执行失败")
|
||||
MyMsgBox("EventData 必须为以 http:// 或 https:// 开头的网址。" & vbCrLf & "PCL 不支持其他乱七八糟的下载协议。", "事件执行失败")
|
||||
Exit Sub
|
||||
End If
|
||||
PageOtherTest.StartCustomDownload(Data(0), GetFileNameFromPath(Data(0)))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
''' <summary>
|
||||
''' 接下来要播放的音乐文件路径。未初始化时为 Nothing。
|
||||
''' </summary>
|
||||
Public MusicToplayList As List(Of String) = Nothing
|
||||
Public MusicWaitingList As List(Of String) = Nothing
|
||||
''' <summary>
|
||||
''' 全部音乐文件路径。未初始化时为 Nothing。
|
||||
''' </summary>
|
||||
@@ -25,17 +25,17 @@
|
||||
For Each File In My.Computer.FileSystem.GetFiles(Path & "PCL\Musics\", FileIO.SearchOption.SearchAllSubDirectories, "*.*")
|
||||
'文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
|
||||
Dim Extend As String = File.Split(".").Last.ToLower
|
||||
If Not (Extend = "ini" OrElse Extend = "jpg" OrElse Extend = "txt" OrElse Extend = "cfg" 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)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
'打乱顺序播放
|
||||
MusicToplayList = If(Setup.Get("UiMusicRandom"), Shuffle(New List(Of String)(MusicAllList)), New List(Of String)(MusicAllList))
|
||||
If Not IgnoreFirst = "" AndAlso Not MusicToplayList.Count = 0 AndAlso MusicToplayList(0) = IgnoreFirst Then
|
||||
MusicWaitingList = If(Setup.Get("UiMusicRandom"), Shuffle(New List(Of String)(MusicAllList)), New List(Of String)(MusicAllList))
|
||||
If Not IgnoreFirst = "" AndAlso Not MusicWaitingList.Count = 0 AndAlso MusicWaitingList(0) = IgnoreFirst Then
|
||||
'若需要避免成为第一项的为第一项,则将它放在最后
|
||||
MusicToplayList.RemoveAt(0)
|
||||
MusicToplayList.Add(IgnoreFirst)
|
||||
MusicWaitingList.RemoveAt(0)
|
||||
MusicWaitingList.Add(IgnoreFirst)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log(ex, "初始化音乐列表失败", LogLevel.Feedback)
|
||||
@@ -46,12 +46,11 @@
|
||||
''' </summary>
|
||||
Private Function DequeueNextMusicAddress() As String
|
||||
'初始化,确保存在音乐
|
||||
If MusicAllList Is Nothing Then MusicListInit(False)
|
||||
If MusicAllList.Count = 0 Then Throw New Exception("在没有音乐时尝试获取音乐路径")
|
||||
If MusicAllList Is Nothing OrElse MusicAllList.Count = 0 Then MusicListInit(False)
|
||||
'出列下一个音乐,如果出列结束则生成新列表
|
||||
DequeueNextMusicAddress = MusicToplayList(0)
|
||||
MusicToplayList.RemoveAt(0)
|
||||
If MusicToplayList.Count = 0 Then MusicListInit(False, DequeueNextMusicAddress)
|
||||
DequeueNextMusicAddress = MusicWaitingList(0)
|
||||
MusicWaitingList.RemoveAt(0)
|
||||
If MusicWaitingList.Count = 0 Then MusicListInit(False, DequeueNextMusicAddress)
|
||||
End Function
|
||||
|
||||
#End Region
|
||||
@@ -106,7 +105,8 @@
|
||||
Case MusicStates.Play
|
||||
MusicPause()
|
||||
Case Else
|
||||
Hint("音乐目前为停止状态!", HintType.Critical)
|
||||
Log("[Music] 音乐目前为停止状态,已强制尝试开始播放", LogLevel.Debug)
|
||||
MusicRefreshPlay(False)
|
||||
End Select
|
||||
End If
|
||||
End Sub
|
||||
@@ -212,15 +212,15 @@
|
||||
''' 继续音乐播放,返回是否成功切换了状态。
|
||||
''' </summary>
|
||||
Public Function MusicResume() As Boolean
|
||||
If MusicState = MusicStates.Pause Then
|
||||
If MusicState = MusicStates.Play OrElse Not MusicAllList.Any() Then
|
||||
Return False
|
||||
Else
|
||||
RunInThread(Sub()
|
||||
MusicNAudio.Play()
|
||||
MusicRefreshUI()
|
||||
Log("[Music] 已恢复播放")
|
||||
End Sub)
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
Log(ex, "播放音乐失败(" & MusicCurrent & ")", LogLevel.Developer)
|
||||
If MusicAllList.Count > 1 Then
|
||||
Thread.Sleep(1000)
|
||||
MusicStartPlay(DequeueNextMusicAddress)
|
||||
MusicStartPlay(DequeueNextMusicAddress, IsFirstLoad)
|
||||
End If
|
||||
Finally
|
||||
If CurrentWave IsNot Nothing Then CurrentWave.Dispose()
|
||||
|
||||
@@ -25,7 +25,7 @@ Public Class CookieWebClient
|
||||
''' </summary>
|
||||
Public Timeout As Integer = 600000
|
||||
|
||||
Protected Overrides Function GetWebRequest(ByVal address As Uri) As WebRequest
|
||||
Protected Overrides Function GetWebRequest(address As Uri) As WebRequest
|
||||
Dim r As WebRequest = MyBase.GetWebRequest(address)
|
||||
Dim request = TryCast(r, HttpWebRequest)
|
||||
|
||||
@@ -37,19 +37,19 @@ Public Class CookieWebClient
|
||||
Return r
|
||||
End Function
|
||||
|
||||
Protected Overrides Function GetWebResponse(ByVal request As WebRequest, ByVal result As IAsyncResult) As WebResponse
|
||||
Protected Overrides Function GetWebResponse(request As WebRequest, result As IAsyncResult) As WebResponse
|
||||
Dim response As WebResponse = MyBase.GetWebResponse(request, result)
|
||||
ReadCookies(response)
|
||||
Return response
|
||||
End Function
|
||||
|
||||
Protected Overrides Function GetWebResponse(ByVal request As WebRequest) As WebResponse
|
||||
Protected Overrides Function GetWebResponse(request As WebRequest) As WebResponse
|
||||
Dim response As WebResponse = MyBase.GetWebResponse(request)
|
||||
ReadCookies(response)
|
||||
Return response
|
||||
End Function
|
||||
|
||||
Private Sub ReadCookies(ByVal r As WebResponse)
|
||||
Private Sub ReadCookies(r As WebResponse)
|
||||
Dim response = TryCast(r, HttpWebResponse)
|
||||
If response IsNot Nothing Then
|
||||
Dim cookies As CookieCollection = response.Cookies
|
||||
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.6.2.0")>
|
||||
<Assembly: AssemblyFileVersion("2.6.2.0")>
|
||||
<Assembly: AssemblyVersion("2.6.10.0")>
|
||||
<Assembly: AssemblyFileVersion("2.6.10.0")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
@@ -196,7 +196,7 @@ Public Module ModDownloadLib
|
||||
Else
|
||||
NewItem.Info = Entry("lore").ToString
|
||||
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
|
||||
'建立菜单
|
||||
If IsSaveOnly Then
|
||||
@@ -299,8 +299,8 @@ Public Module ModDownloadLib
|
||||
WikiName = "Java版Infdev_20100618"
|
||||
ElseIf Id = "c0.30_01c" OrElse Id = "c0.30_survival" OrElse Id.Contains("生存测试") Then
|
||||
WikiName = "Java版Classic_0.30(生存模式)"
|
||||
ElseIf Id = "c0.31" Then
|
||||
WikiName = "Java版Indev_0.31(2010年1月30日)"
|
||||
ElseIf Id.StartsWith("c0.31") Then
|
||||
WikiName = "Java版Indev_0.31_20100130"
|
||||
ElseIf Id.StartsWith("c") Then
|
||||
WikiName = "Java版" & Id.Replace("c", "Classic_")
|
||||
ElseIf Id.StartsWith("rd-") Then
|
||||
@@ -382,11 +382,11 @@ Public Module ModDownloadLib
|
||||
Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback)
|
||||
End Try
|
||||
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
|
||||
Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
|
||||
If Java Is Nothing Then
|
||||
If Not JavaDownloadConfirm(17) Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
'开始自动下载
|
||||
Dim JavaLoader = JavaFixLoaders(17)
|
||||
JavaLoader.Start(17, IsForceRestart:=True)
|
||||
@@ -399,7 +399,12 @@ Public Module ModDownloadLib
|
||||
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
End If
|
||||
'添加 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
|
||||
'开始启动
|
||||
SyncLock InstallSyncLock
|
||||
@@ -489,7 +494,7 @@ Public Module ModDownloadLib
|
||||
''' <summary>
|
||||
''' 获取下载某个 OptiFine 版本的加载器列表。
|
||||
''' </summary>
|
||||
Private Function McDownloadOptiFineLoader(DownloadInfo As DlOptiFineListEntry, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
|
||||
Private Function McDownloadOptiFineLoader(DownloadInfo As DlOptiFineListEntry, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional ClientFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
|
||||
|
||||
'参数初始化
|
||||
McFolder = If(McFolder, PathMcFolder)
|
||||
@@ -504,67 +509,66 @@ Public Module ModDownloadLib
|
||||
|
||||
'获取下载地址
|
||||
Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 OptiFine 主文件下载地址",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
'启动依赖版本的下载
|
||||
If ClientDownloadLoader Is Nothing Then
|
||||
If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
|
||||
ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
|
||||
End If
|
||||
Task.Progress = 0.1
|
||||
Dim Sources As New List(Of String)
|
||||
'BMCLAPI 源
|
||||
If DownloadInfo.IsPreview Then
|
||||
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(" ", "/"))
|
||||
Else
|
||||
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 & " ", ""))
|
||||
End If
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
|
||||
End Try
|
||||
Task.Progress = 0.9
|
||||
'OptiFine 中文镜像源
|
||||
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))}
|
||||
End Sub) With {.ProgressWeight = 8})
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
'启动依赖版本的下载
|
||||
If ClientDownloadLoader Is Nothing Then
|
||||
If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
|
||||
ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
|
||||
End If
|
||||
Task.Progress = 0.1
|
||||
Dim Sources As New List(Of String)
|
||||
'BMCLAPI 源
|
||||
If DownloadInfo.IsPreview Then
|
||||
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(" ", "/"))
|
||||
Else
|
||||
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 & " ", ""))
|
||||
End If
|
||||
'官方源
|
||||
Dim PageData As String
|
||||
Try
|
||||
PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
|
||||
Task.Progress = 0.8
|
||||
Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
|
||||
Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
|
||||
End Try
|
||||
'OptiFine 中文镜像源
|
||||
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))}
|
||||
End Sub) With {.ProgressWeight = 8})
|
||||
Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 8})
|
||||
Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("等待原版下载",
|
||||
Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
|
||||
'等待原版文件下载完成
|
||||
If ClientDownloadLoader Is Nothing Then Exit Sub
|
||||
Dim TargetLoaders As List(Of LoaderBase) =
|
||||
ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName).
|
||||
Where(Function(l) l.State <> LoadState.Finished).ToList
|
||||
If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成")
|
||||
Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted
|
||||
TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList
|
||||
Thread.Sleep(50)
|
||||
Loop
|
||||
If Task.IsAborted Then Exit Sub
|
||||
'拷贝原版文件
|
||||
If Not IsCustomFolder Then Exit Sub
|
||||
SyncLock VanillaSyncLock
|
||||
Dim ClientName As String = New DirectoryInfo(ClientDownloadLoader.Input).Name
|
||||
Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit)
|
||||
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then
|
||||
CopyFile(ClientDownloadLoader.Input & ClientName & ".json",
|
||||
Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
|
||||
'等待原版文件下载完成
|
||||
If ClientDownloadLoader Is Nothing Then Exit Sub
|
||||
Dim TargetLoaders As List(Of LoaderBase) =
|
||||
ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName).
|
||||
Where(Function(l) l.State <> LoadState.Finished).ToList
|
||||
If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成")
|
||||
Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted
|
||||
TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList
|
||||
Thread.Sleep(50)
|
||||
Loop
|
||||
If Task.IsAborted Then Exit Sub
|
||||
'拷贝原版文件
|
||||
If Not IsCustomFolder Then Exit Sub
|
||||
SyncLock VanillaSyncLock
|
||||
Dim ClientName As String = GetFolderNameFromPath(ClientFolder)
|
||||
Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit)
|
||||
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then
|
||||
CopyFile(ClientFolder & ClientName & ".json",
|
||||
McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json")
|
||||
End If
|
||||
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then
|
||||
CopyFile(ClientDownloadLoader.Input & ClientName & ".jar",
|
||||
End If
|
||||
If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then
|
||||
CopyFile(ClientFolder & ClientName & ".jar",
|
||||
McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
|
||||
End If
|
||||
End SyncLock
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
End If
|
||||
End SyncLock
|
||||
End Sub) With {.ProgressWeight = 0.1, .Show = False})
|
||||
|
||||
'安装(新旧方式均需要原版 Jar 和 Json)
|
||||
If IsNewVersion Then
|
||||
@@ -586,7 +590,19 @@ Public Module ModDownloadLib
|
||||
BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
|
||||
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
|
||||
'复制文件
|
||||
File.Delete(BaseMcFolder & "launcher_profiles.json")
|
||||
@@ -596,7 +612,7 @@ Public Module ModDownloadLib
|
||||
File.Delete(Target)
|
||||
DeleteDirectory(BaseMcFolderHome)
|
||||
Catch ex As Exception
|
||||
Throw New Exception("安装新版 OptiFine 版本失败", ex)
|
||||
Throw New Exception("安装 OptiFine(方式 A)失败", ex)
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = 8})
|
||||
Else
|
||||
@@ -645,7 +661,7 @@ Public Module ModDownloadLib
|
||||
End If
|
||||
WriteFile(VersionFolder & Id & ".json", Json)
|
||||
Catch ex As Exception
|
||||
Throw New Exception("安装旧版 OptiFine 版本失败", ex)
|
||||
Throw New Exception("安装 OptiFine(方式 B)失败", ex)
|
||||
End Try
|
||||
End Sub) With {.ProgressWeight = 1})
|
||||
End If
|
||||
@@ -1110,11 +1126,11 @@ Public Module ModDownloadLib
|
||||
End Try
|
||||
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
|
||||
Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
|
||||
If Java Is Nothing Then
|
||||
If Not JavaDownloadConfirm(17) Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
'开始自动下载
|
||||
Dim JavaLoader = JavaFixLoaders(17)
|
||||
JavaLoader.Start(17, IsForceRestart:=True)
|
||||
@@ -1127,7 +1143,12 @@ Public Module ModDownloadLib
|
||||
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
|
||||
End If
|
||||
'添加 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
|
||||
'开始启动
|
||||
SyncLock InstallSyncLock
|
||||
@@ -1259,7 +1280,7 @@ Public Module ModDownloadLib
|
||||
''' <summary>
|
||||
''' 获取下载某个 Forge 版本的加载器列表。
|
||||
''' </summary>
|
||||
Private Function McDownloadForgeLoader(Version As String, Inherit As String, Optional DownloadInfo As DlForgeVersionEntry = Nothing, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
|
||||
Private Function McDownloadForgeLoader(Version As String, Inherit As String, Optional DownloadInfo As DlForgeVersionEntry = Nothing, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional ClientFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
|
||||
|
||||
'参数初始化
|
||||
McFolder = If(McFolder, PathMcFolder)
|
||||
@@ -1386,13 +1407,13 @@ Public Module ModDownloadLib
|
||||
'拷贝原版文件
|
||||
If Not IsCustomFolder Then Exit Sub
|
||||
SyncLock VanillaSyncLock
|
||||
Dim ClientName As String = New DirectoryInfo(ClientDownloadLoader.Input).Name
|
||||
Dim ClientName As String = GetFolderNameFromPath(ClientFolder)
|
||||
Directory.CreateDirectory(McFolder & "versions\" & Inherit)
|
||||
If Not File.Exists(McFolder & "versions\" & Inherit & "\" & Inherit & ".json") Then
|
||||
CopyFile(ClientDownloadLoader.Input & ClientName & ".json", McFolder & "versions\" & Inherit & "\" & Inherit & ".json")
|
||||
CopyFile(ClientFolder & ClientName & ".json", McFolder & "versions\" & Inherit & "\" & Inherit & ".json")
|
||||
End If
|
||||
If Not File.Exists(McFolder & "versions\" & Inherit & "\" & Inherit & ".jar") Then
|
||||
CopyFile(ClientDownloadLoader.Input & ClientName & ".jar", McFolder & "versions\" & Inherit & "\" & Inherit & ".jar")
|
||||
CopyFile(ClientFolder & ClientName & ".jar", McFolder & "versions\" & Inherit & "\" & Inherit & ".jar")
|
||||
End If
|
||||
End SyncLock
|
||||
#End Region
|
||||
@@ -1415,12 +1436,25 @@ Public Module ModDownloadLib
|
||||
'释放 launcher_installer.json
|
||||
McFolderLauncherProfilesJsonCreate(McFolder)
|
||||
Task.Progress = 0.05
|
||||
'释放 Forge 注入器
|
||||
WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller"))
|
||||
Task.Progress = 0.06
|
||||
'运行注入器
|
||||
ForgeInjector(InstallerAddress, Task, McFolder)
|
||||
Task.Progress = 0.97
|
||||
'运行 Forge 安装器
|
||||
Dim UseJavaWrapper As Boolean = True
|
||||
Retry:
|
||||
Try
|
||||
'释放 Forge 注入器
|
||||
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
|
||||
Dim DeltaList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.SkipWhile(Function(Info As DirectoryInfo) As Boolean
|
||||
Return OldList.Contains(Info.FullName)
|
||||
@@ -1470,7 +1504,7 @@ Public Module ModDownloadLib
|
||||
Task.Progress = 0.6
|
||||
'解压支持库文件
|
||||
Installer.Dispose()
|
||||
ZipFile.ExtractToDirectory(InstallerAddress, InstallerAddress & "_unrar\")
|
||||
ExtractFile(InstallerAddress, InstallerAddress & "_unrar\")
|
||||
CopyDirectory(InstallerAddress & "_unrar\maven\", McFolder & "libraries\")
|
||||
DeleteDirectory(InstallerAddress & "_unrar\")
|
||||
Else
|
||||
@@ -1797,6 +1831,10 @@ Public Module ModDownloadLib
|
||||
''' 必填。安装目标版本名称。
|
||||
''' </summary>
|
||||
Public TargetVersionName As String
|
||||
''' <summary>
|
||||
''' 必填。安装目标文件夹。
|
||||
''' </summary>
|
||||
Public TargetVersionFolder As String
|
||||
|
||||
''' <summary>
|
||||
''' 必填。欲下载的 Minecraft 的版本名。
|
||||
@@ -1909,7 +1947,7 @@ Public Module ModDownloadLib
|
||||
Dim Loader As New LoaderCombo(Of String)(Request.TargetVersionName & " 安装", SubLoaders) With {.OnStateChanged = AddressOf McInstallState}
|
||||
|
||||
'启动
|
||||
Loader.Start(PathMcFolder & "versions\" & Request.TargetVersionName & "\")
|
||||
Loader.Start(Request.TargetVersionFolder)
|
||||
LoaderTaskbarAdd(Loader)
|
||||
FrmMain.BtnExtraDownload.ShowRefresh()
|
||||
FrmMain.BtnExtraDownload.Ribble()
|
||||
@@ -1950,9 +1988,9 @@ Public Module ModDownloadLib
|
||||
If Directory.Exists(TempMcFolder) Then DeleteDirectory(TempMcFolder)
|
||||
Dim OptiFineFolder As String = Nothing
|
||||
If Request.OptiFineVersion IsNot Nothing Then
|
||||
If Request.OptiFineVersion.Contains("_HD_U") Then Request.OptiFineVersion = "HD_U_" & Request.OptiFineVersion.Split("_HD_U").Last '#735
|
||||
If Request.OptiFineVersion.Contains("_HD_U_") Then Request.OptiFineVersion = "HD_U_" & Request.OptiFineVersion.Split("_HD_U_").Last '#735
|
||||
Request.OptiFineEntry = New DlOptiFineListEntry With {
|
||||
.NameDisplay = Request.MinecraftName & " " & Request.OptiFineVersion.Replace("HD_U_", "").Replace("_", ""),
|
||||
.NameDisplay = Request.MinecraftName & " " & Request.OptiFineVersion.Replace("HD_U_", "").Replace("_", "").Replace("pre", " pre"),
|
||||
.Inherit = Request.MinecraftName,
|
||||
.IsPreview = Request.OptiFineVersion.ToLower.Contains("pre"),
|
||||
.NameVersion = Request.MinecraftName & "-OptiFine_" & Request.OptiFineVersion,
|
||||
@@ -2009,28 +2047,29 @@ Public Module ModDownloadLib
|
||||
If OptiFineAsMod Then
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineSaveLoader(Request.OptiFineEntry, OptiFineFolder & Request.OptiFineEntry.NameFile)) With {.Show = False, .ProgressWeight = 16, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
|
||||
Else
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineLoader(Request.OptiFineEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 24, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineLoader(Request.OptiFineEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 24, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
|
||||
End If
|
||||
End If
|
||||
'Forge
|
||||
If Request.ForgeVersion IsNot Nothing Then
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 Forge " & Request.ForgeVersion, McDownloadForgeLoader(Request.ForgeVersion, Request.MinecraftName, Request.ForgeEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 25, .Block = Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 Forge " & Request.ForgeVersion, McDownloadForgeLoader(Request.ForgeVersion, Request.MinecraftName, Request.ForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 25, .Block = Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
|
||||
End If
|
||||
'LiteLoader
|
||||
If Request.LiteLoaderEntry IsNot Nothing Then
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 LiteLoader " & Request.MinecraftName, McDownloadLiteLoaderLoader(Request.LiteLoaderEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 1, .Block = Request.FabricVersion Is Nothing})
|
||||
End If
|
||||
'Fabric Loader
|
||||
'Fabric
|
||||
If Request.FabricVersion IsNot Nothing Then
|
||||
LoaderList.Add(New LoaderCombo(Of String)("下载 Fabric " & Request.FabricVersion, McDownloadFabricLoader(Request.FabricVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2, .Block = True})
|
||||
End If
|
||||
'合并安装
|
||||
LoaderList.Add(New LoaderTask(Of String, String)("安装游戏", Sub(Task As LoaderTask(Of String, String))
|
||||
InstallMerge(OutputFolder, OutputFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, FabricFolder, LiteLoaderFolder)
|
||||
Task.Progress = 0.3
|
||||
If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries")
|
||||
If Directory.Exists(TempMcFolder & "mods") Then CopyDirectory(TempMcFolder & "mods", PathMcFolder & "mods")
|
||||
End Sub) With {.ProgressWeight = 2, .Block = True})
|
||||
LoaderList.Add(New LoaderTask(Of String, String)("安装游戏",
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
InstallMerge(OutputFolder, OutputFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, FabricFolder, LiteLoaderFolder)
|
||||
Task.Progress = 0.3
|
||||
If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries")
|
||||
If Directory.Exists(TempMcFolder & "mods") Then CopyDirectory(TempMcFolder & "mods", PathMcFolder & "mods")
|
||||
End Sub) With {.ProgressWeight = 2, .Block = True})
|
||||
'补全文件
|
||||
If Not DontFixLibraries AndAlso
|
||||
(Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.Split(".")(0) >= 20) OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then
|
||||
@@ -2175,7 +2214,7 @@ Public Module ModDownloadLib
|
||||
OutputJson.Merge(FabricJson)
|
||||
End If
|
||||
'修改
|
||||
If RealArguments IsNot Nothing Then OutputJson("minecraftArguments") = RealArguments
|
||||
If RealArguments IsNot Nothing AndAlso RealArguments.Replace(" ", "") <> "" Then OutputJson("minecraftArguments") = RealArguments
|
||||
OutputJson.Remove("_comment_")
|
||||
OutputJson.Remove("inheritsFrom")
|
||||
OutputJson.Remove("jar")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
'初始化加载器信息
|
||||
Private Sub PageDownloadCompDetail_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
|
||||
Project = FrmMain.PageCurrent.Additional
|
||||
Project = FrmMain.PageCurrent.Additional(0)
|
||||
PageLoaderInit(Load, PanLoad, PanMain, CardIntro, CompFileLoader, AddressOf Load_OnFinish)
|
||||
End Sub
|
||||
'自动重试
|
||||
@@ -40,7 +40,7 @@
|
||||
End Class
|
||||
Private Sub Load_OnFinish()
|
||||
'获取目标版本与 Mod 加载器
|
||||
Dim TargetVersion As String = ""
|
||||
Dim TargetVersion As String
|
||||
Select Case Project.Type
|
||||
Case CompType.Mod
|
||||
TargetVersion = If(PageDownloadMod.Loader.Input.GameVersion, "")
|
||||
@@ -102,7 +102,9 @@
|
||||
NewCard.SwapControl = NewStack
|
||||
PanMain.Children.Add(NewCard)
|
||||
'确定卡片是否展开
|
||||
If Pair.Key = TargetCardName Then
|
||||
If Pair.Key = TargetCardName OrElse
|
||||
(FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
|
||||
CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
|
||||
MyCard.StackInstall(NewStack, If(Project.Type = CompType.ModPack, 9, 8), Pair.Key) 'FUTURE: Res
|
||||
Else
|
||||
NewCard.IsSwaped = True
|
||||
@@ -128,7 +130,7 @@
|
||||
Private IsFirstInit As Boolean = True
|
||||
Public Sub Init() Handles Me.PageEnter
|
||||
AniControlEnabled += 1
|
||||
Project = FrmMain.PageCurrent.Additional
|
||||
Project = FrmMain.PageCurrent.Additional(0)
|
||||
PanBack.ScrollToHome()
|
||||
|
||||
'重启加载器
|
||||
@@ -142,6 +144,8 @@
|
||||
'放置当前工程
|
||||
If CompItem IsNot Nothing Then PanIntro.Children.Remove(CompItem)
|
||||
CompItem = Project.ToCompItem(True, True)
|
||||
CompItem.IsHitTestVisible = False
|
||||
CompItem.HasAnimation = False
|
||||
CompItem.Margin = New Thickness(-7, -7, 0, 8)
|
||||
PanIntro.Children.Insert(0, CompItem)
|
||||
|
||||
|
||||
@@ -571,10 +571,23 @@
|
||||
NewCard.IsSwaped = True
|
||||
PanMinecraft.Children.Add(NewCard)
|
||||
Next
|
||||
'自动选择版本
|
||||
If McVersionWaitingForSelect Is Nothing Then Exit Try
|
||||
Log("[Download] 自动选择 MC 版本:" & McVersionWaitingForSelect)
|
||||
For Each Version As JObject In Versions
|
||||
If Version("id").ToString <> McVersionWaitingForSelect Then Continue For
|
||||
Dim Item = McDownloadListItem(Version, Sub()
|
||||
End Sub, False)
|
||||
MinecraftSelected(Item, Nothing)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log(ex, "可视化安装版本列表出错", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
''' <summary>
|
||||
''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
|
||||
''' </summary>
|
||||
Public Shared McVersionWaitingForSelect As String = Nothing
|
||||
|
||||
#End Region
|
||||
|
||||
@@ -584,7 +597,7 @@
|
||||
''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFineGetError() As String
|
||||
If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFine Is Nothing OrElse LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFine.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFine.State, Object).Error.Message
|
||||
'检查 Forge 1.13 - 1.14.3:全部不兼容
|
||||
If SelectedForge IsNot Nothing AndAlso
|
||||
@@ -687,7 +700,7 @@
|
||||
''' </summary>
|
||||
Private Function LoadLiteLoaderGetError() As String
|
||||
If Not SelectedMinecraftId.Contains("1.") OrElse Val(SelectedMinecraftId.Split(".")(1)) > 12 Then Return "没有可用版本"
|
||||
If LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadLiteLoader Is Nothing OrElse LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadLiteLoader.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadLiteLoader.State, Object).Error.Message
|
||||
For Each Version As DlLiteLoaderListEntry In DlLiteLoaderListLoader.Output.Value
|
||||
If Version.Inherit = SelectedMinecraftId Then Return Nothing
|
||||
@@ -832,10 +845,10 @@
|
||||
''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricGetError() As String
|
||||
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabric Is Nothing OrElse LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabric.State, Object).Error.Message
|
||||
For Each Version As JObject In DlFabricListLoader.Output.Value("game")
|
||||
If Version("version").ToString = SelectedMinecraftId.Replace("∞", "infinite") 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 SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
|
||||
Return Nothing
|
||||
@@ -895,7 +908,7 @@
|
||||
Public Shared Function IsSuitableFabricApi(DisplayName As String, MinecraftVersion As String) As Boolean
|
||||
Try
|
||||
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 Not DisplayName.Contains("/") OrElse Not DisplayName.Contains("]") Then Return False
|
||||
'直接的判断(例如 1.18.1/22w03a)
|
||||
@@ -932,11 +945,16 @@
|
||||
''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadFabricApiGetError() As String
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
If LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabricApi Is Nothing OrElse LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadFabricApi.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadFabricApi.State, Object).Error.Message
|
||||
If DlFabricApiLoader.Output Is Nothing Then
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
Return "正在获取版本列表……"
|
||||
End If
|
||||
For Each Version In DlFabricApiLoader.Output
|
||||
If IsSuitableFabricApi(Version.DisplayName, SelectedMinecraftId) Then Return Nothing
|
||||
If Not IsSuitableFabricApi(Version.DisplayName, SelectedMinecraftId) Then Continue For
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
Return Nothing
|
||||
Next
|
||||
Return "没有可用版本"
|
||||
End Function
|
||||
@@ -1019,14 +1037,21 @@
|
||||
''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function LoadOptiFabricGetError() As String
|
||||
If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
|
||||
If SelectedMinecraftId.StartsWith("1.14") OrElse SelectedMinecraftId.StartsWith("1.15") Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
|
||||
If LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFabric Is Nothing OrElse LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Run Then Return "正在获取版本列表……"
|
||||
If LoadOptiFabric.State.LoadingState = MyLoading.MyLoadingState.Error Then Return "获取版本列表失败:" & CType(LoadOptiFabric.State, Object).Error.Message
|
||||
If DlOptiFabricLoader.Output Is Nothing Then
|
||||
If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
|
||||
Return "正在获取版本列表……"
|
||||
End If
|
||||
For Each Version In DlOptiFabricLoader.Output
|
||||
If IsSuitableOptiFabric(Version, SelectedMinecraftId) Then Return Nothing
|
||||
If Not IsSuitableOptiFabric(Version, SelectedMinecraftId) Then Continue For '2135#
|
||||
If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
|
||||
If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
|
||||
If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
|
||||
Return Nothing '通过检查
|
||||
Next
|
||||
Return "没有可用版本"
|
||||
End Function
|
||||
@@ -1101,6 +1126,7 @@
|
||||
'提交安装申请
|
||||
Dim Request As New McInstallRequest With {
|
||||
.TargetVersionName = TextSelectName.Text,
|
||||
.TargetVersionFolder = $"{PathMcFolder}versions\{TextSelectName.Text}\",
|
||||
.MinecraftJson = SelectedMinecraftJsonUrl,
|
||||
.MinecraftName = SelectedMinecraftId,
|
||||
.OptiFineEntry = SelectedOptiFine,
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.20.2" />
|
||||
<local:MyComboBoxItem Content="1.20.1" />
|
||||
<local:MyComboBoxItem Content="1.20" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
|
||||
@@ -32,9 +32,7 @@
|
||||
'列表项
|
||||
PanProjects.Children.Clear()
|
||||
For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
|
||||
PanProjects.Children.Add(
|
||||
Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing,
|
||||
Loader.Input.ModLoader = CompModLoaderType.Any, AddressOf ProjectClick))
|
||||
PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, Loader.Input.ModLoader = CompModLoaderType.Any))
|
||||
Next
|
||||
'页码
|
||||
CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
|
||||
@@ -68,12 +66,6 @@
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
'进入详情页面
|
||||
|
||||
Public Sub ProjectClick(sender As MyCompItem, e As EventArgs)
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, .Additional = sender.Tag})
|
||||
End Sub
|
||||
|
||||
'切换页码
|
||||
|
||||
Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
|
||||
@@ -114,6 +106,7 @@
|
||||
TextSearchName.Text = ""
|
||||
TextSearchVersion.Text = "全部 (也可自行输入)"
|
||||
TextSearchVersion.SelectedIndex = 0
|
||||
ComboSearchSource.SelectedIndex = 0
|
||||
ComboSearchTag.SelectedIndex = 0
|
||||
ComboSearchLoader.SelectedIndex = 0
|
||||
Loader.LastFinishedTime = 0 '要求强制重新开始
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="版本" Margin="0,0,18,0" />
|
||||
<local:MyComboBox x:Name="TextSearchVersion" Grid.Row="2" Grid.Column="1" IsEditable="True" MaxDropDownHeight="320">
|
||||
<local:MyComboBoxItem Content="全部 (也可自行输入)" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="1.20.2" />
|
||||
<local:MyComboBoxItem Content="1.20.1" />
|
||||
<local:MyComboBoxItem Content="1.20" />
|
||||
<local:MyComboBoxItem Content="1.19.4" />
|
||||
<local:MyComboBoxItem Content="1.18.2" />
|
||||
@@ -65,7 +67,7 @@
|
||||
<local:MyComboBoxItem Tag="4736/" Content="空岛" />
|
||||
<local:MyComboBoxItem Tag="5128/" Content="原版改良" />
|
||||
<local:MyComboBoxItem Tag="4487/" Content="FTB"
|
||||
ToolTip="该整合包由 FTB 官方发布" ToolTipService.InitialShowDelay="150" ToolTipService.Placement="Right" ToolTipService.VerticalOffset="-1" ToolTipService.HorizontalOffset="-40" />
|
||||
ToolTip="由 FTB 官方发布" ToolTipService.HorizontalOffset="70" />
|
||||
<local:MyComboBoxItem Tag="4480/" Content="基于地图" />
|
||||
<local:MyComboBoxItem Tag="4481/lightweight" Content="轻量整合" />
|
||||
<local:MyComboBoxItem Tag="4482/" Content="大型整合" />
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
'列表项
|
||||
PanProjects.Children.Clear()
|
||||
For i = Math.Min(Page * PageSize, Storage.Results.Count - 1) To Math.Min((Page + 1) * PageSize - 1, Storage.Results.Count - 1)
|
||||
PanProjects.Children.Add(
|
||||
Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, True, AddressOf ProjectClick))
|
||||
PanProjects.Children.Add(Storage.Results(i).ToCompItem(Loader.Input.GameVersion Is Nothing, True))
|
||||
Next
|
||||
'页码
|
||||
CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
|
||||
@@ -66,12 +65,6 @@
|
||||
End Select
|
||||
End Sub
|
||||
|
||||
'进入详情页面
|
||||
|
||||
Public Sub ProjectClick(sender As MyCompItem, e As EventArgs)
|
||||
FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail, .Additional = sender.Tag})
|
||||
End Sub
|
||||
|
||||
'切换页码
|
||||
|
||||
Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
|
||||
@@ -116,6 +109,7 @@
|
||||
TextSearchName.Text = ""
|
||||
TextSearchVersion.Text = "全部 (也可自行输入)"
|
||||
TextSearchVersion.SelectedIndex = 0
|
||||
ComboSearchSource.SelectedIndex = 0
|
||||
ComboSearchTag.SelectedIndex = 0
|
||||
Loader.LastFinishedTime = 0 '要求强制重新开始
|
||||
End Sub
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'皮肤储存
|
||||
Private _Address As String
|
||||
Public Property Address() As String
|
||||
Public Property Address As String
|
||||
Get
|
||||
Return _Address
|
||||
End Get
|
||||
@@ -76,12 +76,19 @@
|
||||
''' </summary>
|
||||
Public Sub Load()
|
||||
Try
|
||||
Address = Loader.Output
|
||||
'检查文件存在
|
||||
If Address = "" Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
|
||||
Address = Loader.Output
|
||||
If String.IsNullOrEmpty(Address) Then Throw New Exception("皮肤加载器 " & Loader.Name & " 没有输出")
|
||||
If Not Address.StartsWith(PathImage) AndAlso Not File.Exists(Address) Then Throw New FileNotFoundException("皮肤文件未找到", Address)
|
||||
'加载
|
||||
Dim Image As New MyBitmap(Address)
|
||||
Dim Image As MyBitmap
|
||||
Try
|
||||
Image = New MyBitmap(Address)
|
||||
Catch ex As Exception '#2272
|
||||
Log(ex, $"皮肤文件已损坏:{Address}", LogLevel.Hint)
|
||||
File.Delete(Address)
|
||||
Exit Sub
|
||||
End Try
|
||||
ImgBack.Tag = Address
|
||||
'大小检查
|
||||
Dim Scale As Integer = Image.Pic.Width / 64
|
||||
@@ -97,7 +104,7 @@
|
||||
(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 - 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
|
||||
ImgFore.Source = Nothing
|
||||
End If
|
||||
@@ -105,7 +112,7 @@
|
||||
ImgFore.Source = Nothing
|
||||
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)
|
||||
Catch ex As Exception
|
||||
Log(ex, "载入头像失败(" & If(Address, "null") & "," & Loader.Name & ")", LogLevel.Hint)
|
||||
|
||||
@@ -79,7 +79,45 @@
|
||||
''' 切换至启动中页面。
|
||||
''' </summary>
|
||||
Public Sub PageChangeToLaunching()
|
||||
LaunchingPreload()
|
||||
'修改登陆方式
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
LabLaunchingMethod.Text = "联机离线登录"
|
||||
Else
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
End If
|
||||
Case McLoginType.Ms
|
||||
LabLaunchingMethod.Text = "正版登录"
|
||||
Case McLoginType.Nide
|
||||
LabLaunchingMethod.Text = "统一通行证"
|
||||
Case McLoginType.Auth
|
||||
LabLaunchingMethod.Text = "Authlib-Injector"
|
||||
End Select
|
||||
'初始化页面
|
||||
LabLaunchingName.Text = McVersionCurrent.Name
|
||||
LabLaunchingStage.Text = "初始化"
|
||||
LabLaunchingTitle.Text = If(McLaunchLoader.Input Is Nothing OrElse McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingProgress.Text = "0.00 %"
|
||||
LabLaunchingProgress.Opacity = 1
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingProgressLeft.Opacity = 0.6
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingDownload.Text = "0 B/s"
|
||||
LabLaunchingDownload.Opacity = 0
|
||||
LabLaunchingDownload.Visibility = Visibility.Collapsed
|
||||
LabLaunchingDownloadLeft.Opacity = 0
|
||||
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
|
||||
ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star)
|
||||
ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star)
|
||||
PanLaunchingHint.Opacity = 0
|
||||
PanLaunchingHint.Visibility = Visibility.Collapsed
|
||||
PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画
|
||||
McLaunchProcess = Nothing
|
||||
McLaunchWatcher = Nothing
|
||||
'获取 “你知道吗” 提示
|
||||
LabLaunchingHint.Text = PageOtherTest.GetRandomHint()
|
||||
'初始化其他页面
|
||||
PanInput.IsHitTestVisible = False
|
||||
PanLaunching.IsHitTestVisible = False
|
||||
LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Run
|
||||
@@ -356,7 +394,7 @@ Finish:
|
||||
Select Case Type
|
||||
Case 0
|
||||
If FrmLoginLegacy IsNot Nothing AndAlso FrmLoginLegacy.IsReloaded Then
|
||||
Return New EqualableList(Of String) From {0, If(FrmLoginLegacy.ComboName.Text, "")}
|
||||
Return New EqualableList(Of String) From {0, If(FrmLoginLegacy.ComboName.Text.Trim, "")}
|
||||
ElseIf Setup.Get("LoginLegacyName") = "" Then
|
||||
Return New EqualableList(Of String) From {0, ""}
|
||||
Else
|
||||
@@ -526,11 +564,13 @@ Finish:
|
||||
|
||||
'版本选择按钮
|
||||
Private Sub BtnVersion_Click(sender As Object, e As EventArgs) Handles BtnVersion.Click
|
||||
If McLaunchLoader.State = LoadState.Loading Then Exit Sub
|
||||
FrmMain.PageChange(FormMain.PageType.VersionSelect)
|
||||
End Sub
|
||||
'启动按钮
|
||||
Public Sub LaunchButtonClick() Handles BtnLaunch.Click
|
||||
If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled Then Exit Sub
|
||||
If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled OrElse
|
||||
(FrmMain.PageRight IsNot Nothing AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentStay AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentEnter) Then Exit Sub
|
||||
'愚人节处理
|
||||
If IsAprilEnabled AndAlso Not IsAprilGiveup Then
|
||||
ThemeUnlock(12, False, "隐藏主题 滑稽彩 已解锁!")
|
||||
@@ -625,61 +665,13 @@ ExitRefresh:
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
'更多按钮
|
||||
'版本设置按钮
|
||||
Private Sub BtnMore_Click(sender As Object, e As EventArgs) Handles BtnMore.Click
|
||||
If McLaunchLoader.State = LoadState.Loading Then Exit Sub
|
||||
McVersionCurrent.Load()
|
||||
PageVersionLeft.Version = McVersionCurrent
|
||||
FrmMain.PageChange(FormMain.PageType.VersionSetup, 0)
|
||||
End Sub
|
||||
'刷新 UI
|
||||
Public Sub LaunchingPreload()
|
||||
'修改登陆方式
|
||||
Select Case Setup.Get("LoginType")
|
||||
Case McLoginType.Legacy
|
||||
If PageLinkHiper.HiperState = LoadState.Finished Then
|
||||
LabLaunchingMethod.Text = "联机离线登录"
|
||||
Else
|
||||
LabLaunchingMethod.Text = "离线登录"
|
||||
End If
|
||||
Case McLoginType.Ms
|
||||
LabLaunchingMethod.Text = "正版登录"
|
||||
Case McLoginType.Nide
|
||||
LabLaunchingMethod.Text = "统一通行证"
|
||||
Case McLoginType.Auth
|
||||
LabLaunchingMethod.Text = "Authlib-Injector"
|
||||
End Select
|
||||
'初始化页面
|
||||
LabLaunchingName.Text = McVersionCurrent.Name
|
||||
LabLaunchingStage.Text = "初始化"
|
||||
LabLaunchingTitle.Text = If(McLaunchLoader.Input Is Nothing OrElse McLaunchLoader.Input.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
|
||||
LabLaunchingProgress.Text = "0.00 %"
|
||||
LabLaunchingProgress.Opacity = 1
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingProgressLeft.Opacity = 0.6
|
||||
LabLaunchingDownload.Visibility = Visibility.Visible
|
||||
LabLaunchingDownload.Text = "0 B/s"
|
||||
LabLaunchingDownload.Opacity = 0
|
||||
LabLaunchingDownload.Visibility = Visibility.Collapsed
|
||||
LabLaunchingDownloadLeft.Opacity = 0
|
||||
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
|
||||
ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star)
|
||||
ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star)
|
||||
PanLaunchingHint.Opacity = 0
|
||||
PanLaunchingHint.Visibility = Visibility.Collapsed
|
||||
PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画
|
||||
McLaunchProcess = Nothing
|
||||
McLaunchWatcher = Nothing
|
||||
'获取 “你知道吗” 提示
|
||||
Dim Hint As String = 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
|
||||
Private ShowProgress As Double = 0
|
||||
''' <summary>
|
||||
''' 每 0.2s 执行一次,刷新启动的数据 UI 显示。
|
||||
''' </summary>
|
||||
@@ -763,6 +755,7 @@ ExitRefresh:
|
||||
Log(ex, "刷新启动信息失败", LogLevel.Feedback)
|
||||
End Try
|
||||
End Sub
|
||||
Private ShowProgress As Double = 0
|
||||
'尺寸改变动画
|
||||
Private IsWidthAnimating As Boolean = False
|
||||
Private ActualUsedWidth As Double
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
<StackPanel x:Name="PanCustom">
|
||||
</StackPanel>
|
||||
<local:MyCard Margin="0,0,0,15" Title="快照版提示" x:Name="PanHint">
|
||||
<StackPanel Margin="25,40,23,12">
|
||||
<TextBlock x:Name="LabHint1" TextWrapping="Wrap" Margin="0,0,0,2" />
|
||||
<TextBlock x:Name="LabHint2" TextWrapping="Wrap" Margin="0,0,0,2" />
|
||||
<local:MyIconButton LogoScale="1.1" Logo="F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z"
|
||||
Height="20" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
x:Name="BtnHintClose" />
|
||||
<StackPanel Margin="25,38,23,15">
|
||||
<TextBlock x:Name="LabHint1" TextWrapping="Wrap" Margin="0,0,0,2" FontSize="13.5" />
|
||||
<TextBlock x:Name="LabHint2" TextWrapping="Wrap" Margin="0,0,0,2" FontSize="13.5" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="启动日志" x:Name="PanLog">
|
||||
|
||||
@@ -3,100 +3,214 @@
|
||||
Private Sub Init() Handles Me.Loaded
|
||||
PanBack.ScrollToHome()
|
||||
PanLog.Visibility = If(ModeDebug, Visibility.Visible, Visibility.Collapsed)
|
||||
'刷新主页
|
||||
If ShouldRefresh Then
|
||||
ShouldRefresh = False
|
||||
RefreshCustom()
|
||||
End If
|
||||
'快照版提示
|
||||
#If BETA Then
|
||||
PanHint.Visibility = Visibility.Collapsed
|
||||
#Else
|
||||
PanHint.Title = "快照版提示"
|
||||
PanHint.Visibility = If(ThemeCheckGold(), Visibility.Collapsed, Visibility.Visible)
|
||||
LabHint1.Text = "快照版包含尚未在正式版发布的测试性功能,仅用于赞助者本人尝鲜。所以请不要发给其他人或者用于制作整合包哦!如果发现了 Bug,可以在 更多 → 反馈 中提交!"
|
||||
LabHint2.Text = "你可以通过赞助¥23.33 档位换取解锁码来隐藏这个提示。"
|
||||
LabHint1.Text = "快照版包含尚未正式发布的测试功能,仅用于赞助者本人尝鲜。请不要发给其他人或者用来制作整合包哦!"
|
||||
LabHint2.Text = $"若已累积赞助¥23.33,在爱发电私信发送 {vbLQ}解锁码{vbRQ} 即可永久隐藏此提示。"
|
||||
#End If
|
||||
End Sub
|
||||
|
||||
Private LatestFileContent As String = ""
|
||||
Private LatestFileLink As String = Nothing
|
||||
Private IsRefreshing As Boolean = False
|
||||
Public ShouldRefresh As Boolean = True
|
||||
'暂时关闭快照版提示
|
||||
#If Not BETA Then
|
||||
Private Sub BtnHintClose_Click(sender As Object, e As EventArgs) Handles BtnHintClose.Click
|
||||
AniDispose(PanHint, True)
|
||||
End Sub
|
||||
#End If
|
||||
|
||||
#Region "自定义主页"
|
||||
|
||||
''' <summary>
|
||||
''' 刷新自定义的主页。返回是否成功。
|
||||
''' 刷新自定义主页。
|
||||
''' </summary>
|
||||
Private Sub RefreshCustom()
|
||||
If IsRefreshing Then
|
||||
ShouldRefresh = True
|
||||
Exit Sub
|
||||
End If
|
||||
Private Sub Refresh() Handles Me.Loaded
|
||||
RunInNewThread(Sub()
|
||||
Try
|
||||
SyncLock RefreshLock
|
||||
RefreshReal()
|
||||
End SyncLock
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
End Sub, $"刷新自定义主页 #{GetUuid()}")
|
||||
End Sub
|
||||
Private Sub RefreshReal()
|
||||
Dim Content As String = ""
|
||||
Dim Url As String
|
||||
Select Case Setup.Get("UiCustomType")
|
||||
Case 1
|
||||
'加载本地文件
|
||||
Log("[Page] 主页自定义数据来源:本地文件")
|
||||
Content = ReadFile(Path & "PCL\Custom.xaml") 'ReadFile 会进行存在检测
|
||||
Case 2
|
||||
Url = Setup.Get("UiCustomNet")
|
||||
Download:
|
||||
'加载联网文件
|
||||
If String.IsNullOrWhiteSpace(Url) Then Exit Select
|
||||
If Url = Setup.Get("CacheSavedPageUrl") AndAlso File.Exists(PathTemp & "Cache\Custom.xaml") Then
|
||||
'缓存可用
|
||||
Log("[Page] 主页自定义数据来源:联网缓存文件")
|
||||
Content = ReadFile(PathTemp & "Cache\Custom.xaml")
|
||||
'后台更新缓存
|
||||
OnlineLoader.Start(Url)
|
||||
Else
|
||||
'缓存不可用
|
||||
Log("[Page] 主页自定义数据来源:联网全新下载")
|
||||
Hint("正在加载主页……")
|
||||
RunInUiWait(Sub() LoadContent("")) '在加载结束前清空页面
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
OnlineLoader.Start(Url) '下载完成后将会再次触发更新
|
||||
Exit Sub
|
||||
End If
|
||||
Case 3
|
||||
Select Case Setup.Get("UiCustomPreset")
|
||||
Case 0 '你知道吗
|
||||
Log("[Page] 主页预设:你知道吗")
|
||||
Content = "
|
||||
<local:MyCard Title=""你知道吗?"" Margin=""0,0,0,15"">
|
||||
<TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{hint}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
|
||||
<local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
|
||||
EventType=""刷新主页"" EventData=""/""
|
||||
Logo=""M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z"" />
|
||||
</local:MyCard>"
|
||||
Case 1 '回声洞
|
||||
Log("[Page] 主页预设:回声洞")
|
||||
Content = "
|
||||
<local:MyCard Title=""回声洞"" Margin=""0,0,0,15"">
|
||||
<TextBlock Margin=""25,38,23,15"" FontSize=""13.5"" IsHitTestVisible=""False"" Text=""{cave}"" TextWrapping=""Wrap"" Foreground=""{DynamicResource ColorBrush1}"" />
|
||||
<local:MyIconButton Height=""22"" Width=""22"" Margin=""9"" VerticalAlignment=""Top"" HorizontalAlignment=""Right""
|
||||
EventType=""刷新主页"" EventData=""/""
|
||||
Logo=""M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z"" />
|
||||
</local:MyCard>"
|
||||
Case 2 '新闻
|
||||
Log("[Page] 主页预设:Minecraft 新闻")
|
||||
Url = "http://pcl.mcnews.thestack.top"
|
||||
GoTo Download
|
||||
Case 3 '简单主页
|
||||
Log("[Page] 主页预设:简单主页")
|
||||
Url = "https://cdn.wearclub.online/PCL/Custom.xaml"
|
||||
GoTo Download
|
||||
End Select
|
||||
End Select
|
||||
RunInUi(Sub() LoadContent(Content))
|
||||
End Sub
|
||||
Private RefreshLock As New Object
|
||||
|
||||
'联网获取自定义主页文件
|
||||
Private OnlineLoader As New LoaderTask(Of String, Integer)("自定义主页获取", AddressOf OnlineLoaderSub) With {.ReloadTimeout = 10 * 60 * 1000}
|
||||
Private Sub OnlineLoaderSub(Task As LoaderTask(Of String, Integer))
|
||||
Try
|
||||
IsRefreshing = True
|
||||
PanCustom.Children.Clear()
|
||||
RunInNewThread(Sub()
|
||||
Dim FileContent As String = ""
|
||||
Select Case Setup.Get("UiCustomType")
|
||||
Case 0
|
||||
'啥也不干
|
||||
Case 1
|
||||
'加载本地文件
|
||||
FileContent = ReadFile(Path & "PCL\Custom.xaml") 'ReadFile 会进行存在检测
|
||||
Log("[System] 尝试从本地文件读取主页自定义数据(" & FileContent.Length & ")")
|
||||
Case 2
|
||||
'加载联网文件
|
||||
Try
|
||||
Dim Link As String = Setup.Get("UiCustomNet")
|
||||
If Link = LatestFileLink Then
|
||||
FileContent = LatestFileContent
|
||||
Log("[System] 尝试缓存加载主页自定义数据(" & FileContent.Length & ")")
|
||||
ElseIf Not String.IsNullOrWhiteSpace(Link) Then
|
||||
Log("[System] 开始从网络读取主页自定义数据(" & Link & ")")
|
||||
FileContent = NetGetCodeByRequestRetry(Link)
|
||||
Log("[System] 尝试从网络读取主页自定义数据(" & FileContent.Length & ")")
|
||||
End If
|
||||
LatestFileLink = Link
|
||||
LatestFileContent = FileContent
|
||||
Catch ex As Exception
|
||||
Log(ex, "获取 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
LatestFileLink = ""
|
||||
End Try
|
||||
End Select
|
||||
If FileContent = "" Then
|
||||
IsRefreshing = False
|
||||
Exit Sub
|
||||
End If
|
||||
FileContent = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & FileContent & "</StackPanel>"
|
||||
FileContent = FileContent.Replace("{path}", Path)
|
||||
RunInUi(Sub()
|
||||
Try
|
||||
Log("[System] 加载主页自定义数据")
|
||||
PanCustom.Children.Add(GetObjectFromXML(FileContent))
|
||||
IsRefreshing = False
|
||||
Catch ex As Exception
|
||||
IsRefreshing = False
|
||||
Log("[System] 自定义信息内容:" & vbCrLf & FileContent)
|
||||
If MyMsgBox(ex.Message, "加载自定义主页失败", "重试", "取消") = 1 Then
|
||||
LatestFileLink = Nothing
|
||||
LatestFileContent = ""
|
||||
RefreshCustom()
|
||||
End If
|
||||
End Try
|
||||
End Sub)
|
||||
End Sub, "主页自定义刷新")
|
||||
'获取版本校验地址
|
||||
Dim VersionAddress As String
|
||||
If Task.Input.Contains(".xaml") Then
|
||||
VersionAddress = Task.Input.Replace(".xaml", ".xaml.ini")
|
||||
Else
|
||||
VersionAddress = Task.Input.Split("?").First
|
||||
If Not VersionAddress.EndsWith("/") Then VersionAddress += "/"
|
||||
VersionAddress += "version"
|
||||
If Task.Input.Contains("?") Then VersionAddress += Task.Input.Split("?").Last
|
||||
End If
|
||||
'校验版本
|
||||
Dim Version As String = ""
|
||||
Dim NeedDownload As Boolean = True
|
||||
Try
|
||||
Version = NetGetCodeByRequestOnce(VersionAddress, Timeout:=10000)
|
||||
If Version.Length > 100 Then Throw New Exception($"获取的自定义主页版本过长({Version.Length} 字符)")
|
||||
Dim CurrentVersion As String = Setup.Get("CacheSavedPageVersion")
|
||||
If Version <> "" AndAlso CurrentVersion <> "" AndAlso Version = CurrentVersion Then
|
||||
Log($"[Page] 当前缓存的自定义主页已为最新,当前版本:{Version},检查源:{VersionAddress}")
|
||||
NeedDownload = False
|
||||
Else
|
||||
Log($"[Page] 需要下载联网自定义主页,当前版本:{Version},检查源:{VersionAddress}")
|
||||
End If
|
||||
Catch exx As Exception
|
||||
Log(exx, $"联网获取自定义主页版本失败", LogLevel.Developer)
|
||||
Log($"[Page] 无法检查联网自定义主页版本,将直接下载,检查源:{VersionAddress}")
|
||||
End Try
|
||||
'实际下载
|
||||
If NeedDownload Then
|
||||
Dim FileContent As String = NetGetCodeByRequestRetry(Task.Input)
|
||||
Log($"[Page] 已联网下载自定义主页,内容长度:{FileContent.Length},来源:{Task.Input}")
|
||||
Setup.Set("CacheSavedPageUrl", Task.Input)
|
||||
Setup.Set("CacheSavedPageVersion", Version)
|
||||
WriteFile(PathTemp & "Cache\Custom.xaml", FileContent)
|
||||
End If
|
||||
'要求刷新
|
||||
Refresh()
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 PCL 主页自定义信息失败", LogLevel.Msgbox)
|
||||
IsRefreshing = False
|
||||
If Setup.Get("CacheSavedPageVersion") = "" Then
|
||||
Log(ex, $"联网下载自定义主页失败({Task.Input})", LogLevel.Msgbox)
|
||||
Else
|
||||
Log(ex, $"联网下载自定义主页失败({Task.Input})")
|
||||
End If
|
||||
Task.Input = ""
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Public Sub ForceRefresh(Optional ShowHint As Boolean = True)
|
||||
ShouldRefresh = True
|
||||
LatestFileContent = ""
|
||||
LatestFileLink = Nothing
|
||||
If ShowHint Then Hint("已刷新主页!", HintType.Finish)
|
||||
FrmMain.PageChange(FormMain.PageType.Launch)
|
||||
Init()
|
||||
''' <summary>
|
||||
''' 立即强制刷新自定义主页。
|
||||
''' 必须在 UI 线程调用。
|
||||
''' </summary>
|
||||
Public Sub ForceRefresh()
|
||||
Log("[Page] 要求强制刷新自定义主页")
|
||||
ClearCache()
|
||||
'实际的刷新
|
||||
If FrmMain.PageCurrent.Page = FormMain.PageType.Launch Then
|
||||
PanBack.ScrollToHome()
|
||||
Refresh()
|
||||
Else
|
||||
FrmMain.PageChange(FormMain.PageType.Launch)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 清空自定义主页缓存信息。
|
||||
''' </summary>
|
||||
Private Sub ClearCache()
|
||||
LoadedContentHash = -1
|
||||
OnlineLoader.Input = ""
|
||||
Setup.Set("CacheSavedPageUrl", "")
|
||||
Setup.Set("CacheSavedPageVersion", "")
|
||||
Log("[Page] 已清空自定义主页缓存")
|
||||
End Sub
|
||||
|
||||
''' <summary>
|
||||
''' 从文本内容中加载自定义主页。
|
||||
''' 必须在 UI 线程调用。
|
||||
''' </summary>
|
||||
Private Sub LoadContent(Content As String)
|
||||
SyncLock LoadContentLock
|
||||
'如果加载目标内容一致则不加载
|
||||
Dim Hash = Content.GetHashCode()
|
||||
If Hash = LoadedContentHash Then Exit Sub
|
||||
LoadedContentHash = Hash
|
||||
'实际加载内容
|
||||
PanCustom.Children.Clear()
|
||||
If String.IsNullOrWhiteSpace(Content) Then
|
||||
Log($"[Page] 实例化:清空自定义主页 UI,来源为空")
|
||||
Return
|
||||
End If
|
||||
Content = "<StackPanel xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2"">" & Content & "</StackPanel>"
|
||||
Content = HelpArgumentReplace(Content)
|
||||
Log($"[Page] 实例化:加载自定义主页 UI,最终内容长度:{Content.Count}")
|
||||
Try
|
||||
PanCustom.Children.Add(GetObjectFromXML(Content))
|
||||
Catch ex As Exception
|
||||
Log("[Page] 加载失败的自定义主页内容:" & vbCrLf & Content)
|
||||
If MyMsgBox($"自定义主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{ex.Message}", "加载自定义主页失败", "重试", "取消") = 1 Then
|
||||
GoTo Refresh '防止 SyncLock 死锁
|
||||
End If
|
||||
End Try
|
||||
End SyncLock
|
||||
Return
|
||||
Refresh:
|
||||
ForceRefresh()
|
||||
End Sub
|
||||
Private LoadedContentHash As Integer = -1
|
||||
Private LoadContentLock As New Object
|
||||
|
||||
#End Region
|
||||
|
||||
End Class
|
||||
@@ -17,7 +17,7 @@ Public Class PageLoginLegacy
|
||||
Public Sub Reload(KeepInput As Boolean)
|
||||
If KeepInput AndAlso IsReloaded Then '避免第一次就以 KeepInput 的方式加载,导致文本框里没东西
|
||||
'保留输入,只刷新下拉框列表
|
||||
Dim Input As String = ComboName.Text
|
||||
Dim Input As String = ComboName.Text.Trim
|
||||
ComboName.ItemsSource = If(Setup.Get("LoginLegacyName") = "", Nothing, Setup.Get("LoginLegacyName").ToString.Split("¨"))
|
||||
ComboName.Text = Input
|
||||
Else
|
||||
@@ -26,7 +26,7 @@ Public Class PageLoginLegacy
|
||||
ComboName.ItemsSource = Nothing
|
||||
Else
|
||||
ComboName.ItemsSource = Setup.Get("LoginLegacyName").ToString.Split("¨")
|
||||
ComboName.Text = Setup.Get("LoginLegacyName").ToString.Split("¨")(0)
|
||||
ComboName.Text = Setup.Get("LoginLegacyName").ToString.Split("¨")(0).Trim
|
||||
End If
|
||||
End If
|
||||
IsReloaded = True
|
||||
@@ -50,17 +50,14 @@ Public Class PageLoginLegacy
|
||||
Return IsVaild(GetLoginData())
|
||||
End Function
|
||||
|
||||
Private Sub ComboName_PreviewKeyDown(sender As Object, e As Input.KeyEventArgs) Handles ComboName.PreviewKeyDown
|
||||
If e.Key = Key.Space Then
|
||||
Dim Index = CType(ComboName.Template.FindName("PART_EditableTextBox", ComboName), MyTextBox).CaretIndex
|
||||
If Index = ComboName.Text.Length OrElse Index = 0 Then e.Handled = True
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ComboLegacy_TextChanged(sender As Object, e As TextChangedEventArgs) Handles ComboName.TextChanged
|
||||
If ComboName.Text.EndsWith(" ") Then
|
||||
ComboName.Text = ComboName.Text.Trim()
|
||||
SendKeys.SendWait("{END}")
|
||||
ElseIf ComboName.Text.StartsWith(" ") Then
|
||||
ComboName.Text = ComboName.Text.Trim()
|
||||
SendKeys.SendWait("{HOME}")
|
||||
End If
|
||||
If Setup.Get("LaunchSkinType") = 0 Then
|
||||
PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
|
||||
End If
|
||||
If Setup.Get("LaunchSkinType") = 0 Then PageLaunchLeft.SkinLegacy.Start(IsForceRestart:=True)
|
||||
HintChinese.Visibility = If(RegexCheck(ComboName.Text, "^[0-9A-Za-z_]*$"), Visibility.Collapsed, Visibility.Visible)
|
||||
End Sub
|
||||
Private Sub Skin_Click() Handles Skin.Click
|
||||
|
||||
@@ -103,8 +103,8 @@
|
||||
End Sub
|
||||
'账号迁移入口
|
||||
Private Sub BtnMigration_Click(sender As Object, e As EventArgs) Handles BtnMigration.Click
|
||||
MyMsgBox("在迁移过程中,你可能需要设置你的档案信息,此时请注意让年龄大于 18 岁,否则可能导致无法登录!", "迁移提示", "继续", ForceWait:=True)
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/account-security")
|
||||
MyMsgBox($"请在接下来要打开的页面登录你的 Mojang 账号。{vbCrLf}在迁移时,你可能需要设置档案信息,请注意让年龄大于 18 岁,否则可能会导致无法登录!", "迁移提示", "继续", ForceWait:=True)
|
||||
OpenWebsite("https://www.minecraft.net/zh-hans/login")
|
||||
End Sub
|
||||
|
||||
End Class
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
Private IsLoad As Boolean = False
|
||||
Private Sub OnLoaded() Handles Me.Loaded
|
||||
FormMain.EndProgramForce(Result.Aborted)
|
||||
If IsLoad Then Exit Sub
|
||||
IsLoad = True
|
||||
'启动监视线程
|
||||
|
||||
@@ -11,6 +11,7 @@ Public Class PageLinkIoi
|
||||
'页面初始化
|
||||
Private IsLoad As Boolean = False
|
||||
Private Sub MeLoaded() Handles Me.Loaded
|
||||
FormMain.EndProgramForce(Result.Aborted)
|
||||
|
||||
'重复加载部分
|
||||
PanBack.ScrollToHome()
|
||||
@@ -132,7 +133,7 @@ StartDownload:
|
||||
Log("[IOI] 解压联机模块以完成下载")
|
||||
If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe")
|
||||
If File.Exists(PathIoi) Then File.Delete(PathIoi)
|
||||
Compression.ZipFile.ExtractToDirectory(PathTemp & "联机模块.zip", PathTemp)
|
||||
ExtractFile(PathTemp & "联机模块.zip", PathTemp)
|
||||
File.Delete(PathTemp & "联机模块.zip")
|
||||
CopyFile(PathTemp & "ioi.exe", PathIoi)
|
||||
File.Delete(PathTemp & "ioi.exe")
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
If String.IsNullOrEmpty(Entry.XamlContent) Then Throw New Exception("帮助 xaml 文件为空")
|
||||
Me.Entry = Entry
|
||||
PanCustom.Children.Clear()
|
||||
FileContent = FileContent.Replace("{path}", Path)
|
||||
FileContent = HelpArgumentReplace(FileContent)
|
||||
PanCustom.Children.Add(GetObjectFromXML(FileContent))
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
|
||||
@@ -259,6 +259,7 @@
|
||||
Directory.CreateDirectory(Path & ".minecraft\")
|
||||
Directory.CreateDirectory(Path & ".minecraft\versions\")
|
||||
Setup.Set("LaunchFolderSelect", "$.minecraft\")
|
||||
McFolderLauncherProfilesJsonCreate(Path & ".minecraft\")
|
||||
Hint("新建 .minecraft 文件夹成功!", HintType.Finish)
|
||||
End If
|
||||
McFolderListLoader.Start(IsForceRestart:=True)
|
||||
|
||||
@@ -219,11 +219,12 @@
|
||||
If(IsHintIndie, vbCrLf & "由于该版本开启了版本隔离,删除版本时该版本对应的存档、资源包、Mod 等文件也将被一并删除!", ""),
|
||||
"版本删除确认", , "取消",, True)
|
||||
Case 1
|
||||
IniClearCache(Version.Path & "PCL\Setup.ini")
|
||||
If IsShiftPressed Then
|
||||
DeleteDirectory(Version.Path)
|
||||
Hint("版本 " & Version.Name & " 已永久删除!", HintType.Finish)
|
||||
Else
|
||||
FileIO.FileSystem.DeleteDirectory(Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
|
||||
FileIO.FileSystem.DeleteDirectory(Version.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
|
||||
Hint("版本 " & Version.Name & " 已删除到回收站!", HintType.Finish)
|
||||
End If
|
||||
Case 2
|
||||
@@ -251,6 +252,8 @@
|
||||
'同时出现在当前卡片与收藏夹
|
||||
LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
|
||||
End If
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "删除版本 " & Version.Name & " 被主动取消")
|
||||
Catch ex As Exception
|
||||
Log(ex, "删除版本 " & Version.Name & " 失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
<local:MyComboBoxItem Content="全屏" />
|
||||
<local:MyComboBoxItem Content="默认大小" IsSelected="True" ToolTip="以 854×480 的默认窗口大小启动 Minecraft" />
|
||||
<local:MyComboBoxItem Content="与启动器窗口大小一致" />
|
||||
<local:MyComboBoxItem Content="自定义大小" ToolTip="注意:Forge 1.12.2 等个别版本存在 Bug,会导致高度设定无效" />
|
||||
<local:MyComboBoxItem Content="自定义大小" ToolTip="注意:Forge 1.12 存在 Bug,会导致高度设定无效" />
|
||||
<local:MyComboBoxItem Content="最大化" />
|
||||
<local:MyComboBoxItem Content="保持游戏默认" />
|
||||
</local:MyComboBox>
|
||||
@@ -165,7 +165,7 @@
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="游戏内存">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
<local:MyHint x:Name="LabRamWarn" CanClose="False" Text="32 位 Java 最多只能分配 1 GB 内存,建议安装 64 位 Java!" IsWarn="True" Margin="0,0,0,12" />
|
||||
<local:MyHint x:Name="LabRamWarn" Text="32 位 Java 最多只能分配 1 GB 内存,建议安装 64 位 Java!" IsWarn="True" Margin="0,0,0,12" />
|
||||
<Grid Margin="0,0,0,3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -213,7 +213,7 @@
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="高级设置" IsSwaped="True" CanSwap="True">
|
||||
<local:MyCard Margin="0,0,0,15" Title="高级启动选项" IsSwaped="True" CanSwap="True">
|
||||
<Grid Margin="25,40,25,15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Name" />
|
||||
@@ -240,9 +240,9 @@
|
||||
Text="等待命令执行完成后再继续启动" Margin="0,8,0,7" />
|
||||
<StackPanel Margin="0,12,0,4" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<local:MyCheckBox Text="忽略 Java 兼容性警告" x:Name="CheckAdvanceJava" Tag="LaunchAdvanceJava" Margin="0,0,60,0"
|
||||
ToolTip="如果在设置中手动选择了与当前版本不兼容的 Java,则自动跳过兼容性警告弹窗,强制使用手动选择的 Java。" />
|
||||
ToolTip="警告:强烈推荐在版本设置中关闭特定版本的警告,而不是在这里关闭所有版本的警告!
如果在设置中手动选择了与当前版本不兼容的 Java,则自动跳过兼容性警告弹窗,强制使用手动选择的 Java。" />
|
||||
<local:MyCheckBox Text="关闭文件校验" x:Name="CheckAdvanceAssets" Tag="LaunchAdvanceAssets"
|
||||
ToolTip="完全不更改 assets;不校验 libraries、第三方登录库与版本主 jar 文件是否被修改。
如果你没有修改相关文件,请勿勾选此项。
强烈推荐在版本设置中关闭特定版本的校验,而不是在这里关闭所有版本的校验。" />
|
||||
ToolTip="警告:强烈推荐在版本设置中关闭特定版本的校验,而不是在这里关闭所有版本的校验!
完全不更改 assets;不校验 libraries、第三方登录库与版本主 jar 文件是否被修改。
如果你没有修改相关文件,请勿勾选此项。" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
|
||||
@@ -433,7 +433,7 @@ PreFin:
|
||||
Try
|
||||
For Each Java In Sort(JavaList.Clone(), Function(l, r) l.VersionCode < r.VersionCode)
|
||||
Dim ListItem = New MyComboBoxItem With {.Content = Java.ToString, .ToolTip = Java.PathFolder, .Tag = Java}
|
||||
ToolTipService.SetHorizontalOffset(ListItem, 0)
|
||||
ToolTipService.SetHorizontalOffset(ListItem, 400)
|
||||
ComboArgumentJava.Items.Add(ListItem)
|
||||
'判断人为选中
|
||||
If SelectedBySetup = "" Then Continue For
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<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 测试版(快照、预发布版)更新时弹窗提示" />
|
||||
<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>
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="系统">
|
||||
|
||||
@@ -187,6 +187,7 @@
|
||||
End Try
|
||||
FrmSpeedRight.PanMain.Children.Insert(0, Card)
|
||||
RightCards.Add(Loader.Name, Card)
|
||||
Log($"[Watcher] 新建下载管理卡片:{Loader.Name}")
|
||||
'添加取消按钮
|
||||
Dim Cancel As New MyIconButton With {.Name = "BtnCancel", .Logo = "F1 M2,0 L0,2 8,10 0,18 2,20 10,12 18,20 20,18 12,10 20,2 18,0 10,8 2,0Z", .Height = 20, .Margin = New Thickness(0, 10, 10, 0), .LogoScale = 1.1, .HorizontalAlignment = HorizontalAlignment.Right, .VerticalAlignment = VerticalAlignment.Top}
|
||||
Card.Children.Add(Cancel)
|
||||
@@ -194,6 +195,7 @@
|
||||
AniDispose(sender, False)
|
||||
AniDispose(Card, True, Sub() If FrmSpeedRight.PanMain.Children.Count = 0 AndAlso FrmMain.PageCurrent = FormMain.PageType.DownloadManager Then FrmMain.PageBack())
|
||||
RightCards.Remove(Loader.Name)
|
||||
Log($"[Watcher] 关闭下载管理卡片:{Loader.Name}")
|
||||
RunInThread(Sub() Loader.Abort())
|
||||
End Sub
|
||||
'如果已经失败,再刷新一次,修改成失败的控件
|
||||
@@ -217,6 +219,7 @@
|
||||
Dim Card As Grid = RightCards(Loader.Name)
|
||||
FrmSpeedRight.PanMain.Children.Remove(Card)
|
||||
RightCards.Remove(Loader.Name)
|
||||
Log($"[Watcher] 移除下载管理卡片:{Loader.Name}")
|
||||
End Sub)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
@@ -7,22 +7,20 @@
|
||||
<Grid>
|
||||
<Grid x:Name="PanAllBack">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel x:Name="PanMain" Margin="25,10" Grid.IsSharedSizeScope="True">
|
||||
<StackPanel x:Name="PanMain" Margin="25,10,25,10" Grid.IsSharedSizeScope="True"> <!-- 若显示多选模块则为 92 -->
|
||||
<local:MySearchBox Margin="0,15" HintText="搜索 Mod" x:Name="SearchBox" />
|
||||
<local:MyCard Grid.Row="0" Margin="0,0,0,15" Title="管理" x:Name="PanManage">
|
||||
<StackPanel Margin="25,40,15,20">
|
||||
<TextBlock Margin="0,0,0,11" Text="左键点击某个 Mod 可以单独启用或禁用它。将 Mod 文件拖入窗口可以快速安装 Mod。" TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,0,0,11" Text="点击红石灯图标可以快速启用或禁用单个 Mod。将 Mod 文件拖入窗口即可快速安装。" TextWrapping="Wrap" />
|
||||
<Grid Height="35">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<local:MyButton x:Name="BtnManageOpen" Grid.Column="0" MinWidth="140" Text="打开 Mod 文件夹" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
|
||||
<local:MyButton x:Name="BtnManageEnabled" Grid.Column="1" MinWidth="140" Text="全部启用" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnManageDisabled" Grid.Column="2" MinWidth="140" Text="全部禁用" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnManageCheck" Grid.Column="3" MinWidth="140" Text="检查 Mod" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ToolTip="快速检查 Mod 的版本要求、重复、前置缺失等问题。
结果仅供参考,由于部分 Mod 标注的信息有误,可能并不准确。" Visibility="Collapsed" />
|
||||
<local:MyButton x:Name="BtnManageSelectAll" Grid.Column="1" MinWidth="140" Text="全选" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
|
||||
<local:MyButton x:Name="BtnManageCheck" Grid.Column="2" MinWidth="140" Text="检查 Mod" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ToolTip="快速检查 Mod 的版本要求、重复、前置缺失等问题。
结果仅供参考,由于部分 Mod 标注的信息有误,可能并不准确。" Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
@@ -47,5 +45,22 @@
|
||||
<local:MyCard HorizontalAlignment="Center" VerticalAlignment="Center" Margin="40,0" SnapsToDevicePixels="True" x:Name="PanLoad" UseAnimation="False">
|
||||
<local:MyLoading Text="正在加载 Mod 列表" Margin="20,20,20,17" x:Name="Load" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</local:MyCard>
|
||||
<local:MyCard x:Name="CardSelect" Visibility="Collapsed" Opacity="0"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="25,25,25,0" UseAnimation="False" HasMouseAnimation="False">
|
||||
<local:MyCard.RenderTransform>
|
||||
<TranslateTransform x:Name="TransSelect" Y="-10" />
|
||||
</local:MyCard.RenderTransform>
|
||||
<TextBlock x:Name="LabSelect" Text="已选择 8 个文件" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="9" Foreground="{DynamicResource ColorBrush2}" />
|
||||
<StackPanel Orientation="Horizontal" Margin="5,28,5,5">
|
||||
<local:MyIconTextButton x:Name="BtnSelectEnable" Text="启用"
|
||||
LogoScale="1.05" Logo="M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 921.6a409.6 409.6 0 1 1 409.6-409.6 409.6 409.6 0 0 1-409.6 409.6z M716.8 339.968l-256 253.44L328.192 460.8A51.2 51.2 0 0 0 256 532.992l168.448 168.96a51.2 51.2 0 0 0 72.704 0l289.28-289.792A51.2 51.2 0 0 0 716.8 339.968z" />
|
||||
<local:MyIconTextButton x:Name="BtnSelectDisable" Text="禁用"
|
||||
LogoScale="0.98" Logo="M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m409.6 512a404.48 404.48 0 0 1-86.528 250.88L261.12 188.928A404.48 404.48 0 0 1 512 102.4a409.6 409.6 0 0 1 409.6 409.6zM102.4 512a404.48 404.48 0 0 1 86.528-250.88L762.88 835.072A404.48 404.48 0 0 1 512 921.6a409.6 409.6 0 0 1-409.6-409.6z" />
|
||||
<local:MyIconTextButton x:Name="BtnSelectDelete" Text="删除"
|
||||
LogoScale="0.96" Logo="M520.192 0C408.43 0 317.44 82.87 313.563 186.734H52.736c-29.038 0-52.663 21.943-52.663 49.079s23.625 49.152 52.663 49.152h58.075v550.473c0 103.35 75.118 187.757 167.717 187.757h472.43c92.599 0 167.716-83.894 167.716-187.757V285.477h52.59c29.038 0 52.59-21.943 52.663-49.08-0.073-27.135-23.625-49.151-52.663-49.151H726.235C723.237 83.017 631.955 0 520.192 0zM404.846 177.957c3.803-50.03 50.176-89.015 107.447-89.015 57.197 0 103.57 38.985 106.788 89.015H404.92zM284.379 933.669c-33.353 0-69.997-39.351-69.997-95.525v-549.01H833.39v549.522c0 56.247-36.645 95.525-69.998 95.525H284.379v-0.512z M357.23 800.695a48.274 48.274 0 0 0 47.616-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.69 49.006V751.69c0 27.282 20.846 49.006 47.617 49.006z m166.62 0a48.274 48.274 0 0 0 47.688-49.006V471.7a48.274 48.274 0 0 0-47.689-49.08 48.274 48.274 0 0 0-47.543 49.006V751.69c0 27.282 21.431 49.006 47.543 49.006z m142.92 0a48.274 48.274 0 0 0 47.543-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.616 49.006V751.69c0 27.282 20.773 49.006 47.543 49.006z" />
|
||||
<local:MyIconTextButton x:Name="BtnSelectCancel" Text="取消选择"
|
||||
LogoScale="0.8" Logo="M867.648 951.296 512 595.648l-355.648 355.648c-11.52 11.52-30.272 11.52-41.856 0L72.64 909.44c-11.52-11.52-11.52-30.272 0-41.856L428.352 512 72.64 156.352c-11.52-11.52-11.52-30.272 0-41.856l41.856-41.856c11.52-11.52 30.272-11.52 41.856 0L512 428.288l355.648-355.648c11.52-11.52 30.272-11.52 41.856 0l41.856 41.856c11.52 11.52 11.52 30.272 0 41.856L595.648 512l355.648 355.648c11.52 11.52 11.52 30.272 0 41.856l-41.856 41.856C897.984 962.88 879.168 962.88 867.648 951.296L867.648 951.296z" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
</Grid>
|
||||
</local:MyPageRight>
|
||||
@@ -6,7 +6,11 @@
|
||||
Public Sub PageOther_Loaded() Handles Me.Loaded
|
||||
|
||||
PanBack.ScrollToHome()
|
||||
AniControlEnabled += 1
|
||||
SelectedMods.Clear()
|
||||
RefreshList()
|
||||
ChangeAllSelected(False)
|
||||
AniControlEnabled -= 1
|
||||
|
||||
'非重复加载部分
|
||||
If IsLoad Then Exit Sub
|
||||
@@ -42,6 +46,7 @@
|
||||
|
||||
#Region "UI 化"
|
||||
|
||||
Private PanItems As StackPanel
|
||||
''' <summary>
|
||||
''' 将 Mod 列表加载为 UI。
|
||||
''' </summary>
|
||||
@@ -62,19 +67,14 @@
|
||||
|
||||
SearchBox.Text = ""
|
||||
'建立 StackPanel
|
||||
Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, If(List.Count > 0, 20, 0)), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
|
||||
PanItems = New StackPanel With {.Margin = New Thickness(18, MyCard.SwapedHeight, 18, If(List.Count > 0, 20, 0)), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
|
||||
For Each ModEntity As McMod In List
|
||||
NewStack.Children.Add(McModListItem(ModEntity))
|
||||
PanItems.Children.Add(McModListItem(ModEntity))
|
||||
Next
|
||||
'建立 MyCard
|
||||
Dim NewCard As New MyCard With {.Title = McModGetTitle(List), .Margin = New Thickness(0, 0, 0, 15)}
|
||||
NewCard.Children.Add(NewStack)
|
||||
NewCard.Children.Add(PanItems)
|
||||
PanList.Children.Add(NewCard)
|
||||
'显示提示
|
||||
If List.Count > 0 AndAlso Not Setup.Get("HintModDisable") Then
|
||||
Setup.Set("HintModDisable", True)
|
||||
Hint("直接点击某个 Mod 项即可将它禁用!")
|
||||
End If
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
|
||||
@@ -88,20 +88,12 @@
|
||||
For Each ModEntity As McMod In List
|
||||
Counter(ModEntity.State) += 1
|
||||
Next
|
||||
If List.Count = 0 Then
|
||||
'全空
|
||||
Return "未找到任何 Mod"
|
||||
ElseIf Counter(McMod.McModState.Disabled) = 0 AndAlso Counter(McMod.McModState.Unavaliable) = 0 Then
|
||||
'只有启用的 Mod
|
||||
Return "Mod 列表(" & Counter(McMod.McModState.Fine) & ")"
|
||||
Else
|
||||
'混合种类
|
||||
Dim TypeList As New List(Of String)
|
||||
If Counter(McMod.McModState.Fine) > 0 Then TypeList.Add("启用 " & Counter(McMod.McModState.Fine))
|
||||
If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
|
||||
If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
|
||||
Return "Mod 列表(" & Join(TypeList, ",") & ")"
|
||||
End If
|
||||
If List.Count = 0 Then Return "未找到任何 Mod"
|
||||
Dim TypeList As New List(Of String)
|
||||
If Counter(McMod.McModState.Fine) > 0 Then TypeList.Add("启用 " & Counter(McMod.McModState.Fine))
|
||||
If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
|
||||
If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
|
||||
Return "Mod 列表(" & Join(TypeList, ",") & ")"
|
||||
End Function
|
||||
Private Function McModListItem(Entry As McMod) As MyListItem
|
||||
'图标
|
||||
@@ -138,16 +130,21 @@
|
||||
' Desc += " : " & Join(DepList, ", ")
|
||||
'End If
|
||||
'实例化
|
||||
Dim NewItem As New MyListItem With {.Logo = Logo, .SnapsToDevicePixels = True, .Title = Title, .Info = Desc, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry}
|
||||
NewItem.PaddingRight = If(Entry.IsFileAvailable, 73, 55)
|
||||
'事件
|
||||
NewItem.ContentHandler = AddressOf McModContent
|
||||
AniControlEnabled += 1
|
||||
Dim NewItem As New MyListItem With {
|
||||
.LogoClickable = True, .Logo = Logo, .SnapsToDevicePixels = True, .Title = Title, .Info = Desc, .Height = 42, .Tag = Entry,
|
||||
.Type = MyListItem.CheckType.CheckBox, .IsScaleAnimationEnabled = False,
|
||||
.PaddingRight = 73,
|
||||
.ContentHandler = AddressOf McModContent, .Checked = SelectedMods.Contains(Entry.RawFileName)
|
||||
}
|
||||
AniControlEnabled -= 1
|
||||
Return NewItem
|
||||
End Function
|
||||
Private Sub McModContent(sender As MyListItem, e As EventArgs)
|
||||
Dim ModEntity As McMod = sender.Tag
|
||||
'注册点击事件
|
||||
AddHandler sender.Click, AddressOf Item_Click
|
||||
AddHandler sender.Changed, AddressOf CheckChanged
|
||||
AddHandler sender.LogoClick, AddressOf ItemLogo_Click
|
||||
'图标按钮
|
||||
Dim BtnDel As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete, .Tag = sender}
|
||||
BtnDel.ToolTip = "删除"
|
||||
@@ -161,18 +158,14 @@
|
||||
ToolTipService.SetVerticalOffset(BtnOpen, 30)
|
||||
ToolTipService.SetHorizontalOffset(BtnOpen, 2)
|
||||
AddHandler BtnOpen.Click, AddressOf Open_Click
|
||||
If ModEntity.IsFileAvailable Then
|
||||
Dim BtnCont As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .Tag = sender}
|
||||
BtnCont.ToolTip = "详情"
|
||||
ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
|
||||
ToolTipService.SetVerticalOffset(BtnCont, 30)
|
||||
ToolTipService.SetHorizontalOffset(BtnCont, 2)
|
||||
AddHandler BtnCont.Click, AddressOf Info_Click
|
||||
AddHandler sender.MouseRightButtonDown, AddressOf Info_Click
|
||||
sender.Buttons = {BtnCont, BtnOpen, BtnDel}
|
||||
Else
|
||||
sender.Buttons = {BtnOpen, BtnDel}
|
||||
End If
|
||||
Dim BtnCont As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .Tag = sender}
|
||||
BtnCont.ToolTip = "详情"
|
||||
ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
|
||||
ToolTipService.SetVerticalOffset(BtnCont, 30)
|
||||
ToolTipService.SetHorizontalOffset(BtnCont, 2)
|
||||
AddHandler BtnCont.Click, AddressOf Info_Click
|
||||
AddHandler sender.MouseRightButtonDown, AddressOf Info_Click
|
||||
sender.Buttons = {BtnCont, BtnOpen, BtnDel}
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
@@ -210,13 +203,150 @@
|
||||
#End If
|
||||
|
||||
''' <summary>
|
||||
''' 一键启用 / 禁用。
|
||||
''' 全选。
|
||||
''' </summary>
|
||||
Private Sub BtnManageChange_Click(sender As MyButton, e As EventArgs) Handles BtnManageEnabled.Click, BtnManageDisabled.Click
|
||||
Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
|
||||
ChangeAllSelected(True)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "选择"
|
||||
|
||||
'选择的 Mod 的路径(不含 .disabled)
|
||||
Public SelectedMods As New List(Of String)
|
||||
|
||||
'单项切换选择状态
|
||||
Public Sub CheckChanged(sender As MyListItem, e As RouteEventArgs)
|
||||
If AniControlEnabled <> 0 Then Return
|
||||
'更新选择了的内容
|
||||
Dim SelectedKey As String = CType(sender.Tag, McMod).RawFileName
|
||||
If sender.Checked Then
|
||||
If Not SelectedMods.Contains(SelectedKey) Then SelectedMods.Add(SelectedKey)
|
||||
Else
|
||||
SelectedMods.Remove(SelectedKey)
|
||||
End If
|
||||
'更新下边栏 UI
|
||||
RefreshBottomBar()
|
||||
End Sub
|
||||
|
||||
'改变下边栏状态
|
||||
Private ShownCount As Integer = 0
|
||||
Private Sub RefreshBottomBar()
|
||||
'计数
|
||||
Dim NewCount As Integer = SelectedMods.Count
|
||||
Dim Selected = NewCount > 0
|
||||
If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
|
||||
'按钮可用性
|
||||
If Selected Then
|
||||
Dim HasEnabled As Boolean = False
|
||||
Dim HasDisabled As Boolean = False
|
||||
For Each ModEntity In McModLoader.Output
|
||||
If SelectedMods.Contains(ModEntity.RawFileName) Then
|
||||
If ModEntity.State = McMod.McModState.Fine Then
|
||||
HasEnabled = True
|
||||
ElseIf ModEntity.State = McMod.McModState.Disabled Then
|
||||
HasDisabled = True
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
BtnSelectDisable.IsEnabled = HasEnabled
|
||||
BtnSelectEnable.IsEnabled = HasDisabled
|
||||
End If
|
||||
'更新显示状态
|
||||
CardSelect.IsHitTestVisible = Selected
|
||||
If AniControlEnabled = 0 Then
|
||||
If Selected Then
|
||||
'仅在数量增加时播放出现/跳跃动画
|
||||
If ShownCount >= NewCount Then
|
||||
ShownCount = NewCount
|
||||
Return
|
||||
Else
|
||||
ShownCount = NewCount
|
||||
End If
|
||||
'出现/跳跃动画
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
|
||||
AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
|
||||
AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
|
||||
}, "Mod Sidebar")
|
||||
Else
|
||||
'不重复播放隐藏动画
|
||||
If ShownCount = 0 Then Return
|
||||
ShownCount = 0
|
||||
'隐藏动画
|
||||
AniStart({
|
||||
AaOpacity(CardSelect, -CardSelect.Opacity, 90),
|
||||
AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
|
||||
AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
|
||||
}, "Mod Sidebar")
|
||||
End If
|
||||
Else
|
||||
AniStop("Mod Sidebar")
|
||||
ShownCount = NewCount
|
||||
If Selected Then
|
||||
CardSelect.Visibility = Visibility.Visible
|
||||
CardSelect.Opacity = 1
|
||||
TransSelect.Y = -25
|
||||
Else
|
||||
CardSelect.Visibility = Visibility.Collapsed
|
||||
CardSelect.Opacity = 0
|
||||
TransSelect.Y = -10
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'切换所有项的选择状态
|
||||
Private Sub ChangeAllSelected(Value As Boolean)
|
||||
AniControlEnabled += 1
|
||||
SelectedMods.Clear()
|
||||
If IsSearching Then
|
||||
'搜索中
|
||||
For Each Item As MyListItem In PanSearchList.Children
|
||||
Item.Checked = Value
|
||||
If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
|
||||
Next
|
||||
If Not Value Then '只取消选择
|
||||
If PanItems IsNot Nothing Then
|
||||
For Each Item As MyListItem In PanItems.Children
|
||||
Item.Checked = Value
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
Else
|
||||
'非搜索中
|
||||
If PanItems IsNot Nothing Then
|
||||
For Each Item As MyListItem In PanItems.Children
|
||||
Item.Checked = Value
|
||||
If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
AniControlEnabled -= 1
|
||||
'更新下边栏 UI
|
||||
RefreshBottomBar()
|
||||
End Sub
|
||||
Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit
|
||||
Dim CacheAniControlEnabled = AniControlEnabled
|
||||
AniControlEnabled = 0
|
||||
ChangeAllSelected(False)
|
||||
AniControlEnabled += CacheAniControlEnabled
|
||||
End Sub
|
||||
Private Sub PageVersionMod_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
|
||||
If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "下边栏"
|
||||
|
||||
'启用 / 禁用
|
||||
Private Sub BtnSelectEorD_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
|
||||
Dim IsSuccessful As Boolean = True
|
||||
Dim SuccessfulCount As Integer = 0
|
||||
Dim IsDisable As Boolean = sender.Text.Contains("禁用")
|
||||
For Each ModEntity In McModLoader.Output
|
||||
Dim IsDisable As Boolean = sender.Equals(BtnSelectDisable)
|
||||
For Each ModEntity In McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
|
||||
Dim NewPath As String = Nothing
|
||||
If ModEntity.State = McMod.McModState.Fine And IsDisable Then
|
||||
'禁用
|
||||
@@ -225,7 +355,6 @@
|
||||
'启用
|
||||
NewPath = ModEntity.Path.Substring(0, ModEntity.Path.Count - ".disabled".Count)
|
||||
Else
|
||||
SuccessfulCount += 1
|
||||
Continue For
|
||||
End If
|
||||
'重命名
|
||||
@@ -233,27 +362,100 @@
|
||||
If File.Exists(NewPath) AndAlso Not File.Exists(ModEntity.Path) Then Continue For '因为未知原因 Mod 的状态已经切换完了
|
||||
File.Delete(NewPath)
|
||||
FileSystem.Rename(ModEntity.Path, NewPath)
|
||||
SuccessfulCount += 1
|
||||
Catch ex As FileNotFoundException
|
||||
Log(ex, "未找到需要重命名的 Mod(" & If(ex.FileName, "null") & ")", LogLevel.Feedback)
|
||||
RefreshList()
|
||||
Exit Sub
|
||||
Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
|
||||
RefreshList(True)
|
||||
Return
|
||||
Catch ex As Exception
|
||||
Log(ex, "全局状态改变中重命名 Mod 失败")
|
||||
Log(ex, $"批量重命名 Mod 失败({If(ModEntity.Path, "null")})")
|
||||
IsSuccessful = False
|
||||
End Try
|
||||
'更改 Loader 和 UI 中的列表
|
||||
Dim NewModEntity As New McMod(NewPath)
|
||||
Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
|
||||
McModLoader.Output.RemoveAt(IndexOfLoader)
|
||||
McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
|
||||
Dim List = If(IsSearching, PanSearchList, PanItems).Children
|
||||
Dim IndexOfUi As Integer = List.IndexOf(List.OfType(Of MyListItem).FirstOrDefault(Function(i) CType(i.Tag, McMod) Is ModEntity))
|
||||
If IndexOfUi = -1 Then Continue For '因为未知原因 Mod 的状态已经切换完了
|
||||
List.RemoveAt(IndexOfUi)
|
||||
List.Insert(IndexOfUi, McModListItem(NewModEntity))
|
||||
Next
|
||||
If Not IsSuccessful Then Hint("由于文件被占用,部分 Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
Log("[System] 已确认 " & SuccessfulCount & "/" & McModLoader.Output.Count & " 个 Mod 的状态与要求一致")
|
||||
RefreshList()
|
||||
If Not IsSearching Then
|
||||
'改变禁用数量的显示
|
||||
CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
|
||||
'更新加载器状态
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
|
||||
End If
|
||||
If Not IsSuccessful Then
|
||||
Hint("由于文件被占用,部分 Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
RefreshList(True)
|
||||
Else
|
||||
ChangeAllSelected(False)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'删除
|
||||
Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
|
||||
Dim IsSuccessful As Boolean = True
|
||||
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
|
||||
Dim DeleteList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
|
||||
For Each ModEntity In DeleteList
|
||||
'删除
|
||||
Try
|
||||
If IsShiftPressed Then
|
||||
File.Delete(ModEntity.Path)
|
||||
Else
|
||||
My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
|
||||
End If
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "批量删除 Mod 被主动取消")
|
||||
RefreshList(True)
|
||||
Return
|
||||
Catch ex As Exception
|
||||
Log(ex, $"批量删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
|
||||
IsSuccessful = False
|
||||
End Try
|
||||
'更改 Loader 和 UI 中的列表
|
||||
McModLoader.Output.Remove(ModEntity)
|
||||
Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
|
||||
Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyListItem).First(Function(i) CType(i.Tag, McMod) Is ModEntity))
|
||||
Parent.Children.RemoveAt(IndexOfUi)
|
||||
Next
|
||||
If Not IsSearching Then
|
||||
'改变禁用数量的显示
|
||||
CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
|
||||
'更新加载器状态
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
|
||||
End If
|
||||
If Not IsSuccessful Then
|
||||
Hint("由于文件被占用,部分 Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
|
||||
RefreshList(True)
|
||||
ElseIf If(IsSearching, PanSearchList, PanItems).Children.Count = 0 Then
|
||||
RefreshList(True)
|
||||
Else
|
||||
ChangeAllSelected(False)
|
||||
End If
|
||||
If IsSuccessful Then
|
||||
If IsShiftPressed Then
|
||||
Hint($"已彻底删除 {DeleteList.Count} 个文件!", HintType.Finish)
|
||||
Else
|
||||
Hint($"已将 {DeleteList.Count} 个文件删除到回收站!", HintType.Finish)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
|
||||
'取消选择
|
||||
Private Sub BtnSelectCancel_Click() Handles BtnSelectCancel.Click
|
||||
ChangeAllSelected(False)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "单个 Mod 项"
|
||||
|
||||
'点击:切换可用状态 / 显示错误原因
|
||||
Public Sub Item_Click(sender As MyListItem, e As EventArgs)
|
||||
'点击 Logo:切换可用状态 / 显示错误原因
|
||||
Public Sub ItemLogo_Click(sender As MyListItem, e As EventArgs)
|
||||
Try
|
||||
|
||||
Dim ModEntity As McMod = sender.Tag
|
||||
@@ -291,12 +493,13 @@
|
||||
Parent.Children.RemoveAt(IndexOfUi)
|
||||
Parent.Children.Insert(IndexOfUi, McModListItem(NewModEntity))
|
||||
'仅在非搜索页面才执行,以确保搜索页内外显示一致
|
||||
If String.IsNullOrWhiteSpace(SearchBox.Text) Then
|
||||
If Not IsSearching Then
|
||||
'改变禁用数量的显示
|
||||
CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
|
||||
'更新加载器状态
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
|
||||
End If
|
||||
RefreshBottomBar()
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "单个状态改变中重命名 Mod 失败")
|
||||
@@ -345,6 +548,8 @@
|
||||
End If
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
|
||||
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "删除 Mod 被主动取消")
|
||||
Catch ex As Exception
|
||||
Log(ex, "删除 Mod 失败", LogLevel.Feedback)
|
||||
End Try
|
||||
@@ -356,27 +561,30 @@
|
||||
Dim ListItem As MyListItem
|
||||
ListItem = If(TypeOf sender Is MyIconButton, sender.Tag, sender)
|
||||
Dim ModEntity As McMod = ListItem.Tag
|
||||
'加载失败信息
|
||||
If ModEntity.State = McMod.McModState.Unavaliable Then
|
||||
MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntity.FileUnavailableReason), "Mod 读取失败")
|
||||
Return
|
||||
End If
|
||||
'获取信息
|
||||
Dim ContentLines As New List(Of String)
|
||||
If ModEntity.Description IsNot Nothing Then ContentLines.Add(ModEntity.Description & vbCrLf)
|
||||
If ModEntity.Authors IsNot Nothing Then ContentLines.Add("作者:" & ModEntity.Authors)
|
||||
ContentLines.Add("文件:" & ModEntity.FileName & "(" & GetString(New FileInfo(ModEntity.Path).Length) & ")")
|
||||
If ModEntity.Version IsNot Nothing Then ContentLines.Add("版本:" & ModEntity.Version)
|
||||
If ModeDebug Then
|
||||
Dim DebugInfo As New List(Of String)
|
||||
If ModEntity.ModId IsNot Nothing Then
|
||||
DebugInfo.Add("Mod ID:" & ModEntity.ModId)
|
||||
End If
|
||||
If ModEntity.Dependencies.Count > 0 Then
|
||||
DebugInfo.Add("依赖于:")
|
||||
For Each Dep In ModEntity.Dependencies
|
||||
DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
|
||||
Next
|
||||
End If
|
||||
If DebugInfo.Count > 0 Then
|
||||
ContentLines.Add(vbCrLf & "—————— 调试信息 ——————")
|
||||
ContentLines.AddRange(DebugInfo)
|
||||
End If
|
||||
Dim DebugInfo As New List(Of String)
|
||||
If ModEntity.ModId IsNot Nothing Then
|
||||
DebugInfo.Add("Mod ID:" & ModEntity.ModId)
|
||||
End If
|
||||
If ModEntity.Dependencies.Count > 0 Then
|
||||
DebugInfo.Add("依赖于:")
|
||||
For Each Dep In ModEntity.Dependencies
|
||||
DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
|
||||
Next
|
||||
End If
|
||||
If DebugInfo.Count > 0 Then
|
||||
ContentLines.Add("")
|
||||
ContentLines.AddRange(DebugInfo)
|
||||
End If
|
||||
'获取用于搜索的 Mod 名称
|
||||
Dim ModOriginalName As String = ModEntity.Name.Replace(" ", "+")
|
||||
@@ -424,22 +632,14 @@
|
||||
|
||||
#End Region
|
||||
|
||||
Public ReadOnly Property IsSearching As Boolean
|
||||
Get
|
||||
Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
|
||||
End Get
|
||||
End Property
|
||||
Public Sub SearchRun() Handles SearchBox.TextChanged
|
||||
If String.IsNullOrWhiteSpace(SearchBox.Text) Then
|
||||
'隐藏
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.RunOnUpdated)
|
||||
AniStart({
|
||||
AaOpacity(PanSearch, -PanSearch.Opacity, 100),
|
||||
AaCode(Sub()
|
||||
PanSearch.Height = 0
|
||||
PanSearch.Visibility = Visibility.Collapsed
|
||||
PanList.Visibility = Visibility.Visible
|
||||
PanManage.Visibility = Visibility.Visible
|
||||
End Sub,, True),
|
||||
AaOpacity(PanList, 1 - PanList.Opacity, 150, 30),
|
||||
AaOpacity(PanManage, 1 - PanManage.Opacity, 150, 30)
|
||||
}, "FrmVersionMod Search Switch", True)
|
||||
Else
|
||||
ChangeAllSelected(False)
|
||||
If IsSearching Then
|
||||
'构造请求
|
||||
Dim QueryList As New List(Of SearchEntry(Of McMod))
|
||||
For Each Entry As McMod In McModLoader.Output
|
||||
@@ -470,15 +670,25 @@
|
||||
'显示
|
||||
AniStart({
|
||||
AaOpacity(PanList, -PanList.Opacity, 100),
|
||||
AaOpacity(PanManage, -PanManage.Opacity, 100),
|
||||
AaCode(Sub()
|
||||
PanList.Visibility = Visibility.Collapsed
|
||||
PanManage.Visibility = Visibility.Collapsed
|
||||
PanSearch.Visibility = Visibility.Visible
|
||||
PanSearch.TriggerForceResize()
|
||||
End Sub,, True),
|
||||
AaOpacity(PanSearch, 1 - PanSearch.Opacity, 200, 60)
|
||||
}, "FrmVersionMod Search Switch", True)
|
||||
Else
|
||||
'隐藏
|
||||
LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.RunOnUpdated)
|
||||
AniStart({
|
||||
AaOpacity(PanSearch, -PanSearch.Opacity, 100),
|
||||
AaCode(Sub()
|
||||
PanSearch.Height = 0
|
||||
PanSearch.Visibility = Visibility.Collapsed
|
||||
PanList.Visibility = Visibility.Visible
|
||||
End Sub,, True),
|
||||
AaOpacity(PanList, 1 - PanList.Opacity, 150, 30)
|
||||
}, "FrmVersionMod Search Switch", True)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="2" HorizontalAlignment="Left" Text="分类" Margin="0,0,25,0" />
|
||||
<local:MyComboBox Grid.Column="1" Grid.Row="2" x:Name="ComboDisplayType">
|
||||
<local:MyComboBoxItem Content="自动" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="从版本列表中隐藏"
|
||||
ToolTip="该版本默认将不会在版本列表中显示。
在版本列表页面按下 F11,即可查看所有隐藏的版本。" ToolTipService.HorizontalOffset="-350" />
|
||||
<local:MyComboBoxItem Content="从版本列表中隐藏" ToolTip="该版本默认将不会在版本列表中显示。
在版本列表页面按下 F11,即可查看所有隐藏的版本。" />
|
||||
<local:MyComboBoxItem Content="可安装 Mod 的版本" />
|
||||
<local:MyComboBoxItem Content="常规版本" />
|
||||
<local:MyComboBoxItem Content="不常用版本" />
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
DeleteDirectory(PageVersionLeft.Version.Path)
|
||||
Hint("版本 " & PageVersionLeft.Version.Name & " 已永久删除!", HintType.Finish)
|
||||
Else
|
||||
FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.AllDialogs, FileIO.RecycleOption.SendToRecycleBin)
|
||||
FileIO.FileSystem.DeleteDirectory(PageVersionLeft.Version.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
|
||||
Hint("版本 " & PageVersionLeft.Version.Name & " 已删除到回收站!", HintType.Finish)
|
||||
End If
|
||||
Case 2
|
||||
@@ -333,6 +333,8 @@
|
||||
End Select
|
||||
LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
|
||||
FrmMain.PageBack()
|
||||
Catch ex As OperationCanceledException
|
||||
Log(ex, "删除版本 " & PageVersionLeft.Version.Name & " 被主动取消")
|
||||
Catch ex As Exception
|
||||
Log(ex, "删除版本 " & PageVersionLeft.Version.Name & " 失败", LogLevel.Msgbox)
|
||||
End Try
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
d:DesignWidth="800" d:DesignHeight="800">
|
||||
<local:MyScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" x:Name="PanBack">
|
||||
<StackPanel x:Name="PanMain" Margin="25,10">
|
||||
<local:MyCard x:Name="CardArgument" Margin="0,15" Title="启动选项">
|
||||
<local:MyHint Text="本页面的设置均只对当前版本生效,这对整合包制作应该会有所帮助。
你也可以在设置页面中修改对全部版本生效的全局设置。" IsWarn="False" Margin="0,15" />
|
||||
<local:MyCard x:Name="CardArgument" Margin="0,0,0,15" Title="启动选项">
|
||||
<StackPanel Margin="25,40,25,21">
|
||||
<TextBlock Margin="0,-2,0,1" Text="在这个页面修改的设置均只对当前版本生效,这对整合包制作应该会有所帮助。" TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,0,0,11" Text="你也可以在设置页面中,修改对全部版本生效的全局设置。" TextWrapping="Wrap" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Name" />
|
||||
@@ -44,9 +43,9 @@
|
||||
<local:MyComboBox x:Name="ComboArgumentIndie" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionArgumentIndie" Grid.Column="1">
|
||||
<local:MyComboBoxItem Content="使用全局设置" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="开启"
|
||||
ToolTip="让这个版本与其他版本的存档、Mod 等档案相互独立,互不干涉" ToolTipService.HorizontalOffset="-350"/>
|
||||
ToolTip="让这个版本与其他版本的存档、Mod 等档案相互独立,互不干涉" ToolTipService.HorizontalOffset="100"/>
|
||||
<local:MyComboBoxItem Content="关闭"
|
||||
ToolTip="与其余未开启隔离的版本均使用同一档案,存档、Mod 等均为公用。
若存在不同版本的 Forge 或 Fabric,可能会由于 Mod 与版本冲突导致未知问题。" ToolTipService.HorizontalOffset="-350"/>
|
||||
ToolTip="与其余未开启隔离的版本均使用同一档案,存档、Mod 等均为公用。
若存在不同版本的 Forge 或 Fabric,可能会由于 Mod 与版本冲突导致未知问题。" ToolTipService.HorizontalOffset="100"/>
|
||||
</local:MyComboBox>
|
||||
<TextBlock VerticalAlignment="Center" Grid.Row="6" HorizontalAlignment="Left" Text="游戏 Java" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboArgumentJava" Grid.ColumnSpan="2" Grid.Row="6" Grid.Column="1" MaxDropDownHeight="240"
|
||||
@@ -59,7 +58,7 @@
|
||||
</local:MyCard>
|
||||
<local:MyCard Margin="0,0,0,15" Title="游戏内存">
|
||||
<StackPanel Margin="25,40,25,15">
|
||||
<local:MyHint x:Name="LabRamWarn" CanClose="False" Text="32 位 Java 最多只能分配 1 GB 内存,建议安装 64 位 Java!" IsWarn="True" Margin="0,0,0,12" />
|
||||
<local:MyHint x:Name="LabRamWarn" Text="32 位 Java 最多只能分配 1 GB 内存,建议安装 64 位 Java!" IsWarn="True" Margin="0,0,0,12" />
|
||||
<Grid Margin="0,0,0,3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -189,29 +188,26 @@
|
||||
<RowDefinition Height="9" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="9" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="9" />
|
||||
<RowDefinition Height="31" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="JVM 参数头" Margin="0,0,25,0" />
|
||||
<local:MyTextBox x:Name="TextAdvanceJvm" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionAdvanceJvm" Grid.Column="1" ToolTip="启动 Minecraft 时使用的额外 JVM 参数,在没有确定把握的情况下请不要尝试修改。
若留空,则使用全局设置的值。" HintText="使用全局设置"
|
||||
Height="57" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" />
|
||||
<TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="JVM 参数尾" Margin="0,0,25,0" />
|
||||
<local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。
例如输入 --demo 则会以试玩模式启动游戏。
若留空,则使用全局设置的值。" HintText="使用全局设置" />
|
||||
<TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="文件校验" Margin="0,0,25,0" />
|
||||
<local:MyComboBox x:Name="ComboAdvanceAssets" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceAssets" Grid.Column="1">
|
||||
<local:MyComboBoxItem Content="使用全局设置" IsSelected="True" />
|
||||
<local:MyComboBoxItem Content="开启"
|
||||
ToolTip="在启动 Minecraft 时,简单校验并下载缺失或错误的文件" ToolTipService.HorizontalOffset="-300" />
|
||||
<local:MyComboBoxItem Content="关闭"
|
||||
ToolTip="完全不更改 assets;不校验 libraries、第三方登录库与版本主 jar 文件是否被修改。
如果你没有修改相关文件,请勿勾选此项。" ToolTipService.HorizontalOffset="-300" />
|
||||
</local:MyComboBox>
|
||||
<TextBlock Grid.Row="6" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
|
||||
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="6" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
|
||||
<TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
|
||||
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
|
||||
ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。

在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。

可以使用以下替换标记实现相对路径(路径均以 \ 结尾):
 · {path}:PCL 的 exe 文件所在的文件夹
 · {minecraft}:.minecraft 文件夹
 · {verpath}:版本文件夹(.minecraft\versions\版本名\)
 · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹
 · {java}:游戏运行时的 Java 文件夹

除此之外,也支持以下替换标记:
 · {user}:玩家名字
 · {login}:玩家的登录方式
 · {uuid}:玩家的 UUID
 · {name}:游戏版本名
 · {time}:当前的系统时间
 · {version}:游戏对应的原版版本号

例如:
 · "{verpath}test.exe" :运行版本文件夹下的 test.exe 程序
 · "{java}java.exe" -jar "{verpath}test.jar" :用 Java 运行版本文件夹下的 test.jar
 · notepad "{verindie}option.txt" :使用记事本打开该版本的设置文件

涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" />
|
||||
<local:MyCheckBox Grid.Row="7" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
|
||||
<local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
|
||||
Text="等待命令执行完成后再继续启动" Margin="0,5,0,0" />
|
||||
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
|
||||
<local:MyCheckBox Text="忽略 Java 兼容性警告" x:Name="CheckAdvanceJava" Tag="VersionAdvanceJava" Margin="0,0,60,0"
|
||||
ToolTip="如果在设置中手动选择了与当前版本不兼容的 Java,则自动跳过兼容性警告弹窗,强制使用手动选择的 Java。" />
|
||||
<local:MyCheckBox Text="关闭文件校验" x:Name="CheckAdvanceAssetsV2" Tag="VersionAdvanceAssetsV2"
|
||||
ToolTip="完全不更改 assets;不校验 libraries、第三方登录库与版本主 jar 文件是否被修改。
如果你没有修改相关文件,请勿勾选此项。" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</local:MyCard>
|
||||
</StackPanel>
|
||||
|
||||
@@ -67,7 +67,13 @@
|
||||
TextAdvanceGame.Text = Setup.Get("VersionAdvanceGame", Version:=PageVersionLeft.Version)
|
||||
TextAdvanceRun.Text = Setup.Get("VersionAdvanceRun", Version:=PageVersionLeft.Version)
|
||||
CheckAdvanceRunWait.Checked = Setup.Get("VersionAdvanceRunWait", Version:=PageVersionLeft.Version)
|
||||
ComboAdvanceAssets.SelectedIndex = Setup.Get("VersionAdvanceAssets", Version:=PageVersionLeft.Version)
|
||||
If Setup.Get("VersionAdvanceAssets", Version:=PageVersionLeft.Version) = 2 Then
|
||||
Log("[Setup] 已迁移老版本的关闭文件校验设置")
|
||||
Setup.Reset("VersionAdvanceAssets", Version:=PageVersionLeft.Version)
|
||||
Setup.Set("VersionAdvanceAssetsV2", True, Version:=PageVersionLeft.Version)
|
||||
End If
|
||||
CheckAdvanceAssetsV2.Checked = Setup.Get("VersionAdvanceAssetsV2", Version:=PageVersionLeft.Version)
|
||||
CheckAdvanceJava.Checked = Setup.Get("VersionAdvanceJava", Version:=PageVersionLeft.Version)
|
||||
|
||||
Catch ex As Exception
|
||||
Log(ex, "重载版本独立设置时出错", LogLevel.Feedback)
|
||||
@@ -92,6 +98,8 @@
|
||||
Setup.Reset("VersionAdvanceJvm", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceGame", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceAssets", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceAssetsV2", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceJava", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceRun", Version:=PageVersionLeft.Version)
|
||||
Setup.Reset("VersionAdvanceRunWait", Version:=PageVersionLeft.Version)
|
||||
|
||||
@@ -121,10 +129,10 @@
|
||||
Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderRamCustom.Change
|
||||
If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Value, Version:=PageVersionLeft.Version)
|
||||
End Sub
|
||||
Private Shared Sub ComboChange(sender As MyComboBox, e As Object) Handles ComboArgumentIndie.SelectionChanged, ComboAdvanceAssets.SelectionChanged
|
||||
Private Shared Sub ComboChange(sender As MyComboBox, e As Object) Handles ComboArgumentIndie.SelectionChanged
|
||||
If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.SelectedIndex, Version:=PageVersionLeft.Version)
|
||||
End Sub
|
||||
Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckAdvanceRunWait.Change
|
||||
Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckAdvanceRunWait.Change, CheckAdvanceAssetsV2.Change, CheckAdvanceJava.Change
|
||||
If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked, Version:=PageVersionLeft.Version)
|
||||
End Sub
|
||||
|
||||
@@ -420,7 +428,7 @@ PreFin:
|
||||
Try
|
||||
For Each Java In Sort(JavaList.Clone(), Function(l, r) l.VersionCode < r.VersionCode)
|
||||
Dim ListItem = New MyComboBoxItem With {.Content = Java.ToString, .ToolTip = Java.PathFolder, .Tag = Java}
|
||||
ToolTipService.SetHorizontalOffset(ListItem, 0)
|
||||
ToolTipService.SetHorizontalOffset(ListItem, 400)
|
||||
ComboArgumentJava.Items.Add(ListItem)
|
||||
'判断人为选中
|
||||
If SelectedBySetup = "" OrElse SelectedBySetup = "使用全局设置" Then Continue For
|
||||
|
||||
@@ -182,8 +182,12 @@
|
||||
<Compile Include="Controls\MyCard.vb" />
|
||||
<Compile Include="Controls\MyPageRight.vb" />
|
||||
<Compile Include="Controls\MyPageLeft.vb" />
|
||||
<Compile Include="Controls\MyIconTextButton.xaml.vb">
|
||||
<DependentUpon>MyIconTextButton.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Modules\Minecraft\ModComp.vb" />
|
||||
<Compile Include="Modules\Minecraft\ModJava.vb" />
|
||||
<Compile Include="Modules\Minecraft\ModMod.vb" />
|
||||
<Compile Include="Modules\Minecraft\ModModpack.vb" />
|
||||
<Compile Include="Modules\ModEvent.vb" />
|
||||
<Compile Include="Controls\MyExtraButton.xaml.vb">
|
||||
@@ -406,6 +410,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Controls\MyIconTextButton.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Controls\MySearchBox.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -4,249 +4,305 @@
|
||||
你也可以使用 Ctrl + F 快速查找。例如,若需要改变文本颜色,则搜索 “颜色” 即可。 -->
|
||||
|
||||
<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"
|
||||
Text="每个 local:MyCard 代表一张卡片,你可以添加、删除格式类似的 MyCard 来添加多个卡片。每个 TextBlock 代表一段文本,你可以在 Text 属性中书写任何你想写的内容,也可以自行添加更多的 TextBlock。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4" FontSize="11"
|
||||
Text="你可以通过添加、删除属性修改样式,例如上一行的 FontSize 就会将字号改为 11 号。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="20,5,20,0" Foreground="#8C7721"
|
||||
Text="它还有许多可以调整的属性:上一行的 Margin 调整了边距,Foreground 则会让文字变色。" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<local:MyCard Title="卡片、提示条" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<local:MyHint Text="local:MyHint 代表提示条,它默认是警告样式。" />
|
||||
<local:MyHint Margin="0,8,0,2" IsWarn="False"
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<local:MyHint Text="local:MyHint 代表提示条,它默认是警告样式。" />
|
||||
<local:MyHint Margin="0,8,0,2" IsWarn="False"
|
||||
Text="将提示条的 IsWarn 属性改为 False 即可改为提示样式。
使用左边的那堆字符可以在任意地方手动换行,如果需要使用等号、引号等特殊字符,可以自行百度 XAML 转义字符。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,6,0,0"
|
||||
Text="卡片(local:MyCard)的 Title 属性决定了它的标题。
为卡片添加 CanSwap 属性让它可以被折叠,True 代表是,False 代表否。在此基础上,再使用 IsSwaped 属性调整它是否默认被折叠。" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,6"
|
||||
Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" />
|
||||
<Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
|
||||
</StackPanel>
|
||||
<Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
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="这是一个按钮!一个按钮!" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
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="……就能看到这句话!" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="local:MyTextButton 是没有边框的 “纯文本按钮”,其作用与 local:MyButton 一致。" />
|
||||
<local:MyTextButton Margin="0,8,0,10" HorizontalAlignment="Center"
|
||||
<local:MyTextButton Margin="0,8,0,10" HorizontalAlignment="Center"
|
||||
Text="一个精简版的文本按钮" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="按钮可以做到很多事,具体将在 “事件” 部分介绍。" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
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/" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,10,0,10"
|
||||
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你也可以直接在其中换行。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0"
|
||||
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/" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
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" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
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" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
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" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,4,0,0"
|
||||
Text="如果能在 PCL 文件夹中找到该文件,则会优先打开 PCL 文件夹中的文件。当然,你可以使用例如 C:\ 开头的绝对路径。" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="此外,PCL2 会将文件中的花括号 path 替换为 PCL 可执行文件所在文件夹({path}),可以用于执行特定程序或加载图片。" />
|
||||
<local:MyButton Margin="0,4,0,10" Width="200" Height="35"
|
||||
Text="打开 PCL 日志" EventType="打开文件" EventData="Log1.txt" ToolTip="PCL2 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="此外,PCL 会将文件中的 “花括号 path” 替换为 PCL 可执行文件所在文件夹({path}),可以用于执行特定程序或加载图片。" />
|
||||
<local:MyButton Margin="0,4,0,10" Width="200" Height="35"
|
||||
Text="打开 PCL 日志" EventType="打开文件" EventData="Log1.txt" ToolTip="PCL 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="就像为游戏指定服务器 IP 一样,你也可以在 EventData 的竖线后为程序添加启动参数。" />
|
||||
<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。
点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" />
|
||||
</StackPanel>
|
||||
<local:MyButton Margin="0,4,0,0" Width="200" Height="35"
|
||||
Text="定位游戏启动脚本" EventType="打开文件" EventData="explorer.exe|/select,LatestLaunch.bat" ToolTip="PCL 会将上一次启动 MC 的脚本保存在 PCL 文件夹内,且名为 LatestLaunch.bat。
点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" />
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<local:MyCard Title="事件 4:其他事件类型" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,4,0,6" FontWeight="Bold"
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<TextBlock Margin="0,4,0,6" FontWeight="Bold"
|
||||
Text="EventType 共有以下种类:" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="· 打开网页、启动游戏、打开文件:已在上方的卡片中介绍" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="· 今日人品、清理垃圾、内存优化:触发百宝箱中的对应功能" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
Text="查看 PCL 源代码" EventType="打开网页" EventData="https://github.com/Hex-Dragon/PCL2/blob/main/Plain%20Craft%20Launcher%202/FormMain.xaml.vb"/>
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="· 今日人品、清理垃圾、内存优化、安装整合包:触发对应功能" />
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="· 复制文本:将 EventData 中的内容复制到剪贴板" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="· 刷新主页:点击重新加载主页。如果主页文件为联网更新,这会重新下载主页内容" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
<TextBlock Margin="0,0,0,4"
|
||||
Text="· 下载文件:在点击时要求选择文件保存位置,并且下载 EventData 的链接中的文件" />
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
Text="下载 MCBBS 的 Logo" EventType="下载文件" EventData="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,4,0,0"
|
||||
<TextBlock Margin="0,4,0,4"
|
||||
Text="· 打开帮助:跳转到 EventData 对应的帮助文件(输入相对 Help 文件夹的路径)"/>
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
Text="打开自定义帮助页面" EventType="打开帮助" EventData="帮助/自定义帮助.json"/>
|
||||
</StackPanel>
|
||||
<TextBlock Margin="0,4,0,4"
|
||||
Text="· 切换页面:强制切换到 PCL 的某一页面(尝试切换到不太对头的页面会出现不可预知的 Bug,使用时请谨慎)
 EventData 为 页面种类|次要页面种类(PageType|PageSubType,例如 1|1)
 点击上方的 查看 PCL 源代码 按钮,在 1000 行左右的位置即可查找到对应数值"/>
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
Text="切换到下载页面" EventType="切换页面" EventData="1|1"/>
|
||||
<local:MyButton Margin="0,5,0,6" Width="250" Height="35"
|
||||
Text="切换到启动器设置" EventType="切换页面" EventData="3|2"/>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<local:MyCard Title="横向布局 1:StackPanel" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
|
||||
<local:MyCard Title="StackPanel 横向布局" Margin="0,0,0,15" CanSwap="True" IsSwaped="True">
|
||||
<StackPanel Margin="25,40,23,15">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
|
||||
Text="你需要使用 StackPanel 在一行里塞下多个按钮。你可以仅在这个教学卡片的基础上稍作调整,来实现自己的按钮布局。" />
|
||||
<!-- 这是第一行 -->
|
||||
<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"
|
||||
<!-- 这是第一行 -->
|
||||
<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"
|
||||
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" />
|
||||
<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" />
|
||||
</StackPanel>
|
||||
<!-- 这是第二行 -->
|
||||
<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"
|
||||
</StackPanel>
|
||||
<!-- 这是第二行 -->
|
||||
<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"
|
||||
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="内存优化" />
|
||||
<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" />
|
||||
</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>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
Text="你也可以使用列表项 “local:MyListItem” 来替代按钮,其使用方式与按钮类似。" />
|
||||
<local:MyListItem Margin="-5,2,-5,8"
|
||||
Logo="https://www.baidu.com/favicon.ico" Title="百度" Info="百度一下,你就知道!"
|
||||
<local:MyListItem Margin="-5,2,-5,8"
|
||||
Logo="https://www.baidu.com/favicon.ico" Title="百度" Info="百度一下,你就知道!"
|
||||
EventType="打开网页" EventData="https://www.baidu.com/" Type="Clickable" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
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"
|
||||
EventType="打开帮助" EventData="帮助/自定义帮助.json" Type="Clickable" />
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
Text="EventType、EventData 与按钮几乎一致,唯一不同的是,列表项不支持联网加载帮助文件。" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
Text="PCL 内置了一些 Minecraft 方块与物品图片,可以直接使用。"/>
|
||||
<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/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/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/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/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/Egg.png"/>
|
||||
<!-- 鸡蛋 -->
|
||||
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Fabric.png"/>
|
||||
<!-- 布料(Fabric 图标) -->
|
||||
</StackPanel>
|
||||
<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/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/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/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/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/Egg.png"/>
|
||||
<!-- 鸡蛋 -->
|
||||
<Image Width="30" Margin="5,0,0,0" Source="pack://application:,,,/images/Blocks/Fabric.png"/>
|
||||
<!-- 布料(Fabric 图标) -->
|
||||
</StackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,4"
|
||||
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"
|
||||
Title="清理垃圾" Info="使用内置的命令方块图片作为 Logo" EventType="清理垃圾" Type="Clickable" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</local:MyCard>
|
||||
|
||||
<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"
|
||||
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=" 浓度 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=" 浓度 4 " Margin="5,0" Background="{DynamicResource ColorBrush4}" Foreground="#FFFFFF" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4">
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="8,4">
|
||||
<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=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" />
|
||||
<TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" />
|
||||
</StackPanel>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
|
||||
</StackPanel>
|
||||
<TextBlock Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
|
||||
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 会在加载时对其进行替换。
例如,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 属性。
通过设置 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)查找它。不要自己写,写不懂的。
此外,它还有两种 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 属性,只有一个图标可以按。
它的 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 讨论页。
你可以根据其中的介绍为主页添加版本号检查以节省流量,也可以通过检查 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>
|
||||
|
||||
<!--
|
||||
@@ -254,8 +310,10 @@
|
||||
全部通用的属性:
|
||||
- Width、Height:设置宽度与高度
|
||||
- ToolTip:设置鼠标指向时的提示文本
|
||||
- Background:调整背景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
|
||||
- Margin:调整外边距(格式为 左,上,右,下。例如 Margin="0,0,0,2" 即为 2 单位的下边距)
|
||||
- HorizontalAlignment:设置横向对齐方式,居左:Left、居中:Center、居右:Right、拉伸(默认):Stretch
|
||||
- VerticalAlignment:设置纵向对齐方式,居上:Top、居中:Center、居下:Bottom、拉伸(默认):Stretch
|
||||
- IsHitTestVisible:该控件是否响应交互(点击、指向动画等),True 为是(默认),False 为否
|
||||
|
||||
TextBlock(纯文本):
|
||||
- Text:设置显示的文本(如果要使用引号等特殊字符,自行百度 XAML 转义)
|
||||
@@ -263,37 +321,32 @@ TextBlock(纯文本):
|
||||
- FontSize:设置字号
|
||||
- FontWeight:设置为 Bold 时,文字加粗
|
||||
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
|
||||
|
||||
Image(图片):
|
||||
- Source:要显示的图片的网址
|
||||
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
|
||||
- Source:要显示的图片的网址,或 PCL 内置图片位置
|
||||
|
||||
local:MyCard(卡片):
|
||||
- Title:设置显示的标题文本
|
||||
- CanSwap:这张卡片是否可以折叠,True 为是,False 为否
|
||||
- IsSwaped:这张卡片是否默认折叠,要求 CanSwap 必须为 True
|
||||
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)(使用时,CanSwap 属性需为 False)
|
||||
- CanSwap:卡片是否可以折叠,True 为是,False 为否
|
||||
- IsSwaped:卡片是否默认折叠,要求 CanSwap 必须为 True
|
||||
- HorizontalAlignment:若使用,要求 CanSwap 必须为 False
|
||||
- UseAnimation:是否在展开等高度改变时触发动画,True 为是(默认),False 为否
|
||||
- SwapLogoRight:卡片折叠时的箭头是朝下还是朝右,True 为朝右,False 为朝下(默认)
|
||||
- HasMouseAnimation:是否在鼠标指向时改变卡片颜色,True 为是(默认),False 为否
|
||||
|
||||
local:MyHint(提示条):
|
||||
- Text:设置显示的文本(
 代表换行)
|
||||
- IsWarn:设置为 True 代表警告样式,False 代表提示样式
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
|
||||
|
||||
local:MyButton(按钮):
|
||||
- Text:设置显示的文本
|
||||
- Padding:设置内边距,格式与 Margin 一致
|
||||
- ColorType:设置颜色主题(Highlight 为当前启动器的主题颜色,Red 为红色,默认为黑色)
|
||||
- EventType、EventData:触发特定事件
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
|
||||
|
||||
local:MyTextButton(文本按钮):
|
||||
- Text:设置显示的文本
|
||||
- EventType、EventData:触发特定事件
|
||||
- Foreground:调整前景颜色(十六进制字符串,例如 #FF010203 代表 ARGB 中的 255,1,2,3。前两位代表不透明度,FF 为全不透明,00 为全透明)
|
||||
- HorizontalAlignment:设置对齐方式(Left、Center、Right)
|
||||
|
||||
local:MyListItem(列表项):
|
||||
- Logo:列表项的图标(所指定的可以是链接也可以是文件路径)
|
||||
@@ -301,4 +354,17 @@ local:MyListItem(列表项):
|
||||
- Info:列表项的详细信息,它将会以灰色显示在大标题下方
|
||||
- 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.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -1,21 +1,21 @@
|
||||
## Plain Craft Launcher 2 源代码库
|
||||
|
||||
这里提供了 PCL2 的大多数源代码,包括 UI 库、动画模块、下载模块、Minecraft 启动模块等!
|
||||
这里提供了 PCL 的大多数源代码,包括 UI 库、动画模块、下载模块、Minecraft 启动模块等!
|
||||
|
||||
PCL2 的代码绝大多数其实都是几年前学生时代的产物了……那时候英语还不过关……所以经常出现奇葩命名,还有令人高血压的高耦合啊,没做单例啊,瞎勾八乱糊啊之类的问题……额,我也不可能把这一堆玩意儿再从头写一次,各位就基于能跑就行的原则凑合凑合着看吧,求求别喷了(run
|
||||
PCL 的代码绝大多数其实都是几年前学生时代的产物了……那时候英语还不过关……所以经常出现奇葩命名,还有令人高血压的高耦合啊,没做单例啊,瞎勾八乱糊啊之类的问题……额,我也不可能把这一堆玩意儿再从头写一次,各位就基于能跑就行的原则凑合凑合着看吧,求求别喷了(run
|
||||
|
||||
你也可以丢 Pull Request,虽然这个源代码库并不能直接编译,但一些简单的修改应该还是没问题的……
|
||||
|
||||
|
||||
|
||||
**相关内容:**
|
||||
- [PCL2 功能投票](https://github.com/Hex-Dragon/PCL2/discussions/2):来参加投票吧,开发者会优先处理票数较高的帖子!
|
||||
- [程序下载](https://afdian.net/p/0164034c016c11ebafcb52540025c377):下载免费的正式版 PCL2。
|
||||
- [帮助文档库](https://github.com/LTCatt/PCL2Help):PCL2 帮助文档在 GitHub 上的存储库(是的,帮助库在另一个 Repo……)
|
||||
- [PCL 功能投票](https://github.com/Hex-Dragon/PCL2/discussions/2):来参加投票吧,开发者会优先处理票数较高的帖子!
|
||||
- [程序下载](https://afdian.net/p/0164034c016c11ebafcb52540025c377):下载免费的正式版 PCL。
|
||||
- [帮助文档库](https://github.com/LTCatt/PCL2Help):PCL 帮助文档在 GitHub 上的存储库(是的,帮助库在另一个 Repo……)
|
||||
|
||||
|
||||
|
||||
**一些无趣的小细节:**
|
||||
- 别想在这里面找到解密游戏的线索,那部分的代码被我抠掉了(笑)
|
||||
- 哦,对,解锁隐藏主题的相关代码也都被抠掉了,所以别想着用翻源代码这种歪门邪道来绕过解密了.jpg
|
||||
- 源代码库并不是即时更新的,而是在每次 PCL2 发布更新时(手动)同步一次
|
||||
- 源代码库并不是即时更新的,而是在每次 PCL 发布更新时(手动)同步一次
|
||||
Reference in new issue
Block a user