diff --git a/Plain Craft Launcher 2/App.config b/Plain Craft Launcher 2/App.config
index 37eb10c..8d134eb 100644
--- a/Plain Craft Launcher 2/App.config
+++ b/Plain Craft Launcher 2/App.config
@@ -1,34 +1,35 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plain Craft Launcher 2/Application.xaml b/Plain Craft Launcher 2/Application.xaml
index ec434f0..a9b8db3 100644
--- a/Plain Craft Launcher 2/Application.xaml
+++ b/Plain Craft Launcher 2/Application.xaml
@@ -13,6 +13,12 @@
+
+
+
+
+
+
#343d4a
diff --git a/Plain Craft Launcher 2/Controls/MyButton.xaml b/Plain Craft Launcher 2/Controls/MyButton.xaml
index 5cff437..ad6142d 100644
--- a/Plain Craft Launcher 2/Controls/MyButton.xaml
+++ b/Plain Craft Launcher 2/Controls/MyButton.xaml
@@ -7,7 +7,7 @@
-
+
diff --git a/Plain Craft Launcher 2/Controls/MyButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyButton.xaml.vb
index dae17b2..72a968c 100644
--- a/Plain Craft Launcher 2/Controls/MyButton.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyButton.xaml.vb
@@ -1,22 +1,30 @@
-Public Class MyButton
+Imports System.Windows.Markup
+
+
+Public Class MyButton
'声明
Public Event Click(sender As Object, e As MouseButtonEventArgs) '自定义事件
'自定义属性
Public Uuid As Integer = GetUuid()
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
- Return LabText.Text
+ Return GetValue(TextProperty)
End Get
Set(value As String)
- LabText.Text = value
+ SetValue(TextProperty, value)
End Set
End Property '显示文本
Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(MyButton), New PropertyMetadata(New PropertyChangedCallback(
- Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
- If Not IsNothing(sender) Then CType(sender, MyButton).LabText.Text = e.NewValue
- End Sub)))
+ Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
+ If sender IsNot Nothing Then CType(sender, MyButton).LabText.Text = e.NewValue
+ End Sub)))
Public Property TextPadding As Thickness
Get
Return LabText.Padding
diff --git a/Plain Craft Launcher 2/Controls/MyCard.vb b/Plain Craft Launcher 2/Controls/MyCard.vb
index 30c6588..858224d 100644
--- a/Plain Craft Launcher 2/Controls/MyCard.vb
+++ b/Plain Craft Launcher 2/Controls/MyCard.vb
@@ -36,6 +36,11 @@
'属性
Public Uuid As Integer = GetUuid()
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return MainTextBlock.Inlines
+ End Get
+ End Property
Public Property Title As String
Get
Return GetValue(TitleProperty)
@@ -62,7 +67,7 @@
If IsLoad Then Exit Sub
IsLoad = True
'初次加载限定
- If Title <> "" AndAlso MainTextBlock Is Nothing Then
+ If MainTextBlock Is Nothing Then
MainTextBlock = New TextBlock With {.HorizontalAlignment = HorizontalAlignment.Left, .VerticalAlignment = VerticalAlignment.Top, .Margin = New Thickness(15, 12, 0, 0), .FontWeight = FontWeights.Bold, .FontSize = 13, .IsHitTestVisible = False}
MainTextBlock.SetResourceReference(TextBlock.ForegroundProperty, "ColorBrush1")
MainTextBlock.SetBinding(TextBlock.TextProperty, New Binding("Title") With {.Source = Me, .Mode = BindingMode.OneWay})
@@ -222,20 +227,21 @@
'高度增加较大,使用弹起动画
Dim Delta As Double = MathClamp(Math.Abs(DeltaHeight) * 0.05, 3, 10) * Math.Sign(DeltaHeight)
AnimList.AddRange({
- AaHeight(Me, DeltaHeight + Delta, 300, If(IsLoadAnimation, 30, 0), If(DeltaHeight > FrmMain.Height, New AniEaseInFluent(AniEasePower.ExtraStrong), New AniEaseOutFluent(AniEasePower.ExtraStrong))),
- AaHeight(Me, -Delta, 150, 260, Ease:=New AniEaseOutFluent(AniEasePower.Strong))
- })
+ AaHeight(Me, DeltaHeight + Delta, 300, If(IsLoadAnimation, 30, 0), If(DeltaHeight > FrmMain.Height, New AniEaseInFluent(AniEasePower.ExtraStrong), New AniEaseOutFluent(AniEasePower.ExtraStrong))),
+ AaHeight(Me, -Delta, 150, 260, Ease:=New AniEaseOutFluent(AniEasePower.Strong))
+ })
Else
'普通的改变就行啦
AnimList.AddRange({
- AaHeight(Me, DeltaHeight, MathClamp(Math.Abs(DeltaHeight) * 4, 150, 250),, New AniEaseOutFluent)
- })
+ AaHeight(Me, DeltaHeight, MathClamp(Math.Abs(DeltaHeight) * 4, 150, 250),, New AniEaseOutFluent)
+ })
End If
- AnimList.Add(AaCode(Sub()
- IsHeightAnimating = False
- Height = ActualUsedHeight
- If IsSwaped Then SwapControl.Visibility = Visibility.Collapsed
- End Sub,, True))
+ AnimList.Add(AaCode(
+ Sub()
+ IsHeightAnimating = False
+ Height = ActualUsedHeight
+ If IsSwaped Then SwapControl.Visibility = Visibility.Collapsed
+ End Sub,, True))
AniStart(AnimList, "MyCard Height " & Uuid)
IsHeightAnimating = True
@@ -253,7 +259,8 @@
#End Region
- '折叠
+#Region "折叠"
+
'若设置了 CanSwap,或 SwapControl 不为空,则判定为会进行折叠
'这是因为不能直接在 XAML 中设置 SwapControl
Public SwapControl As Object
@@ -294,7 +301,7 @@
Private Sub MyCard_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
Dim Pos As Double = Mouse.GetPosition(Me).Y
If Not IsSwaped AndAlso
- (SwapControl Is Nothing OrElse Pos > SwapedHeight OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
+ (SwapControl Is Nothing OrElse Pos > If(IsSwaped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
IsMouseDown = True
End Sub
Private Sub MyCard_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
@@ -303,7 +310,7 @@
Dim Pos As Double = Mouse.GetPosition(Me).Y
If Not IsSwaped AndAlso
- (SwapControl Is Nothing OrElse Pos > SwapedHeight OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
+ (SwapControl Is Nothing OrElse Pos > If(IsSwaped, SwapedHeight, SwapedHeight - 6) OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
Dim ee = New RouteEventArgs(True)
RaiseEvent PreviewSwap(Me, ee)
@@ -320,6 +327,8 @@
IsMouseDown = False
End Sub
+#End Region
+
End Class
Partial Public Module ModAnimation
Public Sub AniDispose(Control As MyCard, RemoveFromChildren As Boolean, Optional CallBack As ParameterizedThreadStart = Nothing)
diff --git a/Plain Craft Launcher 2/Controls/MyCheckBox.xaml b/Plain Craft Launcher 2/Controls/MyCheckBox.xaml
index 0f369e5..c0cac06 100644
--- a/Plain Craft Launcher 2/Controls/MyCheckBox.xaml
+++ b/Plain Craft Launcher 2/Controls/MyCheckBox.xaml
@@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MyCheckBox"
FocusVisualStyle="{x:Null}"
MinWidth="20" x:Name="PanBack" UseLayoutRounding="False" SnapsToDevicePixels="False" MinHeight="20" Background="{StaticResource ColorBrushSemiTransparent}" Focusable="True" d:DesignWidth="126.4" d:DesignHeight="44.8">
-
+
diff --git a/Plain Craft Launcher 2/Controls/MyCheckBox.xaml.vb b/Plain Craft Launcher 2/Controls/MyCheckBox.xaml.vb
index d531cc8..4211286 100644
--- a/Plain Craft Launcher 2/Controls/MyCheckBox.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyCheckBox.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyCheckBox
+Imports System.Windows.Markup
+
+
+Public Class MyCheckBox
'基础
@@ -14,29 +17,32 @@
End Sub '使外部程序引发本控件的 Change 事件
'自定义属性
-
- Private _Checked As Boolean = False '是否选中
Public Property Checked As Boolean
Get
- Return _Checked
+ Return GetValue(CheckedProperty)
End Get
Set(value As Boolean)
- SetChecked(value, False, True)
+ SetChecked(value, False)
End Set
End Property
+ Public Shared ReadOnly CheckedProperty As DependencyProperty =
+ DependencyProperty.Register("Checked", GetType(Boolean), GetType(MyCheckBox), New PropertyMetadata(False,
+ Sub(d As MyCheckBox, e As DependencyPropertyChangedEventArgs)
+ '在使用 XAML 设置 Checked 属性时,不会触发 Checked_Set 方法,所以需要在这里手动触发 UI 改变
+ If Not d.IsLoaded Then d.SyncUI()
+ End Sub))
+
Private Const AnimationTimeOfCheck As Integer = 150 '勾选状态变更动画长度
'''
''' 手动设置 Checked 属性。
'''
''' 新的 Checked 属性。
''' 是否由用户引发。
- ''' 是否执行动画。
- Public Sub SetChecked(value As Boolean, user As Boolean, anime As Boolean)
+ Public Sub SetChecked(value As Boolean, user As Boolean)
Try
- If value = _Checked Then Exit Sub
+ If value = Checked Then Exit Sub
'Preview 事件
-
If value AndAlso user Then
Dim e = New RouteEventArgs(user)
RaiseEvent PreviewChange(Me, e)
@@ -48,60 +54,67 @@
End If
End If
- _Checked = value
+ SetValue(CheckedProperty, value)
If IsLoaded Then RaiseEvent Change(Me, user)
'更改动画
-
- If IsLoaded AndAlso AniControlEnabled = 0 AndAlso anime Then '防止默认属性变更触发动画
- AllowMouseDown = False
- If Checked Then
- '由无变有
- AniStart({
- AaScale(ShapeBorder, 12 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True),
- AaScaleTransform(ShapeCheck, 1 - CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack(AniEasePower.Weak)),
- AaScale(ShapeBorder, 6, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack, , True)
- }, "MyCheckBox Scale " & Uuid)
- AniStart({
- AaColor(ShapeBorder, Border.BorderBrushProperty, If(IsEnabled, If(IsMouseOver, "ColorBrush3", "ColorBrush2"), "ColorBrushGray4"), AnimationTimeOfCheck)
- }, "MyCheckBox BorderColor " & Uuid)
- AniStart({
- AaCode(Sub() AllowMouseDown = True, AnimationTimeOfCheck * 2)
- }, "MyCheckBox AllowMouseDown " & Uuid)
- Else
- '由有变无
- AniStart({
- AaScale(ShapeBorder, 12 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True),
- AaScaleTransform(ShapeCheck, -CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX, AnimationTimeOfCheck * 0.9, , New AniEaseInFluent(AniEasePower.Weak)),
- AaScale(ShapeBorder, 6, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack, , True)
- }, "MyCheckBox Scale " & Uuid)
- AniStart({
- AaColor(ShapeBorder, Border.BorderBrushProperty, If(IsEnabled, If(IsMouseOver, "ColorBrush3", "ColorBrush1"), "ColorBrushGray4"), AnimationTimeOfCheck)
- }, "MyCheckBox BorderColor " & Uuid)
- AniStart({
- AaCode(Sub() AllowMouseDown = True, AnimationTimeOfCheck * 2)
- }, "MyCheckBox AllowMouseDown " & Uuid)
- End If
- Else
- '不使用动画
- AniStop("MyCheckBox Scale " & Uuid)
- AniStop("MyCheckBox BorderColor " & Uuid)
- AniStop("MyCheckBox AllowMouseDown " & Uuid)
- If Checked Then
- CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX = 1
- CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleY = 1
- ShapeBorder.SetResourceReference(Border.BorderBrushProperty, If(IsEnabled, "ColorBrush2", "ColorBrushGray4"))
- Else
- CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX = 0
- CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleY = 0
- ShapeBorder.SetResourceReference(Border.BorderBrushProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray4"))
- End If
- End If
-
+ SyncUI()
Catch ex As Exception
Log(ex, "设置 Checked 失败")
End Try
End Sub
+ Private Sub SyncUI()
+ If AniControlEnabled = 0 AndAlso IsLoaded Then '防止默认属性变更触发动画
+ AllowMouseDown = False
+ If Checked Then
+ '由无变有
+ AniStart({
+ AaScale(ShapeBorder, 12 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True),
+ AaScaleTransform(ShapeCheck, 1 - CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack(AniEasePower.Weak)),
+ AaScale(ShapeBorder, 6, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack, , True)
+ }, "MyCheckBox Scale " & Uuid)
+ AniStart({
+ AaColor(ShapeBorder, Border.BorderBrushProperty, If(IsEnabled, If(IsMouseOver, "ColorBrush3", "ColorBrush2"), "ColorBrushGray4"), AnimationTimeOfCheck)
+ }, "MyCheckBox BorderColor " & Uuid)
+ AniStart({
+ AaCode(Sub() AllowMouseDown = True, AnimationTimeOfCheck * 2)
+ }, "MyCheckBox AllowMouseDown " & Uuid)
+ Else
+ '由有变无
+ AniStart({
+ AaScale(ShapeBorder, 12 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True),
+ AaScaleTransform(ShapeCheck, -CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX, AnimationTimeOfCheck * 0.9, , New AniEaseInFluent(AniEasePower.Weak)),
+ AaScale(ShapeBorder, 6, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.7, New AniEaseOutBack, , True)
+ }, "MyCheckBox Scale " & Uuid)
+ AniStart({
+ AaColor(ShapeBorder, Border.BorderBrushProperty, If(IsEnabled, If(IsMouseOver, "ColorBrush3", "ColorBrush1"), "ColorBrushGray4"), AnimationTimeOfCheck)
+ }, "MyCheckBox BorderColor " & Uuid)
+ AniStart({
+ AaCode(Sub() AllowMouseDown = True, AnimationTimeOfCheck * 2)
+ }, "MyCheckBox AllowMouseDown " & Uuid)
+ End If
+ Else
+ '不使用动画
+ AniStop("MyCheckBox Scale " & Uuid)
+ AniStop("MyCheckBox BorderColor " & Uuid)
+ AniStop("MyCheckBox AllowMouseDown " & Uuid)
+ If Checked Then
+ CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX = 1
+ CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleY = 1
+ ShapeBorder.SetResourceReference(Border.BorderBrushProperty, If(IsEnabled, "ColorBrush2", "ColorBrushGray4"))
+ Else
+ CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleX = 0
+ CType(ShapeCheck.RenderTransform, ScaleTransform).ScaleY = 0
+ ShapeBorder.SetResourceReference(Border.BorderBrushProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray4"))
+ End If
+ End If
+ End Sub
+
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
Return GetValue(TextProperty)
@@ -110,10 +123,10 @@
SetValue(TextProperty, value)
End Set
End Property '内容
- Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(MyCheckbox), New PropertyMetadata(New PropertyChangedCallback(
- Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
- If Not IsNothing(sender) Then CType(sender, MyCheckbox).LabText.Text = e.NewValue
- End Sub)))
+ Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(MyCheckBox), New PropertyMetadata(New PropertyChangedCallback(
+ Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
+ If Not IsNothing(sender) Then CType(sender, MyCheckBox).LabText.Text = e.NewValue
+ End Sub)))
'点击事件
@@ -123,7 +136,7 @@
If Not MouseDowned Then Exit Sub
Log("[Control] 按下复选框(" & (Not Checked).ToString & "):" & Text)
MouseDowned = False
- SetChecked(Not Checked, True, True)
+ SetChecked(Not Checked, True)
AniStart(AaColor(ShapeBorder, Border.BackgroundProperty, "ColorBrushHalfWhite", 100), "MyCheckBox Background " & Uuid)
End Sub
Private Sub Checkbox_MouseDown() Handles Me.MouseLeftButtonDown
diff --git a/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml b/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml
new file mode 100644
index 0000000..82eb216
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml.vb
new file mode 100644
index 0000000..2af39d2
--- /dev/null
+++ b/Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml.vb
@@ -0,0 +1,158 @@
+Imports System.Windows.Markup
+
+
+Public Class MyExtraTextButton
+
+ '声明
+ Public Event Click(sender As Object, e As MouseButtonEventArgs) '自定义事件
+
+ '自定义属性
+ Public Uuid As Integer = GetUuid()
+ Private _Logo As String = ""
+ Public Property Logo As String
+ Get
+ Return _Logo
+ End Get
+ Set(value As String)
+ If value = _Logo Then Exit Property
+ _Logo = value
+ Path.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 Path IsNot Nothing Then Path.RenderTransform = New ScaleTransform With {.ScaleX = LogoScale, .ScaleY = LogoScale}
+ End Set
+ End Property
+ '显示文本
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ 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(MyExtraTextButton), New PropertyMetadata(New PropertyChangedCallback(
+ Sub(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
+ If sender IsNot Nothing Then CType(sender, MyExtraTextButton).LabText.Text = e.NewValue
+ End Sub)))
+
+ '动画
+ Private _Show As Boolean = False
+ Public Property Show As Boolean
+ Get
+ Return _Show
+ End Get
+ Set(value As Boolean)
+ If _Show = value Then Exit Property
+ _Show = value
+ RunInUi(
+ Sub()
+ If value Then
+ '有了
+ Opacity = 0
+ AniStart({
+ AaOpacity(Me, 1 - Opacity, 80, 50),
+ AaScaleTransform(Me, 0.15 - CType(RenderTransform, ScaleTransform).ScaleX, 400, 50, New AniEaseOutBack),
+ AaScaleTransform(Me, 0.85, 160, 50, New AniEaseOutFluent(AniEasePower.Middle))
+ }, "MyExtraTextButton MainScale " & Uuid)
+ Else
+ '没了
+ AniStart({
+ AaOpacity(Me, -Opacity, 50, 50),
+ AaScaleTransform(Me, -CType(RenderTransform, ScaleTransform).ScaleX, 100,, New AniEaseInFluent(AniEasePower.Weak))
+ }, "MyExtraTextButton MainScale " & Uuid)
+ End If
+ IsHitTestVisible = value '防止缩放动画中依然可以点进去
+ End Sub)
+ End Set
+ End Property
+
+ '触发点击事件
+ Private Sub Button_LeftMouseUp(sender As Object, e As MouseButtonEventArgs) Handles PanClick.MouseLeftButtonUp
+ If IsLeftMouseHeld Then
+ Log("[Control] 按下附加按钮" & If(ToolTip = "", "", ":" & ToolTip.ToString))
+ RaiseEvent Click(sender, e)
+ e.Handled = True
+ Button_LeftMouseUp()
+ End If
+ End Sub
+
+ '鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
+ Private IsLeftMouseHeld As Boolean = False
+ Private Sub Button_LeftMouseDown(sender As Object, e As MouseButtonEventArgs) Handles PanClick.MouseLeftButtonDown
+ If Not IsLeftMouseHeld Then
+ AniStart({
+ AaScaleTransform(PanScale, 0.85 - CType(PanScale.RenderTransform, ScaleTransform).ScaleX, 800,, New AniEaseOutFluent(AniEasePower.Strong)),
+ AaScaleTransform(PanScale, -0.05, 60,, New AniEaseOutFluent)
+ }, "MyExtraTextButton Scale " & Uuid)
+ End If
+ IsLeftMouseHeld = True
+ Focus()
+ End Sub
+ Private Sub Button_LeftMouseUp() Handles PanClick.MouseLeftButtonUp
+ AniStart({
+ AaScaleTransform(PanScale, 1 - CType(PanScale.RenderTransform, ScaleTransform).ScaleX, 300,, New AniEaseOutBack)
+ }, "MyExtraTextButton Scale " & Uuid)
+ IsLeftMouseHeld = False
+ RefreshColor() '直接刷新颜色以判断是否已触发 MouseLeave
+ End Sub
+ Private Sub Button_RightMouseUp() Handles PanClick.MouseRightButtonUp
+ If Not IsLeftMouseHeld Then
+ AniStart({
+ AaScaleTransform(PanScale, 1 - CType(PanScale.RenderTransform, ScaleTransform).ScaleX, 300,, New AniEaseOutBack)
+ }, "MyExtraTextButton Scale " & Uuid)
+ End If
+ RefreshColor() '直接刷新颜色以判断是否已触发 MouseLeave
+ End Sub
+ Private Sub Button_MouseLeave() Handles PanClick.MouseLeave
+ IsLeftMouseHeld = False
+ AniStart({
+ AaScaleTransform(PanScale, 1 - CType(PanScale.RenderTransform, ScaleTransform).ScaleX, 500,, New AniEaseOutFluent)
+ }, "MyExtraTextButton Scale " & Uuid)
+ RefreshColor() '直接刷新颜色以判断是否已触发 MouseLeave
+ End Sub
+
+ '自定义事件
+ '务必放在 IsMouseDown 更新之后
+ Private Const AnimationColorIn As Integer = 120
+ Private Const AnimationColorOut As Integer = 150
+ Public Sub RefreshColor() Handles PanClick.MouseEnter, PanClick.MouseLeave, Me.Loaded
+ Try
+ If IsLoaded AndAlso AniControlEnabled = 0 Then '防止默认属性变更触发动画
+
+ If IsMouseOver Then
+ '指向
+ AniStart(AaColor(PanColor, BackgroundProperty, "ColorBrush4", AnimationColorIn), "MyExtraTextButton Color " & Uuid)
+ Else
+ '普通
+ AniStart(AaColor(PanColor, BackgroundProperty, "ColorBrush3", AnimationColorOut), "MyExtraTextButton Color " & Uuid)
+ End If
+
+ Else
+
+ AniStop("MyExtraTextButton Color " & Uuid)
+ If IsMouseOver Then
+ PanColor.SetResourceReference(BackgroundProperty, "ColorBrush4")
+ Else
+ PanColor.SetResourceReference(BackgroundProperty, "ColorBrush3")
+ End If
+
+ End If
+ Catch ex As Exception
+ Log(ex, "刷新图标按钮颜色出错")
+ End Try
+ End Sub
+
+End Class
diff --git a/Plain Craft Launcher 2/Controls/MyHint.xaml b/Plain Craft Launcher 2/Controls/MyHint.xaml
index 41570ca..09b5694 100644
--- a/Plain Craft Launcher 2/Controls/MyHint.xaml
+++ b/Plain Craft Launcher 2/Controls/MyHint.xaml
@@ -17,7 +17,7 @@
-
+
diff --git a/Plain Craft Launcher 2/Controls/MyHint.xaml.vb b/Plain Craft Launcher 2/Controls/MyHint.xaml.vb
index fd025f0..89fc93a 100644
--- a/Plain Craft Launcher 2/Controls/MyHint.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyHint.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyHint
+Imports System.Windows.Markup
+
+
+Public Class MyHint
Public Uuid As Integer = GetUuid()
@@ -30,6 +33,11 @@
End Set
End Property
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
Return LabText.Text
diff --git a/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
index a094029..53ce505 100644
--- a/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyIconTextButton
+Imports System.Windows.Markup
+
+
+Public Class MyIconTextButton
'基础
@@ -30,6 +33,11 @@
End Set
End Property
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
Return GetValue(TextProperty)
@@ -39,9 +47,9 @@
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)))
+ 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
diff --git a/Plain Craft Launcher 2/Controls/MyImage.vb b/Plain Craft Launcher 2/Controls/MyImage.vb
index 92ba1c0..eaf7289 100644
--- a/Plain Craft Launcher 2/Controls/MyImage.vb
+++ b/Plain Craft Launcher 2/Controls/MyImage.vb
@@ -138,7 +138,7 @@ RetryStart:
ElseIf EnableCache Then
'保存缓存并显示
If File.Exists(TempPath) Then File.Delete(TempPath)
- Rename(TempDownloadingPath, TempPath)
+ FileSystem.Rename(TempDownloadingPath, TempPath)
RunInUi(Sub() ActualSource = TempPath)
Else
'直接显示
diff --git a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb
index 0d1f03d..87a86f7 100644
--- a/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyListItem.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyListItem
+Imports System.Windows.Markup
+
+
+Public Class MyListItem
Implements IMyRadio
Public Event Click(sender As Object, e As MouseButtonEventArgs)
@@ -161,6 +164,11 @@
End Property
'标题
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabTitle.Inlines
+ End Get
+ End Property
Public Property Title As String
Get
Return GetValue(TitleProperty)
@@ -337,16 +345,7 @@
Return _Checked
End Get
Set(value As Boolean)
- SetChecked(value, False, True, _Half)
- End Set
- End Property
- Private _Half As Boolean = False
- Public Property Half As Boolean
- Get
- Return _Half
- End Get
- Set(value As Boolean)
- SetChecked(_Checked, False, True, value)
+ SetChecked(value, False, True)
End Set
End Property
'''
@@ -355,8 +354,7 @@
''' 新的 Checked 属性。
''' 是否由用户引发。
''' 是否执行动画。
- ''' 是否为部分选中。
- Public Sub SetChecked(value As Boolean, user As Boolean, anime As Boolean, Optional half As Boolean = False)
+ Public Sub SetChecked(value As Boolean, user As Boolean, anime As Boolean)
Try
'自定义属性基础
@@ -374,9 +372,8 @@
End If
_Checked = value
Else
- If value = _Checked AndAlso half = _Half Then Exit Sub
+ If value = _Checked Then Exit Sub
_Checked = value
- _Half = half
If IsInitialized Then
RaiseEvent Changed(Me, ChangedEventArgs)
If ChangedEventArgs.Handled Then
@@ -440,9 +437,9 @@
'由无变有
If Not IsNothing(RectCheck) Then
Dim Delta = ActualHeight - RectCheck.ActualHeight - 12
- Anim.Add(AaHeight(RectCheck, Delta * If(half, 0.2, 0.4), 200,, New AniEaseOutFluent(AniEasePower.Weak)))
- Anim.Add(AaHeight(RectCheck, Delta * If(half, 0.3, 0.6), 300,, New AniEaseOutBack(AniEasePower.Weak)))
- Anim.Add(AaOpacity(RectCheck, If(half, 0.5, 1) - RectCheck.Opacity, 30))
+ Anim.Add(AaHeight(RectCheck, Delta * 0.4, 200,, New AniEaseOutFluent(AniEasePower.Weak)))
+ Anim.Add(AaHeight(RectCheck, Delta * 0.6, 300,, New AniEaseOutBack(AniEasePower.Weak)))
+ Anim.Add(AaOpacity(RectCheck, 1 - RectCheck.Opacity, 30))
RectCheck.VerticalAlignment = VerticalAlignment.Center
RectCheck.Margin = New Thickness(-1, 0, 0, 0)
End If
@@ -461,17 +458,12 @@
Else
'不使用动画
AniStop("MyListItem Checked " & Uuid)
-
- If Checked OrElse half Then
+ If Checked Then
If Not IsNothing(RectCheck) Then
RectCheck.Height = Double.NaN
RectCheck.Margin = New Thickness(-1, 6, 0, 6)
RectCheck.Opacity = 1
RectCheck.VerticalAlignment = VerticalAlignment.Stretch
- If half Then
- RectCheck.Height = Height * 0.4
- RectCheck.Opacity = 0.5
- End If
End If
SetResourceReference(ForegroundProperty, If(Height < 40, "ColorBrush3", "ColorBrush2"))
Else
diff --git a/Plain Craft Launcher 2/Controls/MyPageRight.vb b/Plain Craft Launcher 2/Controls/MyPageRight.vb
index 4fafd60..bb04053 100644
--- a/Plain Craft Launcher 2/Controls/MyPageRight.vb
+++ b/Plain Craft Launcher 2/Controls/MyPageRight.vb
@@ -353,6 +353,10 @@
If Control.RenderTransform IsNot Nothing AndAlso TypeOf Control.RenderTransform Is TranslateTransform Then Control.RenderTransform = Nothing
Next
For Each Control As FrameworkElement In GetAllAnimControls(Element)
+ If TypeOf Control Is MyExtraTextButton Then
+ CType(Control, MyExtraTextButton).Show = True
+ Continue For
+ End If
Control.Opacity = 0
Control.RenderTransform = New TranslateTransform(0, -16)
AniList.Add(AaOpacity(Control, 1, 150, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
@@ -379,6 +383,10 @@
Dim Delay As Integer = 0
For Each Element In RealElements
For Each Control As FrameworkElement In GetAllAnimControls(Element)
+ If TypeOf Control Is MyExtraTextButton Then
+ CType(Control, MyExtraTextButton).Show = False
+ Continue For
+ End If
Control.IsHitTestVisible = False
AniList.Add(AaOpacity(Control, -1, 90, Delay))
AniList.Add(AaTranslateY(Control, -6, 90, Delay))
@@ -392,12 +400,13 @@
AniList.Add(AaTranslateX(Scroll, 10 - CType(Scroll.RenderTransform, TranslateTransform).X, 90, 0, New AniEaseInFluent))
End If
'结束
- AniList.Add(AaCode(Sub()
- For Each Element In RealElements
- Element.Visibility = Visibility.Collapsed
- Next
- PageOnExitAnimationFinished()
- End Sub,, True))
+ AniList.Add(AaCode(
+ Sub()
+ For Each Element In RealElements
+ Element.Visibility = Visibility.Collapsed
+ Next
+ PageOnExitAnimationFinished()
+ End Sub,, True))
AniStart(AniList, "PageRight PageChange " & PageUuid)
End Sub
@@ -409,7 +418,7 @@
End Function
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 MyCard OrElse TypeOf Element Is MyHint OrElse TypeOf Element Is TextBlock OrElse TypeOf Element Is MyTextButton Then
+ If TypeOf Element Is MyCard OrElse TypeOf Element Is MyHint OrElse TypeOf Element Is MyExtraTextButton OrElse TypeOf Element Is TextBlock OrElse TypeOf Element Is MyTextButton Then
AllControls.Add(Element)
ElseIf TypeOf Element Is ContentControl Then
Dim Content = CType(Element, ContentControl).Content
diff --git a/Plain Craft Launcher 2/Controls/MyRadioBox.xaml b/Plain Craft Launcher 2/Controls/MyRadioBox.xaml
index d461839..2a8849e 100644
--- a/Plain Craft Launcher 2/Controls/MyRadioBox.xaml
+++ b/Plain Craft Launcher 2/Controls/MyRadioBox.xaml
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusVisualStyle="{x:Null}"
MinWidth="20" x:Name="PanBack" UseLayoutRounding="False" SnapsToDevicePixels="False" MinHeight="20" Background="{StaticResource ColorBrushSemiTransparent}" Focusable="True">
-
+
diff --git a/Plain Craft Launcher 2/Controls/MyRadioBox.xaml.vb b/Plain Craft Launcher 2/Controls/MyRadioBox.xaml.vb
index ba5c05e..816d7f9 100644
--- a/Plain Craft Launcher 2/Controls/MyRadioBox.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyRadioBox.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyRadioBox
+Imports System.Windows.Markup
+
+
+Public Class MyRadioBox
Implements IMyRadio
'基础
@@ -10,27 +13,29 @@
Public Event Changed(sender As Object, e As RouteEventArgs) Implements IMyRadio.Changed
'自定义属性
-
- Private _Checked As Boolean = False '是否选中
Public Property Checked As Boolean
Get
- Return _Checked
+ Return GetValue(CheckedProperty)
End Get
Set(value As Boolean)
- SetChecked(value, False, True)
+ SetChecked(value, False)
End Set
End Property
+ Public Shared ReadOnly CheckedProperty As DependencyProperty =
+ DependencyProperty.Register("Checked", GetType(Boolean), GetType(MyRadioBox), New PropertyMetadata(False,
+ Sub(d As MyRadioBox, e As DependencyPropertyChangedEventArgs)
+ '在使用 XAML 设置 Checked 属性时,不会触发 Checked_Set 方法,所以需要在这里手动触发 UI 改变
+ If Not d.IsLoaded Then d.SyncUI()
+ End Sub))
+
'''
''' 手动设置 Checked 属性。
'''
''' 新的 Checked 属性。
''' 是否由用户引发。
- ''' 是否执行动画。
- Public Sub SetChecked(value As Boolean, user As Boolean, anime As Boolean)
+ Public Sub SetChecked(value As Boolean, user As Boolean)
Try
-
'Preview 事件
-
If value AndAlso user Then
Dim e = New RouteEventArgs(user)
RaiseEvent PreviewCheck(Me, e)
@@ -41,28 +46,24 @@
End If
'自定义属性基础
-
Dim IsChanged As Boolean = False
- If IsLoaded AndAlso Not value = _Checked Then RaiseEvent PreviewChange(Me, New RouteEventArgs(user))
- If Not value = _Checked Then
- _Checked = value
+ If IsLoaded AndAlso Not value = Checked Then RaiseEvent PreviewChange(Me, New RouteEventArgs(user))
+ If Not value = Checked Then
+ SetValue(CheckedProperty, value)
IsChanged = True
End If
'保证只有一个单选框选中
-
If Parent Is Nothing Then Exit Sub
Dim RadioboxList As New List(Of MyRadioBox)
Dim CheckedCount As Integer = 0
- '收集控件列表与选中个数
- For Each Control In CType(Parent, Object).Children
+ For Each Control In CType(Parent, Object).Children '收集控件列表与选中个数
If TypeOf Control Is MyRadioBox Then
RadioboxList.Add(Control)
If Control.Checked Then CheckedCount += 1
End If
Next
- '判断选中情况
- Select Case CheckedCount
+ Select Case CheckedCount '判断选中情况
Case 0
'没有任何单选框被选中,选择第一个
RadioboxList(0).Checked = True
@@ -87,67 +88,74 @@
Next
End If
End Select
- 'End If
-
- '更改动画
-
- If Not IsChanged Then Exit Sub
- If IsLoaded AndAlso AniControlEnabled = 0 AndAlso anime Then '防止默认属性变更触发动画
- If Checked Then
- '由无变有
- If ShapeDot.Opacity < 0.01 Then ShapeDot.Opacity = 1
- AniStart({
- AaScale(ShapeBorder, 10 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent(AniEasePower.Weak), , True),
- AaScale(ShapeBorder, 8, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.6, New AniEaseOutBack, , True)
- }, "MyRadioBox Border " & Uuid)
- AniStart({
- AaScale(ShapeDot, 9 - ShapeDot.Width, AnimationTimeOfCheck * 2.6,, New AniEaseOutBack(AniEasePower.Weak), , True),
- AaOpacity(ShapeDot, 1 - ShapeDot.Opacity, AnimationTimeOfCheck * 0.5, AnimationTimeOfCheck * 0.6)
- }, "MyRadioBox Dot " & Uuid)
- AniStart({
- AaColor(ShapeBorder, Ellipse.StrokeProperty, If(IsMouseOver, "ColorBrush3", If(IsEnabled, "ColorBrush2", "ColorBrushGray4")), AnimationTimeOfCheck)
- }, "MyRadioBox BorderColor " & Uuid)
- Else
- '由有变无
- AniStart({
- AaScale(ShapeBorder, 18 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True)
- }, "MyRadioBox Border " & Uuid)
- AniStart({
- AaScale(ShapeDot, -ShapeDot.Width, AnimationTimeOfCheck, , New AniEaseInFluent, , True),
- AaOpacity(ShapeDot, -ShapeDot.Opacity, AnimationTimeOfCheck * 0.5, AnimationTimeOfCheck * 0.2)
- }, "MyRadioBox Dot " & Uuid)
- AniStart({
- AaColor(ShapeBorder, Ellipse.StrokeProperty, If(IsMouseOver, "ColorBrush3", If(IsEnabled, "ColorBrush1", "ColorBrushGray4")), AnimationTimeOfCheck)
- }, "MyRadioBox BorderColor " & Uuid)
- End If
- Else
- '不使用动画
- AniStop("MyRadioBox Border " & Uuid)
- AniStop("MyRadioBox Dot " & Uuid)
- AniStop("MyRadioBox BorderColor " & Uuid)
- If Checked Then
- ShapeDot.Width = 9
- ShapeDot.Height = 9
- ShapeDot.Opacity = 1
- ShapeDot.Margin = New Thickness(5.5, 0, 0, 0)
- ShapeBorder.SetResourceReference(Ellipse.StrokeProperty, If(IsEnabled, "ColorBrush2", "ColorBrushGray4"))
- Else
- ShapeDot.Width = 0
- ShapeDot.Height = 0
- ShapeDot.Opacity = 0
- ShapeDot.Margin = New Thickness(10, 0, 0, 0)
- ShapeBorder.SetResourceReference(Ellipse.StrokeProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray4"))
- End If
- End If
'触发事件
- If Checked Then RaiseEvent Check(Me, New RouteEventArgs(user))
- RaiseEvent Changed(Me, New RouteEventArgs(user))
+ If IsChanged Then
+ If Checked Then RaiseEvent Check(Me, New RouteEventArgs(user))
+ RaiseEvent Changed(Me, New RouteEventArgs(user))
+ End If
+ '更改动画
+ SyncUI()
Catch ex As Exception
Log(ex, "单选框勾选改变错误", LogLevel.Hint)
End Try
End Sub
+ Private Sub SyncUI()
+ If AniControlEnabled = 0 AndAlso IsLoaded Then '防止默认属性变更触发动画
+ If Checked Then
+ '由无变有
+ If ShapeDot.Opacity < 0.01 Then ShapeDot.Opacity = 1
+ AniStart({
+ AaScale(ShapeBorder, 10 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent(AniEasePower.Weak), , True),
+ AaScale(ShapeBorder, 8, AnimationTimeOfCheck * 2, AnimationTimeOfCheck * 0.6, New AniEaseOutBack, , True)
+ }, "MyRadioBox Border " & Uuid)
+ AniStart({
+ AaScale(ShapeDot, 9 - ShapeDot.Width, AnimationTimeOfCheck * 2.6,, New AniEaseOutBack(AniEasePower.Weak), , True),
+ AaOpacity(ShapeDot, 1 - ShapeDot.Opacity, AnimationTimeOfCheck * 0.5, AnimationTimeOfCheck * 0.6)
+ }, "MyRadioBox Dot " & Uuid)
+ AniStart({
+ AaColor(ShapeBorder, Ellipse.StrokeProperty, If(IsMouseOver, "ColorBrush3", If(IsEnabled, "ColorBrush2", "ColorBrushGray4")), AnimationTimeOfCheck)
+ }, "MyRadioBox BorderColor " & Uuid)
+ Else
+ '由有变无
+ AniStart({
+ AaScale(ShapeBorder, 18 - ShapeBorder.Width, AnimationTimeOfCheck, , New AniEaseOutFluent, , True)
+ }, "MyRadioBox Border " & Uuid)
+ AniStart({
+ AaScale(ShapeDot, -ShapeDot.Width, AnimationTimeOfCheck, , New AniEaseInFluent, , True),
+ AaOpacity(ShapeDot, -ShapeDot.Opacity, AnimationTimeOfCheck * 0.5, AnimationTimeOfCheck * 0.2)
+ }, "MyRadioBox Dot " & Uuid)
+ AniStart({
+ AaColor(ShapeBorder, Ellipse.StrokeProperty, If(IsMouseOver, "ColorBrush3", If(IsEnabled, "ColorBrush1", "ColorBrushGray4")), AnimationTimeOfCheck)
+ }, "MyRadioBox BorderColor " & Uuid)
+ End If
+ Else
+ '不使用动画
+ AniStop("MyRadioBox Border " & Uuid)
+ AniStop("MyRadioBox Dot " & Uuid)
+ AniStop("MyRadioBox BorderColor " & Uuid)
+ If Checked Then
+ ShapeDot.Width = 9
+ ShapeDot.Height = 9
+ ShapeDot.Opacity = 1
+ ShapeDot.Margin = New Thickness(5.5, 0, 0, 0)
+ ShapeBorder.SetResourceReference(Ellipse.StrokeProperty, If(IsEnabled, "ColorBrush2", "ColorBrushGray4"))
+ Else
+ ShapeDot.Width = 0
+ ShapeDot.Height = 0
+ ShapeDot.Opacity = 0
+ ShapeDot.Margin = New Thickness(10, 0, 0, 0)
+ ShapeBorder.SetResourceReference(Ellipse.StrokeProperty, If(IsEnabled, "ColorBrush1", "ColorBrushGray4"))
+ End If
+ End If
+ End Sub
+
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
Return GetValue(TextProperty)
@@ -165,9 +173,9 @@
Private AllowMouseDown As Boolean = True
Private Sub Radiobox_MouseUp() Handles Me.MouseLeftButtonUp
If Not MouseDowned Then Exit Sub
- MouseDowned = False
Log("[Control] 按下单选框:" & Text)
- SetChecked(True, True, True)
+ SetChecked(True, True)
+ MouseDowned = False
AniStart(AaColor(ShapeBorder, Ellipse.FillProperty, "ColorBrushHalfWhite", 100), "MyRadioBox Background " & Uuid)
End Sub
Private Sub Radiobox_MouseDown() Handles Me.MouseLeftButtonDown
diff --git a/Plain Craft Launcher 2/Controls/MyRadioButton.xaml.vb b/Plain Craft Launcher 2/Controls/MyRadioButton.xaml.vb
index 8d9537b..6478a78 100644
--- a/Plain Craft Launcher 2/Controls/MyRadioButton.xaml.vb
+++ b/Plain Craft Launcher 2/Controls/MyRadioButton.xaml.vb
@@ -1,4 +1,7 @@
-Public Class MyRadioButton
+Imports System.Windows.Markup
+
+
+Public Class MyRadioButton
'基础
@@ -108,6 +111,12 @@
Log(ex, "单选按钮勾选改变错误", LogLevel.Hint)
End Try
End Sub
+
+ Public ReadOnly Property Inlines As InlineCollection
+ Get
+ Return LabText.Inlines
+ End Get
+ End Property
Public Property Text As String
Get
Return GetValue(TextProperty)
diff --git a/Plain Craft Launcher 2/FormMain.xaml b/Plain Craft Launcher 2/FormMain.xaml
index d0a3a5e..89def5a 100644
--- a/Plain Craft Launcher 2/FormMain.xaml
+++ b/Plain Craft Launcher 2/FormMain.xaml
@@ -133,7 +133,7 @@
+ LogoScale="0.87" Logo="M1097 584 250 584 562 896C591 925 591 972 562 1001 533 1030 487 1030 458 1001L21 565C6 550-0 531 0 511L0 511 0 511C-0 492 6 472 21 457L458 21C487-7 533-7 562 21 591 50 591 97 562 126L250 438 1097 438C1137 438 1170 471 1170 511 1170 551 1137 584 1097 584L1097 584Z" />
diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb
index e9cf5c6..c6e2c80 100644
--- a/Plain Craft Launcher 2/FormMain.xaml.vb
+++ b/Plain Craft Launcher 2/FormMain.xaml.vb
@@ -10,201 +10,112 @@ Public Class FormMain
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
'统计更新日志条目
#If BETA Then
+ If LastVersion < 352 Then 'Release 2.9.1
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:导出整合包功能"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:支持在超长路径下安装、启动游戏"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法安装 1.21 OptiFine"))
+ FeatureCount += 23
+ BugCount += 10
+ End If
+ If LastVersion < 349 Then 'Release 2.8.13
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:网络环境不佳时,Mod、整合包页面的加载速度"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:部分电脑无法打开程序"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:可能无法识别、更新 Mod"))
+ FeatureCount += 6
+ BugCount += 14
+ End If
If LastVersion < 347 Then 'Release 2.8.12
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面添加下载 Mod、安装 Mod 选项"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 详情页面支持按加载器、游戏版本进行分类和筛选"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持安装同时包含 modpack 文件和启动器的懒人包"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化整合包导入流程"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:Mod 管理页面添加下载 Mod、安装 Mod 选项"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:Mod 详情页面支持按加载器、游戏版本进行分类和筛选"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:支持安装同时包含 modpack 文件和启动器的懒人包"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化:整合包导入流程"))
FeatureCount += 43
BugCount += 37
End If
If LastVersion < 342 Then 'Release 2.8.9
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持下载原版服务端"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "本地 Mod 的标题支持选择显示 Mod 原始文件名"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复搜索后启用/禁用 Mod 时出错的 Bug"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:支持下载原版服务端"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:本地 Mod 的标题支持选择显示 Mod 原始文件名"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:搜索后启用/禁用 Mod 时出错"))
FeatureCount += 17
BugCount += 13
End If
If LastVersion < 340 Then 'Release 2.8.8
- If LastVersion = 338 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复数个与新正版登录相关的严重 Bug"))
+ If LastVersion = 338 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:数个与新正版登录相关的严重 Bug"))
FeatureCount += 3
BugCount += 7
End If
If LastVersion < 338 Then 'Release 2.8.7
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "使用新的正版登录方式,以提高安全性"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化安装整合包、检索 Mod 的稳定性"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法加载部分 Mod 的图标的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复在 Mod 管理页面删除 Mod 导致报错的 Bug"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "优化:使用新的正版登录方式,以提高安全性"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:安装整合包、检索 Mod 的稳定性"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法加载部分 Mod 的图标"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:在 Mod 管理页面删除 Mod 导致报错"))
FeatureCount += 11
BugCount += 21
End If
- If LastVersion < 336 Then 'Release 2.8.6
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "下载 Mod 时会使用 MCIM 国内镜像源"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面允许筛选可更新/启用/禁用的 Mod"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "打开 PCL 时会自动安装同目录下的 modpack.zip"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "爱发电域名迁移至 afdian.com"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复 1.20.1+ 离线登录使用正版皮肤时无法保存游戏的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装的 1.14~1.15 Forge+OptiFine 无法进入世界的 Bug"))
- FeatureCount += 19
- BugCount += 24
- End If
- If LastVersion < 332 Then 'Release 2.8.3
- If LastVersion = 330 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复部分玩家无法启动 MC 的 Bug"))
- End If
- If LastVersion < 330 Then 'Release 2.8.2
- FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "NeoForge 兼容与自动安装"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持编译、运行 PCL 开源代码"))
- FeatureCount += 15
- BugCount += 22
- End If
- If LastVersion < 326 Then 'Release 2.7.4
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "会自动隐藏明显不可用的自动安装选项"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化正版登录流程和 MC 性能"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复正版登录时弹出脚本错误提示的 Bug"))
- FeatureCount += 17
- BugCount += 19
- End If
- If LastVersion < 323 Then 'Release 2.7.3
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 启动游戏前进行内存优化 设置"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化 MC 性能"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装 OptiFine 有概率失败的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复启动 Fabric 1.20.5+ 时无法正确选择 Java 的 Bug"))
- FeatureCount += 22
- BugCount += 21
- End If
- If LastVersion < 321 Then 'Release 2.7.1
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复启动部分整合包导致设置丢失的 Bug"))
- BugCount += 1
- End If
- If LastVersion < 319 Then 'Release 2.7.0
- FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "支持更新 Mod"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持查看可更新的 Mod 的更新日志"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持滑动鼠标快速选中、取消选中多个 Mod"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 MC 24w14a+ 的 Bug"))
- FeatureCount += 10
- BugCount += 10
- End If
#Else
'5: FEAT+
'4: IMP+ FEAT*
'3:BUG+ IMP* FEAT-
'2:BUG* IMP-
'1:BUG-
- If LastVersion < 346 Then 'Snapshot 2.8.13
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化网络环境不佳时,Mod、整合包页面的加载速度"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复部分电脑无法打开程序的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复可能无法识别、更新 Mod 的 Bug"))
+ If LastVersion < 351 Then 'Snapshot 2.9.1
+ If LastVersion = 350 Then
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "优化:对导出整合包功能进行了 7 项优化,详见完整更新日志"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法进行第三方登录"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:在部分版本下,打开导出页面会导致启动器崩溃"))
+ BugCount += 1
+ End If
+ BugCount += 2
+ End If
+ If LastVersion < 350 Then 'Snapshot 2.9.0
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "新增:导出整合包功能"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:支持在超长路径下安装、启动游戏"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法安装 1.21 OptiFine"))
+ FeatureCount += 21
+ BugCount += 10
+ End If
+ If LastVersion < 348 Then 'Snapshot 2.8.13
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:网络环境不佳时,Mod、整合包页面的加载速度"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:部分电脑无法打开程序"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:可能无法识别、更新 Mod"))
FeatureCount += 6
BugCount += 14
End If
If LastVersion < 346 Then 'Snapshot 2.8.12
- If LastVersion = 345 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复帮助页面报错的 Bug"))
+ If LastVersion = 345 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:帮助页面报错"))
End If
If LastVersion < 345 Then 'Snapshot 2.8.11
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面添加下载 Mod、安装 Mod 选项"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 详情页面支持按加载器、游戏版本进行分类和筛选"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:Mod 管理页面添加下载 Mod、安装 Mod 选项"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:Mod 详情页面支持按加载器、游戏版本进行分类和筛选"))
FeatureCount += 23
BugCount += 21
End If
If LastVersion < 343 Then 'Snapshot 2.8.10
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持安装同时包含 modpack 文件和启动器的懒人包"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化整合包导入流程"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:支持安装同时包含 modpack 文件和启动器的懒人包"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "优化:整合包导入流程"))
FeatureCount += 20
BugCount += 16
End If
If LastVersion < 341 Then 'Snapshot 2.8.9
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "支持下载原版服务端"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "本地 Mod 的标题支持选择显示 Mod 原始文件名"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复搜索后启用/禁用 Mod 时出错的 Bug"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:支持下载原版服务端"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "新增:本地 Mod 的标题支持选择显示 Mod 原始文件名"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:搜索后启用/禁用 Mod 时出错"))
FeatureCount += 17
BugCount += 13
End If
If LastVersion < 339 Then 'Snapshot 2.8.8
- If LastVersion = 337 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复数个与新正版登录相关的严重 Bug"))
+ If LastVersion = 337 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:数个与新正版登录相关的严重 Bug"))
FeatureCount += 3
BugCount += 7
End If
If LastVersion < 337 Then 'Snapshot 2.8.7
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "使用新的正版登录方式,以提高安全性"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化安装整合包、检索 Mod 的稳定性"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法加载部分 Mod 的图标的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复在 Mod 管理页面删除 Mod 导致报错的 Bug"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "优化:使用新的正版登录方式,以提高安全性"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:安装整合包、检索 Mod 的稳定性"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:无法加载部分 Mod 的图标"))
+ FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:在 Mod 管理页面删除 Mod 导致报错"))
FeatureCount += 11
BugCount += 21
End If
- If LastVersion < 335 Then 'Snapshot 2.8.6
- BugCount += 2
- End If
- If LastVersion < 334 Then 'Snapshot 2.8.5
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "Mod 管理页面允许筛选可更新/启用/禁用的 Mod"))
- If LastVersion = 333 Then
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装愚人节和预发布版本的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法导出错误报告的 Bug"))
- End If
- FeatureCount += 6
- BugCount += 7
- End If
- If LastVersion < 333 Then 'Snapshot 2.8.4
- FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "下载 Mod 时会使用 MCIM 国内镜像源"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "打开 PCL 时会自动安装同目录下的 modpack.zip"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "爱发电域名迁移至 afdian.com"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复 1.20.1+ 离线登录使用正版皮肤时无法保存游戏的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装的 1.14~1.15 Forge+OptiFine 无法进入世界的 Bug"))
- FeatureCount += 13
- BugCount += 17
- End If
- If LastVersion < 331 Then 'Snapshot 2.8.3
- If LastVersion = 329 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "修复部分玩家无法启动 MC 的 Bug"))
- End If
- If LastVersion < 329 Then 'Snapshot 2.8.2
- If LastVersion >= 327 Then
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装 Beta 版 NeoForge 的整合包的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复自动安装无法选择部分 OptiFine 的 Bug"))
- End If
- FeatureCount += 4
- BugCount += 8
- End If
- If LastVersion < 328 Then 'Snapshot 2.8.1
- If LastVersion = 327 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法安装 Forge 1.12.2- 的 Bug"))
- If LastVersion = 327 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法输入解锁码的 Bug"))
- If LastVersion = 327 Then BugCount += 1
- End If
- If LastVersion < 327 Then 'Snapshot 2.8.0
- FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "NeoForge 兼容与自动安装"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持编译、运行 PCL 开源代码"))
- FeatureCount += 11
- BugCount += 14
- End If
- If LastVersion < 325 Then 'Snapshot 2.7.4
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "会自动隐藏明显不可用的自动安装选项"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化正版登录流程和 MC 性能"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复正版登录时弹出脚本错误提示的 Bug"))
- FeatureCount += 17
- BugCount += 19
- End If
- If LastVersion < 324 Then 'Snapshot 2.7.3
- FeatureCount += 4
- BugCount += 3
- End If
- If LastVersion < 322 Then 'Snapshot 2.7.2
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "添加 启动游戏前进行内存优化 设置"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复安装 OptiFine 有概率失败的 Bug"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复启动 Fabric 1.20.5+ 时无法正确选择 Java 的 Bug"))
- FeatureCount += 18
- BugCount += 18
- End If
- If LastVersion < 320 Then 'Snapshot 2.7.1
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复启动部分整合包导致设置丢失的 Bug"))
- BugCount += 1
- End If
- If LastVersion < 318 Then 'Snapshot 2.7.0
- FeatureList.Add(New KeyValuePair(Of Integer, String)(5, "支持更新 Mod"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持查看可更新的 Mod 的更新日志"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "支持滑动鼠标快速选中、取消选中多个 Mod"))
- FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 MC 24w14a+ 的 Bug"))
- FeatureCount += 10
- BugCount += 10
- End If
#End If
'整理更新日志文本
Dim ContentList As New List(Of String)
@@ -366,6 +277,7 @@ Public Class FormMain
DlClientListMojangLoader.Start(1)
RunCountSub()
ServerLoader.Start(1)
+ RunInNewThread(AddressOf TryClearTaskTemp, "TryClearTaskTemp", ThreadPriority.BelowNormal)
Catch ex As Exception
Log(ex, "初始化加载池运行失败", LogLevel.Feedback)
End Try
@@ -466,7 +378,7 @@ Public Class FormMain
Log("[Start] 已从老版本迁移微软登录结果")
End If
'输出更新日志
- If LastVersionCode = 0 Then Exit Sub
+ If LastVersionCode <= 0 Then Exit Sub
If LowerVersionCode >= VersionCode Then Exit Sub
ShowUpdateLog(LowerVersionCode)
End Sub
@@ -657,6 +569,7 @@ Public Class FormMain
End Sub
Private Sub FormMain_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseDown
'鼠标侧键返回上一级
+ If FrmMain.PanMsg.Children.Count > 0 OrElse WaitingMyMsgBox.Any Then Exit Sub '弹窗中(#5513)
If e.ChangedButton = MouseButton.XButton1 OrElse e.ChangedButton = MouseButton.XButton2 Then TriggerPageBack()
End Sub
Private Sub TriggerPageBack()
diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
index dd6c80a..1e98911 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
@@ -12,13 +12,13 @@ Public Module ModBase
#Region "声明"
'下列版本信息由更新器自动修改
- Public Const VersionBaseName As String = "2.8.13" '不含分支前缀的显示用版本名
- Public Const VersionStandardCode As String = "2.8.13." & VersionBranchCode '标准格式的四段式版本号
+ Public Const VersionBaseName As String = "2.9.1" '不含分支前缀的显示用版本名
+ Public Const VersionStandardCode As String = "2.9.1." & VersionBranchCode '标准格式的四段式版本号
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
#If BETA Then
- Public Const VersionCode As Integer = 347 'Release
+ Public Const VersionCode As Integer = 352 'Release
#Else
- Public Const VersionCode As Integer = 348 'Snapshot
+ Public Const VersionCode As Integer = 351 'Snapshot
#End If
'自动生成的版本信息
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -180,10 +180,6 @@ Public Module ModBase
'''
Public Const IconButtonServer As String = "M224 160a64 64 0 0 0-64 64v576a64 64 0 0 0 64 64h576a64 64 0 0 0 64-64V224a64 64 0 0 0-64-64H224z m0 384h576v256H224v-256z m192 96v64h320v-64H416z m-128 0v64h64v-64H288zM224 224h576v256H224V224z m192 96v64h320v-64H416z m-128 0v64h64v-64H288z"
'''
- ''' 图标,向右箭头,1x
- '''
- Public Const IconButtonRight As String = "M757.792745 435.407215L419.597482 96.904967c-40.010393-40.010393-104.886579-40.010393-144.896972 0-40.010393 40.010393-40.010393 104.988908 0 144.9993L540.344959 507.855701 274.70051 773.807135c-40.010393 40.112721-40.010393 104.988908 0 144.9993 40.010393 40.010393 104.886579 40.010393 144.896972 0l338.092935-338.39992c40.112721-40.010393 40.112721-104.988908 0.102328-144.9993z"
- '''
''' 图标,音符,1x
'''
Public Const IconMusic As String = "M348.293565 716.53287V254.797913c0-41.672348 28.004174-78.358261 68.919652-90.37913L815.994435 40.826435c62.775652-18.610087 125.907478 26.579478 125.907478 89.933913v539.158261c8.013913 42.25113-8.94887 89.177043-47.014956 127.109565a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c21.014261 1.024 40.514783 5.164522 57.878261 12.065391V233.338435c0-12.109913-10.551652-20.034783-20.569044-20.034783a24.620522 24.620522 0 0 0-5.787826 0.934957L439.785739 338.18713a19.545043 19.545043 0 0 0-14.825739 19.144348v438.984348H423.846957c11.53113 43.987478-5.164522 94.208-45.412174 134.322087a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c20.791652 1.024 40.069565 5.075478 57.299478 11.842783z"
@@ -961,17 +957,17 @@ Public Module ModBase
''' 要求的格式。如:“常用图片文件(*.png;*.jpg)|*.png;*.jpg”。
''' 弹窗的标题。
''' 默认的文件名。
- Public Function SelectAs(Title As String, FileName As String, Optional FileFilter As String = Nothing, Optional DefaultDir As String = Nothing) As String
+ Public Function SelectSaveFile(Title As String, FileName As String, Optional FileFilter As String = Nothing, Optional InitialDirectory As String = Nothing) As String
Using fileDialog As New Forms.SaveFileDialog
fileDialog.AddExtension = True
fileDialog.AutoUpgradeEnabled = True
fileDialog.Title = Title
fileDialog.FileName = FileName
If FileFilter IsNot Nothing Then fileDialog.Filter = FileFilter
- If DefaultDir IsNot Nothing Then fileDialog.InitialDirectory = DefaultDir
+ If Not String.IsNullOrEmpty(InitialDirectory) AndAlso Directory.Exists(InitialDirectory) Then fileDialog.InitialDirectory = InitialDirectory
fileDialog.ShowDialog()
- SelectAs = If(fileDialog.FileName.Contains(":\"), fileDialog.FileName, "")
- Log("[UI] 选择文件返回:" & SelectAs)
+ SelectSaveFile = If(fileDialog.FileName.Contains(":\"), fileDialog.FileName, "")
+ Log("[UI] 选择文件返回:" & SelectSaveFile)
End Using
End Function
'''
@@ -979,7 +975,7 @@ Public Module ModBase
'''
''' 要求的格式。如:“常用图片文件(*.png;*.jpg)|*.png;*.jpg”。
''' 弹窗的标题。
- Public Function SelectFile(FileFilter As String, Title As String) As String
+ Public Function SelectFile(FileFilter As String, Title As String, Optional InitialDirectory As String = Nothing) As String
Using fileDialog As New Forms.OpenFileDialog
fileDialog.AddExtension = True
fileDialog.AutoUpgradeEnabled = True
@@ -988,6 +984,7 @@ Public Module ModBase
fileDialog.Multiselect = False
fileDialog.Title = Title
fileDialog.ValidateNames = True
+ If Not String.IsNullOrEmpty(InitialDirectory) AndAlso Directory.Exists(InitialDirectory) Then fileDialog.InitialDirectory = InitialDirectory
fileDialog.ShowDialog()
Log("[UI] 选择单个文件返回:" & fileDialog.FileName)
Return fileDialog.FileName
@@ -1084,6 +1081,37 @@ Re:
End Try
End Function
'''
+ ''' 获取文件 SHA512,若失败则返回空字符串。
+ '''
+ Public Function GetFileSHA512(FilePath As String) As String
+ Dim Retry As Boolean = False
+Re:
+ Try
+ ''检测该文件是否在下载中,若在下载则放弃检测
+ 'If IgnoreOnDownloading AndAlso NetManage.Files.ContainsKey(FilePath) AndAlso NetManage.Files(FilePath).State <= NetState.Merge Then Return ""
+ '获取 SHA512
+ Dim file As New FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
+ Dim sha512 As SHA512 = New SHA512CryptoServiceProvider()
+ Dim retval As Byte() = sha512.ComputeHash(file)
+ file.Close()
+ Dim Result As New StringBuilder()
+ For i As Integer = 0 To retval.Length - 1
+ Result.Append(retval(i).ToString("x2"))
+ Next
+ Return Result.ToString
+ Catch ex As Exception
+ If Retry OrElse TypeOf ex Is FileNotFoundException Then
+ Log(ex, "获取文件 SHA512 失败:" & FilePath)
+ Return ""
+ Else
+ Retry = True
+ Log(ex, "获取文件 SHA512 可重试失败:" & FilePath, LogLevel.Normal)
+ Thread.Sleep(RandomInteger(200, 500))
+ GoTo Re
+ End If
+ End Try
+ End Function
+ '''
''' 获取文件 SHA256,若失败则返回空字符串。
'''
Public Function GetFileSHA256(FilePath As String) As String
@@ -1094,8 +1122,8 @@ Re:
'If IgnoreOnDownloading AndAlso NetManage.Files.ContainsKey(FilePath) AndAlso NetManage.Files(FilePath).State <= NetState.Merge Then Return ""
'获取 SHA256
Dim file As New FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
- Dim sha1 As SHA256 = New SHA256CryptoServiceProvider()
- Dim retval As Byte() = sha1.ComputeHash(file)
+ Dim sha256 As SHA256 = New SHA256CryptoServiceProvider()
+ Dim retval As Byte() = sha256.ComputeHash(file)
file.Close()
Dim Result As New StringBuilder()
For i As Integer = 0 To retval.Length - 1
@@ -1144,7 +1172,7 @@ Re:
End Try
End Function
'''
- ''' 获取流 SHA1,若失败则返回空字符串。
+ ''' 获取流的 SHA1,若失败则返回空字符串。
'''
Public Function GetAuthSHA1(Stream As Stream) As String
Try
@@ -1336,11 +1364,22 @@ RetryDir:
''' 遍历文件夹中的所有文件。
'''
Public Function EnumerateFiles(Directory As String) As IEnumerable(Of FileInfo)
- Dim Info As New DirectoryInfo(Directory)
+ Dim Info As New DirectoryInfo(ShortenPath(Directory))
If Not Info.Exists Then Return New List(Of FileInfo)
Return Info.EnumerateFiles("*", SearchOption.AllDirectories)
End Function
+ '''
+ ''' 若路径长度大于指定值,则将长路径转换为短路径。
+ '''
+ Public Function ShortenPath(LongPath As String, Optional ShortenThreshold As Integer = 247) As String
+ If LongPath.Length <= ShortenThreshold Then Return LongPath
+ Dim ShortPath As New StringBuilder(260)
+ GetShortPathName(LongPath, ShortPath, 260)
+ Return ShortPath.ToString
+ End Function
+ Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (ByVal lpszLongPath As String, ByVal lpszShortPath As StringBuilder, ByVal cchBuffer As Integer) As Integer
+
#End Region
#Region "文本"
@@ -1350,8 +1389,8 @@ RetryDir:
'''
''' 提取 Exception 的具体描述与堆栈。
'''
- ''' 是否必须显示所有堆栈。通常用于判定堆栈信息。
- Public Function GetExceptionDetail(Ex As Exception, Optional ShowAllTrace As Boolean = False) As String
+ ''' 是否必须显示所有堆栈。通常用于判定堆栈信息。
+ Public Function GetExceptionDetail(Ex As Exception, Optional ShowAllStacks As Boolean = False) As String
If Ex Is Nothing Then Return "无可用错误信息!"
'获取最底层的异常
@@ -1362,19 +1401,20 @@ RetryDir:
'获取各级错误的描述与堆栈信息
Dim DescList As New List(Of String)
- Dim StackList As New List(Of String)
+ Dim IsInner As Boolean = False
Do Until Ex Is Nothing
- DescList.Add(Ex.Message.Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr & vbCr, vbCr).Replace(vbCr, vbCrLf))
+ DescList.Add(If(IsInner, "→ ", "") & Ex.Message.Replace(vbLf, vbCr).Replace(vbCr & vbCr, vbCr).Replace(vbCr, vbCrLf))
If Ex.StackTrace IsNot Nothing Then
- For Each St As String In Ex.StackTrace.Split(vbCrLf)
- If ShowAllTrace OrElse St.ToLower.Contains("pcl") Then StackList.Add(St.Replace(vbCr, String.Empty).Replace(vbLf, String.Empty))
+ For Each Stack As String In Ex.StackTrace.Split(vbCrLf.ToCharArray, StringSplitOptions.RemoveEmptyEntries)
+ If ShowAllStacks OrElse Stack.ContainsF("pcl", True) Then
+ DescList.Add(Stack.Replace(vbCr, String.Empty).Replace(vbLf, String.Empty))
+ End If
Next
End If
+ If Ex.GetType.FullName <> "System.Exception" Then DescList.Add(" 错误类型:" & Ex.GetType.FullName)
Ex = Ex.InnerException
+ IsInner = True
Loop
- DescList = DescList.Distinct.ToList
- Dim Desc As String = Join(DescList, vbCrLf & "→ ")
- Dim Stack As String = If(StackList.Any, vbCrLf & Join(StackList, vbCrLf), "")
'常见错误(记得同时修改下面的)
Dim CommonReason As String = Nothing
@@ -1387,20 +1427,15 @@ RetryDir:
ElseIf TypeOf InnerEx Is Runtime.InteropServices.COMException Then
CommonReason = "由于操作系统或显卡存在问题,导致出现错误。请尝试重启 PCL。"
ElseIf {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
- "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) Desc.Contains(s)) Then
+ "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) DescList.Any(Function(l) l.Contains(s))) Then
CommonReason = "你的网络环境不佳,导致难以连接到服务器。请检查网络,多重试几次,或尝试使用 VPN。"
End If
- '获取错误类型
- Dim TypeDesc As String = If(InnerEx.GetType.FullName = "System.Exception", "", vbCrLf & "错误类型:" & InnerEx.GetType.FullName)
-
'构造输出信息
If CommonReason Is Nothing Then
- Return Desc & Stack & TypeDesc
+ Return DescList.Join(vbCrLf)
Else
- Dim Result As String = CommonReason & vbCrLf & DescList.First & vbCrLf & "————————————" & vbCrLf
- DescList(0) = "详细错误信息:"
- Return Result & Join(DescList, vbCrLf & "→ ") & Stack & TypeDesc
+ Return CommonReason & vbCrLf & vbCrLf & "————————————" & vbCrLf & "详细错误信息:" & vbCrLf & DescList.Join(vbCrLf)
End If
End Function
'''
@@ -1444,7 +1479,7 @@ RetryDir:
Return CommonReason & "详细错误:" & DescList.First
Else
DescList.Reverse() '让最深层错误在最左边
- Return Join(DescList, " → ")
+ Return Join(DescList, " ← ")
End If
End Function
@@ -1722,6 +1757,21 @@ RetryDir:
Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").
Replace("""", """).Replace(vbCrLf, "
")
End Function
+ '''
+ ''' 为字符串进行 Like 关键字转义。
+ '''
+ Public Function EscapeLikePattern(input As String) As String
+ Dim sb As New StringBuilder()
+ For Each c As Char In input
+ Select Case c
+ Case "["c, "]"c, "*"c, "?"c, "#"c
+ sb.Append("["c).Append(c).Append("]"c)
+ Case Else
+ sb.Append(c)
+ End Select
+ Next
+ Return sb.ToString()
+ End Function
'正则
'''
@@ -2134,6 +2184,7 @@ NextElement:
''' 文件名。可以为“notepad”等缩写。
''' 运行参数。
Public Sub ShellOnly(FileName As String, Optional Arguments As String = "")
+ FileName = ShortenPath(FileName)
Using Program As New Process
Program.StartInfo.Arguments = Arguments
Program.StartInfo.FileName = FileName
@@ -2179,7 +2230,7 @@ NextElement:
.CreateNoWindow = True,
.RedirectStandardError = True,
.RedirectStandardOutput = True,
- .WorkingDirectory = If(WorkingDirectory, Path.TrimEnd("\"c))
+ .WorkingDirectory = ShortenPath(If(WorkingDirectory, Path.TrimEnd("\"c)))
}
If WorkingDirectory IsNot Nothing Then
If Info.EnvironmentVariables.ContainsKey("appdata") Then
@@ -2288,6 +2339,27 @@ NextElement:
Return New List(Of T)(list)
End Function
+ '''
+ ''' 尝试从字典中获取某项,如果该项不存在,则返回默认值。
+ '''
+ Public Function GetOrDefault(Of TKey, TValue)(Dict As Dictionary(Of TKey, TValue), Key As TKey, Optional DefaultValue As TValue = Nothing) As TValue
+ If Dict.ContainsKey(Key) Then
+ Return Dict(Key)
+ Else
+ Return DefaultValue
+ End If
+ End Function
+ '''
+ ''' 将某项添加到以列表作为值的字典中。
+ '''
+ Public Sub AddToList(Of TKey, TValue)(Dict As Dictionary(Of TKey, List(Of TValue)), Key As TKey, Value As TValue)
+ If Dict.ContainsKey(Key) Then
+ Dict(Key).Add(Value)
+ Else
+ Dict.Add(Key, New List(Of TValue) From {Value})
+ End If
+ End Sub
+
'''
''' 获取程序启动参数。
'''
@@ -2337,11 +2409,18 @@ NextElement:
End Try
End Sub
'''
- ''' 打开 explorer。注意参数中的路径要尽量加上双引号!
+ ''' 打开 explorer。
+ ''' 若不以 \ 结尾,则将视作文件路径,打开并选中此文件。
'''
- Public Sub OpenExplorer(Argument As String)
+ Public Sub OpenExplorer(Location As String)
Try
- ShellOnly("explorer.exe", Argument)
+ Location = ShortenPath(Location.Replace("/", "\").Trim(" "c, """"c))
+ Log("[System] 正在打开资源管理器:" & Location)
+ If Location.EndsWith("\") Then
+ ShellOnly(Location)
+ Else
+ ShellOnly("explorer", $"/select,""{Location}""")
+ End If
Catch ex As Exception
Log(ex, "打开资源管理器失败,请尝试关闭安全软件(如 360 安全卫士)", LogLevel.Msgbox)
End Try
@@ -2792,7 +2871,7 @@ Retry:
On Error Resume Next
FeedbackInfo()
If ForceOpenLog OrElse (ShowMsgbox AndAlso MyMsgBox("若你在汇报一个 Bug,请点击 打开文件夹 按钮,并上传 Log(1~5).txt 中包含错误信息的文件。" & vbCrLf & "游戏崩溃一般与启动器无关,请不要因为游戏崩溃而提交反馈。", "反馈提交提醒", "打开文件夹", "不需要") = 1) Then
- OpenExplorer("""" & Path & "PCL\""")
+ OpenExplorer(Path & "PCL\Log1.txt")
End If
OpenWebsite("https://github.com/Hex-Dragon/PCL2/issues/")
End Sub
@@ -2868,4 +2947,88 @@ Retry:
#End Region
-End Module
\ No newline at end of file
+End Module
+
+#Region "WPF"
+
+'''
+''' 对数据绑定进行加法运算,使用参数决定加数。
+'''
+Public Class AdditionConverter
+ Implements IValueConverter
+ Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
+ If value Is Nothing Then Return 0
+ Dim before As Double
+ If Not Double.TryParse(value.ToString(), before) Then Return 0
+ Dim scale As Double = 1
+ If parameter IsNot Nothing Then Double.TryParse(parameter.ToString(), scale)
+ Return before + scale
+ End Function
+ Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
+ If value Is Nothing Then Return Binding.DoNothing
+ Dim before As Double
+ If Not Double.TryParse(value.ToString(), before) Then Return Binding.DoNothing
+ Dim scale As Double = 1
+ If parameter IsNot Nothing Then Double.TryParse(parameter.ToString(), scale)
+ If scale = 0 Then Return Binding.DoNothing
+ Return before - scale
+ End Function
+End Class
+
+'''
+''' 对数据绑定进行乘法运算,使用参数决定乘数。
+'''
+Public Class MultiplicationConverter
+ Implements IValueConverter
+ Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
+ If value Is Nothing Then Return 0
+ Dim before As Double
+ If Not Double.TryParse(value.ToString(), before) Then Return 0
+ Dim scale As Double = 1
+ If parameter IsNot Nothing Then Double.TryParse(parameter.ToString(), scale)
+ Return before * scale
+ End Function
+ Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
+ If value Is Nothing Then Return Binding.DoNothing
+ Dim before As Double
+ If Not Double.TryParse(value.ToString(), before) Then Return Binding.DoNothing
+ Dim scale As Double = 1
+ If parameter IsNot Nothing Then Double.TryParse(parameter.ToString(), scale)
+ If scale = 0 Then Return Binding.DoNothing
+ Return before / scale
+ End Function
+End Class
+
+'''
+''' 将取反的 Boolean 绑定到 Visibility。
+'''
+Public Class InverseBooleanToVisibilityConverter
+ Implements IValueConverter
+ Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
+ If value Is Nothing Then Return Visibility.Visible
+ Dim boolValue As Boolean
+ Return If(Boolean.TryParse(value.ToString(), boolValue), If(boolValue, Visibility.Collapsed, Visibility.Visible), Visibility.Visible)
+ End Function
+ Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
+ If value Is Nothing Then Return False
+ Return If(TypeOf value Is Visibility, value <> Visibility.Visible, False)
+ End Function
+End Class
+
+'''
+''' 将 Boolean 取反。
+'''
+Public Class InverseBooleanConverter
+ Implements IValueConverter
+ Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
+ If value Is Nothing Then Return False
+ Dim boolValue As Boolean
+ Return If(Boolean.TryParse(value.ToString(), boolValue), Not boolValue, False)
+ End Function
+ Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
+ If value Is Nothing Then Return False
+ Return If(Boolean.TryParse(value.ToString(), value), Not value, False)
+ End Function
+End Class
+
+#End Region
diff --git a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
index f069882..a408f14 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModLoader.vb
@@ -315,23 +315,26 @@
Sub()
Try
IsForceRestarting = IsForceRestart
- If ModeDebug Then Log("[Loader] 加载线程 " & Name & " (" & Thread.CurrentThread.ManagedThreadId & ") 已" & If(IsForceRestarting, "强制", "") & "启动")
+ If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已{If(IsForceRestarting, "强制", "")}启动")
LoadDelegate(Me)
If IsAborted Then
- Log("[Loader] 加载线程 " & Name & " (" & Thread.CurrentThread.ManagedThreadId & ") 已中断但线程正常运行至结束,输出被弃用(最新线程:" & If(LastRunningThread Is Nothing, -1, LastRunningThread.ManagedThreadId) & ")", LogLevel.Developer)
+ Log($"[Loader] 加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已中断但线程正常运行至结束,输出被弃用(最新线程:{If(LastRunningThread Is Nothing, -1, LastRunningThread.ManagedThreadId)})", LogLevel.Developer)
Else
RaisePreviewFinish()
State = LoadState.Finished
LastFinishedTime = GetTimeTick() '未中断,本次输出有效
End If
+ Catch ex As CancelledException
+ If ModeDebug Then Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已触发取消中断,已完成 {Math.Round(Progress * 100)}%")
+ If Not IsAborted Then State = LoadState.Aborted
Catch ex As ThreadInterruptedException
- If ModeDebug Then Log(ex, "加载线程 " & Name & " (" & Thread.CurrentThread.ManagedThreadId & ") 已触发线程中断")
+ If ModeDebug Then Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 已触发线程中断,已完成 {Math.Round(Progress * 100)}%")
'如果线程是因为判断到 IsAborted 而提前中止,则代表已有新线程被重启,此时不应当改为 Aborted
'如果线程是在没有 IsAborted 时手动引发了 ThreadInterruptedException,则代表没有重启线程,这通常代表用户手动取消,应当改为 Aborted
If Not IsAborted Then State = LoadState.Aborted
Catch ex As Exception
If IsAborted Then Exit Sub
- Log(ex, "加载线程 " & Name & " (" & Thread.CurrentThread.ManagedThreadId & ") 发生运行时错误", LogLevel.Developer)
+ Log(ex, $"加载线程 {Name} ({Thread.CurrentThread.ManagedThreadId}) 出错,已完成 {Math.Round(Progress * 100)}%", LogLevel.Developer)
[Error] = ex
State = LoadState.Failed
End Try
@@ -347,7 +350,7 @@
End Sub
Private Sub TriggerThreadAbort()
If LastRunningThread Is Nothing Then Exit Sub
- If ModeDebug Then Log("[Loader] 加载线程 " & Name & " (" & LastRunningThread.ManagedThreadId & ") 已中断")
+ If ModeDebug Then Log($"[Loader] 加载线程 {Name} ({LastRunningThread.ManagedThreadId}) 已中断")
If LastRunningThread.IsAlive Then LastRunningThread.Interrupt()
LastRunningThread = Nothing
End Sub
diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
index f482a8a..030bf5f 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
@@ -220,12 +220,12 @@ RequestFinished:
''' 以多线程下载网页文件的方式获取网页源代码。
'''
''' 网页的 Url。
- Public Function NetGetCodeByDownload(Url As String, Optional Timeout As Integer = 45000, Optional IsJson As Boolean = False, Optional UseBrowserUserAgent As Boolean = False) As String
+ Public Function NetGetCodeByLoader(Url As String, Optional Timeout As Integer = 45000, Optional IsJson As Boolean = False, Optional UseBrowserUserAgent As Boolean = False) As String
Dim Temp As String = PathTemp & "Cache\Code\" & Url.GetHashCode() & "_" & GetUuid()
Dim NewTask As New LoaderDownload("源码获取 " & GetUuid() & "#", New List(Of NetFile) From {New NetFile({Url}, Temp, New FileChecker With {.IsJson = IsJson}, UseBrowserUserAgent)})
Try
NewTask.WaitForExitTime(Timeout, TimeoutMessage:="连接服务器超时(" & Url & ")")
- NetGetCodeByDownload = ReadFile(Temp)
+ NetGetCodeByLoader = ReadFile(Temp)
File.Delete(Temp)
Finally
NewTask.Abort()
@@ -235,12 +235,12 @@ RequestFinished:
''' 以多线程下载网页文件的方式获取网页源代码。
'''
''' 网页的 Url 列表。
- Public Function NetGetCodeByDownload(Urls As IEnumerable(Of String), Optional Timeout As Integer = 45000, Optional IsJson As Boolean = False, Optional UseBrowserUserAgent As Boolean = False) As String
+ Public Function NetGetCodeByLoader(Urls As IEnumerable(Of String), Optional Timeout As Integer = 45000, Optional IsJson As Boolean = False, Optional UseBrowserUserAgent As Boolean = False) As String
Dim Temp As String = PathTemp & "Cache\Code\" & Urls.First.GetHashCode() & "_" & GetUuid()
Dim NewTask As New LoaderDownload("源码获取 " & GetUuid() & "#", New List(Of NetFile) From {New NetFile(Urls, Temp, New FileChecker With {.IsJson = IsJson}, UseBrowserUserAgent)})
Try
NewTask.WaitForExitTime(Timeout, TimeoutMessage:="连接服务器超时(第一下载源:" & Urls.First & ")")
- NetGetCodeByDownload = ReadFile(Temp)
+ NetGetCodeByLoader = ReadFile(Temp)
File.Delete(Temp)
Finally
NewTask.Abort()
@@ -248,13 +248,12 @@ RequestFinished:
End Function
'''
- ''' 从网络中直接下载文件。这不能下载 CDN 中的文件。
+ ''' 使用 WebClient 从网络中下载文件。这不能下载 CDN 中的文件。
'''
''' 网络 Url。
''' 下载的本地地址。
- Public Sub NetDownload(Url As String, LocalFile As String, Optional UseBrowserUserAgent As Boolean = False)
+ Public Sub NetDownloadByClient(Url As String, LocalFile As String, Optional UseBrowserUserAgent As Boolean = False)
Log("[Net] 直接下载文件:" & Url)
-
'初始化
Try
'建立目录
@@ -262,9 +261,8 @@ RequestFinished:
'尝试删除原文件
File.Delete(LocalFile)
Catch ex As Exception
- Throw New WebException("预处理下载文件路径失败(" & LocalFile & ")。", ex)
+ Throw New WebException($"预处理下载文件路径失败({LocalFile})", ex)
End Try
-
'下载
Using Client As New WebClient
Try
@@ -272,10 +270,41 @@ RequestFinished:
Client.DownloadFile(Url, LocalFile)
Catch ex As Exception
File.Delete(LocalFile)
- Throw New WebException("直接下载文件失败(" & Url & ")。", ex)
+ Throw New WebException($"直接下载文件失败({Url})", ex)
End Try
End Using
+ End Sub
+ '''
+ ''' 简单的多线程下载文件。可以下载 CDN 中的文件。
+ '''
+ ''' 文件的 Url。
+ ''' 下载的本地地址。
+ Public Sub NetDownloadByLoader(Url As String, LocalFile As String, Optional LoaderToSyncProgress As LoaderBase = Nothing, Optional Check As FileChecker = Nothing, Optional UseBrowserUserAgent As Boolean = False)
+ Dim NewTask As New LoaderDownload("文件下载 " & GetUuid() & "#", New List(Of NetFile) From {New NetFile({Url}, LocalFile, Check, UseBrowserUserAgent)})
+ Try
+ NewTask.WaitForExit(LoaderToSyncProgress:=LoaderToSyncProgress)
+ Catch ex As Exception
+ Throw New WebException($"多线程直接下载文件失败({Url})", ex)
+ Finally
+ NewTask.Abort()
+ End Try
+ End Sub
+
+ '''
+ ''' 简单的多线程下载文件。可以下载 CDN 中的文件。
+ '''
+ ''' 文件的 Url 列表。
+ ''' 下载的本地地址。
+ Public Sub NetDownloadByLoader(Urls As IEnumerable(Of String), LocalFile As String, Optional LoaderToSyncProgress As LoaderBase = Nothing, Optional Check As FileChecker = Nothing, Optional UseBrowserUserAgent As Boolean = False)
+ Dim NewTask As New LoaderDownload("文件下载 " & GetUuid() & "#", New List(Of NetFile) From {New NetFile(Urls, LocalFile, Check, UseBrowserUserAgent)})
+ Try
+ NewTask.WaitForExit(LoaderToSyncProgress:=LoaderToSyncProgress)
+ Catch ex As Exception
+ Throw New WebException($"多线程直接下载文件失败(第一下载源:" & Urls.First() & ")", ex)
+ Finally
+ NewTask.Abort()
+ End Try
End Sub
'''
@@ -1885,7 +1914,7 @@ Retry:
For Each File As NetFile In WaitingFiles
If NetTaskThreadCount >= NetTaskThreadLimit Then Continue While '最大线程数检查
Dim NewThread = File.TryBeginThread()
- If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(100) '减少 BMCLAPI 请求频率,避免 Too Many Requests
+ If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(30) '减少 BMCLAPI 请求频率(目前每分钟限制 4000 次)
Next
'为进行中的文件追加线程
If Speed >= NetTaskSpeedLimitLow Then Continue While '下载速度足够,无需新增
@@ -1905,7 +1934,7 @@ Retry:
'新增线程
If PreparingCount > DownloadingCount Then Continue For '准备中的线程已多于下载中的线程,不再新增
Dim NewThread = File.TryBeginThread()
- If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(100) '减少 BMCLAPI 请求频率,避免 Too Many Requests
+ If NewThread IsNot Nothing AndAlso NewThread.Source.Url.Contains("bmclapi") Then Thread.Sleep(30) '减少 BMCLAPI 请求频率(目前每分钟限制 4000 次)
Next
End While
Catch ex As Exception
diff --git a/Plain Craft Launcher 2/Modules/Base/ModValidate.vb b/Plain Craft Launcher 2/Modules/Base/ModValidate.vb
index fbbc2f8..0394060 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModValidate.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModValidate.vb
@@ -262,7 +262,6 @@ Public Class ValidateFileName
Public Property IgnoreCase As Boolean = True
Public Property ParentFolder As String = Nothing
Public Property RequireParentFolderExists = True
- Public Property AllowNull As Boolean = False
Public Sub New()
End Sub
Public Sub New(Name As String, Optional UseMinecraftCharCheck As Boolean = True, Optional IgnoreCase As Boolean = True)
@@ -272,16 +271,14 @@ Public Class ValidateFileName
End Sub
Public Overrides Function Validate(Str As String) As String
Try
- If Not AllowNull Then
- '检查是否为空
- Dim NullCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
- If Not NullCheck = "" Then Return NullCheck
- End If
+ '检查是否为空
+ Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
+ If Not LengthCheck = "" Then Return LengthCheck
'检查空格
If Str.StartsWithF(" ") Then Return "文件名不能以空格开头!"
If Str.EndsWithF(" ") Then Return "文件名不能以空格结尾!"
'检查长度
- Dim LengthCheck = New ValidateLength(If(AllowNull, 0, 1), 253).Validate(Str & If(ParentFolder, ""))
+ LengthCheck = New ValidateLength(1, 253).Validate(Str & If(ParentFolder, ""))
If Not LengthCheck = "" Then Return LengthCheck
'检查尾部小数点
If Str.EndsWithF(".") Then Return "文件名不能以小数点结尾!"
@@ -318,7 +315,6 @@ End Class
Public Class ValidateFolderPath
Inherits Validate
Public Property UseMinecraftCharCheck As Boolean = True
- Public Property AllowNull As Boolean = False
Public Sub New()
End Sub
Public Sub New(UseMinecraftCharCheck As Boolean)
@@ -330,9 +326,9 @@ Public Class ValidateFolderPath
If Not Str.TrimEnd("\").EndsWithF(":") Then Str = Str.TrimEnd("\")
'检查是否为空
Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
- If Not AllowNull AndAlso Not LengthCheck = "" Then Return LengthCheck
+ If Not LengthCheck = "" Then Return LengthCheck
'检查长度
- LengthCheck = New ValidateLength(If(AllowNull, 0, 1), 254).Validate(Str)
+ LengthCheck = New ValidateLength(1, 254).Validate(Str)
If Not LengthCheck = "" Then Return LengthCheck
'检查开头
If Str.StartsWithF("\\Mac\") Then GoTo Fin
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
index 04d268d..7edb2c9 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
@@ -360,7 +360,7 @@
'Tags & ModLoaders
Tags = New List(Of String)
ModLoaders = New List(Of CompModLoaderType)
- If Data?("loaders") IsNot Nothing Then
+ If Data.ContainsKey("loaders") Then
For Each Category In Data("loaders").Select(Function(t) t.ToString)
Select Case Category
Case "forge" : ModLoaders.Add(CompModLoaderType.Forge)
@@ -402,7 +402,7 @@
Case "technology" : Tags.Add("科技")
Case "magic" : Tags.Add("魔法")
Case "adventure" : Tags.Add("冒险")
- Case "kitchen-sink" : Tags.Add("大杂烩")
+ Case "kitchen-sink" : Tags.Add("水槽包/大杂烩")
Case "lightweight" : Tags.Add("轻量")
'FUTURE: Res
End Select
@@ -1274,10 +1274,7 @@ Retry:
Dim Url = Data("downloadUrl").ToString
If Url = "" Then Url = $"https://media.forgecdn.net/files/{CInt(Id.ToString.Substring(0, 4))}/{CInt(Id.ToString.Substring(4))}/{FileName}"
Url = Url.Replace(FileName, Net.WebUtility.UrlEncode(FileName)) '对文件名进行编码
- DownloadUrls = (New List(Of String) From {Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge", "://media"),
- Url.Replace("-service.overwolf.wtf", ".forgecdn.net"),
- Url.Replace("://edge", "://media"),
- Url}).Distinct.ToList '对脑残 CurseForge 的下载地址进行多种修正
+ DownloadUrls = HandleCurseForgeDownloadUrls(Url) '对脑残 CurseForge 的下载地址进行多种修正
DownloadUrls.AddRange(DownloadUrls.Select(Function(u) DlSourceModGet(u)).ToList) '添加镜像源,这个写法是为了让镜像源排在后面
DownloadUrls = DownloadUrls.Distinct.ToList '最终去重
'Dependencies
@@ -1352,6 +1349,19 @@ Retry:
End If
End If
End Sub
+
+ '''
+ ''' 重新整理 CurseForge 的下载地址。
+ '''
+ Public Shared Function HandleCurseForgeDownloadUrls(Url As String) As List(Of String)
+ Return {
+ Url.Replace("-service.overwolf.wtf", ".forgecdn.net").Replace("://edge", "://media"),
+ Url.Replace("-service.overwolf.wtf", ".forgecdn.net"),
+ Url.Replace("://edge", "://media"),
+ Url
+ }.Distinct.ToList
+ End Function
+
'''
''' 将当前实例转为可用于保存缓存的 Json。
'''
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
index a3b3c61..0093691 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
@@ -1,24 +1,10 @@
Public Class CrashAnalyzer
- Private Shared IsAnalyzeCacheCleared As Boolean = False
- Private Shared IsAnalyzeCacheClearedLock As New Object
'构造函数
Private TempFolder As String
Public Sub New(UUID As Integer)
- '清理缓存
- SyncLock IsAnalyzeCacheClearedLock
- If Not IsAnalyzeCacheCleared Then
- Try
- DeleteDirectory(PathTemp & "CrashAnalyzer\")
- Catch ex As Exception
- Log(ex, "清理崩溃分析缓存失败")
- End Try
- IsAnalyzeCacheCleared = True
- End If
- End SyncLock
'构建文件结构
- TempFolder = PathTemp & "CrashAnalyzer\" & UUID & RandomInteger(0, 99999999) & "\"
- DeleteDirectory(TempFolder)
+ TempFolder = RequestTaskTempFolder()
Directory.CreateDirectory(TempFolder & "Temp\")
Directory.CreateDirectory(TempFolder & "Report\")
Log("[Crash] 崩溃分析暂存文件夹:" & TempFolder)
@@ -873,7 +859,7 @@ NextStack:
Sub()
'弹窗选择:查看日志
If File.Exists(DirectFile.Value.Key) Then
- ShellOnly("notepad", DirectFile.Value.Key)
+ ShellOnly(DirectFile.Value.Key)
Else
Dim FilePath As String = PathTemp & "Crash.txt"
WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf))
@@ -885,7 +871,7 @@ NextStack:
Dim FileAddress As String = Nothing
Try
'获取文件路径
- RunInUiWait(Sub() FileAddress = SelectAs("选择保存位置", "错误报告-" & Date.Now.ToString("G").Replace("/", "-").Replace(":", ".").Replace(" ", "_") & ".zip", "Minecraft 错误报告(*.zip)|*.zip"))
+ RunInUiWait(Sub() FileAddress = SelectSaveFile("选择保存位置", "错误报告-" & Date.Now.ToString("G").Replace("/", "-").Replace(":", ".").Replace(" ", "_") & ".zip", "Minecraft 错误报告(*.zip)|*.zip"))
If String.IsNullOrEmpty(FileAddress) Then Exit Sub
Directory.CreateDirectory(GetPathFromFullPath(FileAddress))
If File.Exists(FileAddress) Then File.Delete(FileAddress)
@@ -923,7 +909,7 @@ NextStack:
Log(ex, "导出错误报告失败", LogLevel.Feedback)
Exit Sub
End Try
- OpenExplorer("/select,""" & FileAddress & """")
+ OpenExplorer(FileAddress)
End Select
End Sub
'''
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
index 2e23da0..3ba0ce3 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
@@ -622,7 +622,7 @@
Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
Dim Result As String
Try
- Result = NetGetCodeByDownload("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
+ Result = NetGetCodeByLoader("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" &
Loader.Input.Replace("-", "_") & '兼容 Forge 1.7.10-pre4,#4057
".html", UseBrowserUserAgent:=True)
Catch ex As Exception
@@ -829,8 +829,8 @@
Public DlNeoForgeListOfficialLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Official", AddressOf DlNeoForgeListOfficialMain)
Private Sub DlNeoForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
'获取版本列表 JSON
- Dim ResultLatest As String = NetGetCodeByDownload("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
- Dim ResultLegacy As String = NetGetCodeByDownload("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
+ Dim ResultLatest As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
+ Dim ResultLegacy As String = NetGetCodeByLoader("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
'解析
Try
@@ -847,8 +847,8 @@
Public DlNeoForgeListBmclapiLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Bmclapi", AddressOf DlNeoForgeListBmclapiMain)
Public Sub DlNeoForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
'获取版本列表 JSON
- Dim ResultLatest As String = NetGetCodeByDownload("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
- Dim ResultLegacy As String = NetGetCodeByDownload("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
+ Dim ResultLatest As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True, IsJson:=True)
+ Dim ResultLegacy As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/neoforge/meta/api/maven/details/releases/net/neoforged/forge", UseBrowserUserAgent:=True, IsJson:=True)
If ResultLatest.Length < 100 OrElse ResultLegacy.Length < 100 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
'解析
Try
@@ -1095,12 +1095,12 @@
'''
''' 对可能涉及 Mod 镜像源的请求进行处理,返回字符串或 JObject。
- ''' 调用 NetGetCodeByRequest。
+ ''' 调用 NetGetCodeByRequest,会进行重试。
'''
Public Function DlModRequest(Url As String, Optional IsJson As Boolean = False) As Object
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
- Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
+ Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
'Dim McimUrl As String = DlSourceModGet(Url)
'If McimUrl <> Url Then
' Select Case Setup.Get("ToolDownloadMod")
@@ -1132,12 +1132,12 @@
'''
''' 对可能涉及 Mod 镜像源的请求进行处理。
- ''' 调用 NetRequest。
+ ''' 调用 NetRequest,会进行重试。
'''
Public Function DlModRequest(Url As String, Method As String, Data As String, ContentType As String) As String
Dim Urls As New List(Of KeyValuePair(Of String, Integer))
Urls.Add(New KeyValuePair(Of String, Integer)(Url, 5))
- Urls.Add(New KeyValuePair(Of String, Integer)(Url, 15))
+ Urls.Add(New KeyValuePair(Of String, Integer)(Url, 20))
'Dim McimUrl As String = DlSourceModGet(Url)
'If McimUrl <> Url Then
' Select Case Setup.Get("ToolDownloadMod")
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
index d11075e..3d54dfc 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
@@ -653,7 +653,7 @@ Wait:
Private Sub JavaSearchFolder(OriginalPath As String, ByRef Results As Dictionary(Of String, Boolean), Source As Boolean, Optional IsFullSearch As Boolean = False)
Try
Log("[Java] 开始" & If(IsFullSearch, "完全", "部分") & "遍历查找:" & OriginalPath)
- JavaSearchFolder(New DirectoryInfo(OriginalPath), Results, Source, IsFullSearch)
+ JavaSearchFolder(New DirectoryInfo(ShortenPath(OriginalPath)), Results, Source, IsFullSearch)
Catch ex As UnauthorizedAccessException
Log("[Java] 遍历查找 Java 时遭遇无权限的文件夹:" & OriginalPath)
Catch ex As Exception
@@ -741,7 +741,7 @@ Wait:
Private LastJavaBaseDir As String = Nothing '用于在下载中断或失败时删除未完成下载的 Java 文件夹,防止残留只下了一半但 -version 能跑的 Java
Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
Log("[Java] 开始获取 Java 下载信息")
- Dim IndexFileStr As String = NetGetCodeByDownload(
+ Dim IndexFileStr As String = NetGetCodeByLoader(
{"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
IsJson:=True)
@@ -753,7 +753,7 @@ Wait:
Dim Address As String = TargetEntry.Value("manifest")("url")
Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
'获取文件列表
- Dim ListFileStr As String = NetGetCodeByDownload({Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"), Address}, IsJson:=True)
+ Dim ListFileStr As String = NetGetCodeByLoader({Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"), Address}, IsJson:=True)
LastJavaBaseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
Dim Results As New List(Of NetFile)
For Each File As JProperty In CType(GetJson(ListFileStr), JObject)("files")
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
index c111ec2..01b1bab 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
@@ -574,7 +574,7 @@ SkipLogin:
End Sub
Private Sub McLoginServerStart(Data As LoaderTask(Of McLoginServer, McLoginResult))
Dim Input As McLoginServer = Data.Input
- Dim NeedRefresh As Boolean = False
+ Dim NeedRefresh As Boolean = False, WasRefreshed As Boolean = False
Dim LogUsername As String = Input.UserName
If LogUsername.Contains("@") AndAlso Setup.Get("UiLauncherEmail") Then
LogUsername = AccountFilter(LogUsername)
@@ -595,7 +595,7 @@ SkipLogin:
McLoginRequestValidate(Data)
GoTo LoginFinish
Catch ex As Exception
- Dim AllMessage = GetExceptionSummary(ex)
+ Dim AllMessage = GetExceptionDetail(ex)
McLaunchLog("验证登录失败:" & AllMessage)
If (AllMessage.Contains("超时") OrElse AllMessage.Contains("imeout")) AndAlso Not AllMessage.Contains("403") Then
McLaunchLog("已触发超时登录失败")
@@ -610,7 +610,8 @@ Refresh:
McLoginRequestRefresh(Data, NeedRefresh)
GoTo LoginFinish
Catch ex As Exception
- McLaunchLog("刷新登录失败:" & GetExceptionSummary(ex))
+ McLaunchLog("刷新登录失败:" & GetExceptionDetail(ex))
+ If WasRefreshed Then Throw New Exception("二轮刷新登录失败", ex)
End Try
Data.Progress = If(NeedRefresh, 0.85, 0.45)
End If
@@ -619,10 +620,12 @@ Refresh:
If Data.IsAborted Then Throw New ThreadInterruptedException
NeedRefresh = McLoginRequestLogin(Data)
Catch ex As Exception
- McLaunchLog("登录失败:" & GetExceptionSummary(ex))
+ McLaunchLog("登录失败:" & GetExceptionDetail(ex))
Throw
End Try
If NeedRefresh Then
+ McLaunchLog("重新进行刷新登录")
+ WasRefreshed = True
Data.Progress = 0.65
GoTo Refresh
End If
@@ -699,19 +702,17 @@ LoginFinish:
End Sub
Private Sub McLoginRequestRefresh(ByRef Data As LoaderTask(Of McLoginServer, McLoginResult), RequestUser As Boolean)
McLaunchLog("刷新登录开始(Refresh, " & Data.Input.Token & ")")
-
- Dim RefreshInfo As JObject = New JObject
- Dim SelectProfile As JObject = New JObject()
- SelectProfile.Add(New JProperty("name", Setup.Get("Cache" & Data.Input.Token & "Name")))
- SelectProfile.Add(New JProperty("id", Setup.Get("Cache" & Data.Input.Token & "Uuid")))
- RefreshInfo.Add("selectedProfile", SelectProfile)
- RefreshInfo.Add(New JProperty("accessToken", Setup.Get("Cache" & Data.Input.Token & "Access")))
- RefreshInfo.Add(New JProperty("requestUser", True))
-
Dim LoginJson As JObject = GetJson(NetRequestRetry(
Url:=Data.Input.BaseUrl & "/refresh",
Method:="POST",
- Data:=RefreshInfo.ToString(0),
+ Data:=New JObject(
+ New JProperty("selectedProfile", New JObject(
+ New JProperty("name", Setup.Get($"Cache{Data.Input.Token}Name")),
+ New JProperty("id", Setup.Get($"Cache{Data.Input.Token}Uuid"))
+ )),
+ New JProperty("accessToken", Setup.Get($"Cache{Data.Input.Token}Access")),
+ New JProperty("requestUser", True)
+ ).ToString(Newtonsoft.Json.Formatting.None),
Headers:=New Dictionary(Of String, String) From {{"Accept-Language", "zh-CN"}},
ContentType:="application/json; charset=utf-8"))
'将登录结果输出
@@ -768,17 +769,18 @@ LoginFinish:
'缓存无效,要求玩家选择
If SelectedName Is Nothing Then
McLaunchLog("要求玩家选择角色")
- RunInUiWait(Sub()
- Dim SelectionControl As New List(Of IMyRadio)
- Dim SelectionJson As New List(Of JToken)
- For Each Profile In LoginJson("availableProfiles")
- SelectionControl.Add(New MyRadioBox With {.Text = Profile("name").ToString})
- SelectionJson.Add(Profile)
- Next
- Dim SelectedIndex As Integer = MyMsgBoxSelect(SelectionControl, "选择使用的角色")
- SelectedName = SelectionJson(SelectedIndex)("name").ToString
- SelectedId = SelectionJson(SelectedIndex)("id").ToString
- End Sub)
+ RunInUiWait(
+ Sub()
+ Dim SelectionControl As New List(Of IMyRadio)
+ Dim SelectionJson As New List(Of JToken)
+ For Each Profile In LoginJson("availableProfiles")
+ SelectionControl.Add(New MyRadioBox With {.Text = Profile("name").ToString})
+ SelectionJson.Add(Profile)
+ Next
+ Dim SelectedIndex As Integer = MyMsgBoxSelect(SelectionControl, "选择使用的角色")
+ SelectedName = SelectionJson(SelectedIndex)("name").ToString
+ SelectedId = SelectionJson(SelectedIndex)("id").ToString
+ End Sub)
McLaunchLog("玩家选择的角色:" & SelectedName)
End If
Else
@@ -1072,7 +1074,8 @@ Retry:
Case 3
'使用正版用户名
Try
- If SkinName <> "" AndAlso McVersionCurrent.Version.McCodeMain < 20 Then '1.20+ 或快照版不能使用该项(#3746)
+ If SkinName <> "" AndAlso
+ McVersionCurrent IsNot Nothing AndAlso McVersionCurrent.Version.McCodeMain < 20 Then '1.20+ 或快照版不能使用该项(#3746)
Log("[Skin] 由于离线皮肤设置,使用正版 UUID:" & SkinName)
Uuid = McLoginMojangUuid(SkinName, False)
End If
@@ -1629,16 +1632,16 @@ NextVersion:
'基础参数
GameArguments.Add("${classpath_separator}", ";")
- GameArguments.Add("${natives_directory}", GetNativesFolder())
- GameArguments.Add("${library_directory}", PathMcFolder & "libraries")
- GameArguments.Add("${libraries_directory}", PathMcFolder & "libraries")
+ GameArguments.Add("${natives_directory}", ShortenPath(GetNativesFolder()))
+ GameArguments.Add("${library_directory}", ShortenPath(PathMcFolder & "libraries"))
+ GameArguments.Add("${libraries_directory}", ShortenPath(PathMcFolder & "libraries"))
GameArguments.Add("${launcher_name}", "PCL")
GameArguments.Add("${launcher_version}", VersionCode)
GameArguments.Add("${version_name}", Version.Name)
Dim ArgumentInfo As String = Setup.Get("VersionArgumentInfo", Version:=McVersionCurrent)
GameArguments.Add("${version_type}", If(ArgumentInfo = "", Setup.Get("LaunchArgumentInfo"), ArgumentInfo))
- GameArguments.Add("${game_directory}", Left(McVersionCurrent.PathIndie, McVersionCurrent.PathIndie.Count - 1))
- GameArguments.Add("${assets_root}", PathMcFolder & "assets")
+ GameArguments.Add("${game_directory}", ShortenPath(Left(McVersionCurrent.PathIndie, McVersionCurrent.PathIndie.Count - 1)))
+ GameArguments.Add("${assets_root}", ShortenPath(PathMcFolder & "assets"))
GameArguments.Add("${user_properties}", "{}")
GameArguments.Add("${auth_player_name}", McLoginLoader.Output.Name)
GameArguments.Add("${auth_uuid}", McLoginLoader.Output.Uuid)
@@ -1672,7 +1675,7 @@ NextVersion:
GameArguments.Add("${resolution_height}", Math.Round(GameSize.Height))
'Assets 相关参数
- GameArguments.Add("${game_assets}", PathMcFolder & "assets\virtual\legacy") '1.5.2 的 pre-1.6 资源索引应与 legacy 合并
+ GameArguments.Add("${game_assets}", ShortenPath(PathMcFolder & "assets\virtual\legacy")) '1.5.2 的 pre-1.6 资源索引应与 legacy 合并
GameArguments.Add("${assets_index_name}", McAssetsGetIndexName(Version))
'支持库参数
@@ -1689,7 +1692,7 @@ NextVersion:
End If
Next
If OptiFineCp IsNot Nothing Then CpStrings.Insert(CpStrings.Count - 2, OptiFineCp)
- GameArguments.Add("${classpath}", Join(CpStrings, ";"))
+ GameArguments.Add("${classpath}", Join(CpStrings.Select(Function(c) ShortenPath(c)), ";"))
Return GameArguments
End Function
@@ -2050,8 +2053,8 @@ IgnoreCustomSkin:
"@echo off" & vbCrLf &
"title 启动 - " & McVersionCurrent.Name & vbCrLf &
"echo 游戏正在启动,请稍候。" & vbCrLf &
- "set APPDATA=""" & McVersionCurrent.PathIndie & """" & vbCrLf &
- "cd /D """ & McVersionCurrent.PathIndie & """" & vbCrLf &
+ "set APPDATA=""" & ShortenPath(McVersionCurrent.PathIndie) & """" & vbCrLf &
+ "cd /D """ & ShortenPath(McVersionCurrent.PathIndie) & """" & vbCrLf &
CustomCommandGlobal & vbCrLf &
CustomCommandVersion & vbCrLf &
"""" & McLaunchJavaSelected.PathJava & """ " & McLaunchArgument & vbCrLf &
@@ -2062,7 +2065,7 @@ IgnoreCustomSkin:
If CurrentLaunchOptions.SaveBatch IsNot Nothing Then
McLaunchLog("导出启动脚本完成,强制结束启动过程")
AbortHint = "导出启动脚本成功!"
- OpenExplorer("/select,""" & CurrentLaunchOptions.SaveBatch & """")
+ OpenExplorer(CurrentLaunchOptions.SaveBatch)
Loader.Parent.Abort()
Exit Sub '导出脚本完成
End If
@@ -2078,7 +2081,7 @@ IgnoreCustomSkin:
Try
CustomProcess.StartInfo.FileName = "cmd.exe"
CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandGlobal & """"
- CustomProcess.StartInfo.WorkingDirectory = PathMcFolder
+ CustomProcess.StartInfo.WorkingDirectory = ShortenPath(PathMcFolder)
CustomProcess.StartInfo.UseShellExecute = False
CustomProcess.StartInfo.CreateNoWindow = True
CustomProcess.Start()
@@ -2102,7 +2105,7 @@ IgnoreCustomSkin:
Try
CustomProcess.StartInfo.FileName = "cmd.exe"
CustomProcess.StartInfo.Arguments = "/c """ & CustomCommandVersion & """"
- CustomProcess.StartInfo.WorkingDirectory = PathMcFolder
+ CustomProcess.StartInfo.WorkingDirectory = ShortenPath(PathMcFolder)
CustomProcess.StartInfo.UseShellExecute = False
CustomProcess.StartInfo.CreateNoWindow = True
CustomProcess.Start()
@@ -2130,18 +2133,18 @@ IgnoreCustomSkin:
'设置环境变量
If StartInfo.EnvironmentVariables.ContainsKey("appdata") Then
- StartInfo.EnvironmentVariables("appdata") = PathMcFolder
+ StartInfo.EnvironmentVariables("appdata") = ShortenPath(PathMcFolder)
Else
- StartInfo.EnvironmentVariables.Add("appdata", PathMcFolder)
+ StartInfo.EnvironmentVariables.Add("appdata", ShortenPath(PathMcFolder))
End If
Dim Paths As New List(Of String)(StartInfo.EnvironmentVariables("Path").Split(";"))
- Paths.Add(McLaunchJavaSelected.PathFolder)
+ Paths.Add(ShortenPath(McLaunchJavaSelected.PathFolder))
StartInfo.EnvironmentVariables("Path") = Join(Paths.Distinct.ToList, ";")
'设置其他参数
StartInfo.StandardErrorEncoding = Encoding.UTF8
StartInfo.StandardOutputEncoding = Encoding.UTF8
- StartInfo.WorkingDirectory = McVersionCurrent.PathIndie
+ StartInfo.WorkingDirectory = ShortenPath(McVersionCurrent.PathIndie)
StartInfo.UseShellExecute = False
StartInfo.RedirectStandardOutput = True
StartInfo.RedirectStandardError = True
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
index 7a177e1..2b0e976 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
@@ -73,7 +73,8 @@ Public Module ModMinecraft
End If
Dim Name As String = Folder.Split(">")(0)
Dim Path As String = Folder.Split(">")(1)
- If CheckPermission(Path) Then
+ Try
+ CheckPermissionWithException(Path)
'若已有该文件夹,则直接重命名;没有则添加
Dim Renamed As Boolean = False
For Each OriginalFolder As McFolder In CacheMcFolderList
@@ -84,9 +85,10 @@ Public Module ModMinecraft
End If
Next
If Not Renamed Then CacheMcFolderList.Add(New McFolder With {.Name = Name, .Path = Path, .Type = McFolderType.Custom})
- Else
- Hint("无法访问 Minecraft 文件夹:" & Path, HintType.Critical)
- End If
+ Catch ex As Exception
+ MyMsgBox("失效的 Minecraft 文件夹:" & vbCrLf & Path & vbCrLf & vbCrLf & GetExceptionSummary(ex), "Minecraft 文件夹失效", IsWarn:=True)
+ Log(ex, $"无法访问 Minecraft 文件夹 {Path}")
+ End Try
Next
'将自定义文件夹情况同步到设置
@@ -482,17 +484,27 @@ VersionSearchFinish:
Return TrimedJson.StartsWithF("{") AndAlso TrimedJson.EndsWithF("}")
End Function
If _JsonText Is Nothing Then
- If Not File.Exists(Path & Name & ".json") Then Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
- _JsonText = ReadFile(Path & Name & ".json")
+ Dim JsonPath As String = Path & Name & ".json"
+ If Not File.Exists(JsonPath) Then
+ '如果文件夹下只有一个 JSON 文件,则将其作为版本 JSON
+ Dim JsonFiles As String() = Directory.GetFiles(Path, "*.json")
+ If JsonFiles.Count = 1 Then
+ JsonPath = JsonFiles(0)
+ Log("[Minecraft] 未找到同名版本 JSON,自动换用 " & JsonPath, LogLevel.Debug)
+ Else
+ Throw New Exception($"未找到版本 JSON 文件:{Path}{Name}.json")
+ End If
+ End If
+ _JsonText = ReadFile(JsonPath)
'如果 ReadFile 失败会返回空字符串;这可能是由于文件被临时占用,故延时后重试
If Not FastJsonCheck(_JsonText) Then
If RunInUi() Then
Log("[Minecraft] 版本 JSON 文件为空或有误,由于代码在主线程运行,将不再进行重试", LogLevel.Debug)
GetJson(_JsonText) '触发异常
Else
- Log("[Minecraft] 版本 JSON 文件为空或有误,将在 2s 后重试读取(" & Path & Name & ".json)", LogLevel.Debug)
+ Log($"[Minecraft] 版本 JSON 文件为空或有误,将在 2s 后重试读取({JsonPath})", LogLevel.Debug)
Thread.Sleep(2000)
- _JsonText = ReadFile(Path & Name & ".json")
+ _JsonText = ReadFile(JsonPath)
If Not FastJsonCheck(_JsonText) Then GetJson(_JsonText) '触发异常
End If
End If
@@ -779,37 +791,7 @@ ExitDataLoad:
End If
'确定版本描述
Dim CustomInfo As String = ReadIni(Path & "PCL\Setup.ini", "CustomInfo")
- If CustomInfo = "" Then
- Select Case State
- Case McVersionState.Snapshot
- If Version.McName.ContainsF("pre", True) Then
- Info = "预发布版 " & Version.McName
- ElseIf Version.McName.ContainsF("rc", True) Then
- Info = "发布候选 " & Version.McName
- ElseIf Version.McName.Contains("experimental") OrElse Version.McName = "pending" Then
- Info = "实验性快照"
- Else
- Info = "快照 " & Version.McName
- End If
- Case McVersionState.Old
- Info = "远古版本"
- Case McVersionState.Original, McVersionState.Forge, McVersionState.NeoForge, McVersionState.Fabric, McVersionState.OptiFine, McVersionState.LiteLoader
- Info = Version.ToString
- Case McVersionState.Fool
- Info = GetMcFoolName(Version.McName)
- Case McVersionState.Error
- '已有错误信息
- Case Else
- Info = "发生了未知错误,请向作者反馈此问题"
- End Select
- If Not State = McVersionState.Error Then
- If HasJumpLoader Then Info += ", JumpLoader"
- If Setup.Get("VersionServerLogin", Version:=Me) = 3 Then Info += ", 统一通行证验证"
- If Setup.Get("VersionServerLogin", Version:=Me) = 4 Then Info += ", Authlib 验证"
- End If
- Else
- Info = CustomInfo
- End If
+ Info = If(CustomInfo <> "", CustomInfo, GetDefaultDescription())
'确定版本收藏状态
IsStar = ReadIni(Path & "PCL\Setup.ini", "IsStar", False)
'确定版本显示种类
@@ -842,6 +824,41 @@ ExitDataLoad:
End Try
Return Me
End Function
+ '''
+ ''' 获取版本的默认描述。
+ '''
+ Public Function GetDefaultDescription() As String
+ Dim Info As String = ""
+ Select Case State
+ Case McVersionState.Snapshot
+ If Version.McName.ContainsF("pre", True) Then
+ Info = "预发布版 " & Version.McName
+ ElseIf Version.McName.ContainsF("rc", True) Then
+ Info = "发布候选 " & Version.McName
+ ElseIf Version.McName.Contains("experimental") OrElse Version.McName = "pending" Then
+ Info = "实验性快照"
+ Else
+ Info = "快照 " & Version.McName
+ End If
+ Case McVersionState.Old
+ Info = "远古版本"
+ Case McVersionState.Original, McVersionState.Forge, McVersionState.NeoForge, McVersionState.Fabric, McVersionState.OptiFine, McVersionState.LiteLoader
+ Info = Version.ToString
+ Case McVersionState.Fool
+ Info = GetMcFoolName(Version.McName)
+ Case McVersionState.Error
+ Return Me.Info '已有错误信息
+ Case Else
+ Info = "发生了未知错误,请向作者反馈此问题"
+ End Select
+ If Not State = McVersionState.Error Then
+ If HasJumpLoader Then Info += ", JumpLoader"
+ If Setup.Get("VersionServerLogin", Version:=Me) = 3 Then Info += ", 统一通行证验证"
+ If Setup.Get("VersionServerLogin", Version:=Me) = 4 Then Info += ", Authlib 验证"
+ End If
+ Return Info
+ End Function
+
Public IsLoaded As Boolean = False
Public Overrides Function Equals(obj As Object) As Boolean
@@ -1123,10 +1140,10 @@ Reload:
McVersionList = McVersionListLoadNoCache(Path)
End If
IsFirstMcVersionListLoad = False
- If Loader.IsAborted Then Exit Sub
'改变当前选择的版本
OnLoaded:
+ If Loader.IsAborted Then Exit Sub
If McVersionList.Any(Function(v) v.Key <> McVersionCardType.Error) Then
'尝试读取已储存的选择
Dim SavedSelection As String = ReadIni(Path & "PCL.ini", "Version")
@@ -1619,7 +1636,7 @@ OnLoaded:
Dim FileAddress As String = PathTemp & "Cache\Skin\" & GetHash(Address) & ".png"
SyncLock McSkinDownloadLock
If Not File.Exists(FileAddress) Then
- NetDownload(Address, FileAddress & NetDownloadEnd)
+ NetDownloadByClient(Address, FileAddress & NetDownloadEnd)
File.Delete(FileAddress)
FileSystem.Rename(FileAddress & NetDownloadEnd, FileAddress)
Log("[Minecraft] 皮肤下载成功:" & FileAddress)
@@ -1962,7 +1979,7 @@ OnLoaded:
Try
Log("[Minecraft] 开始获取统一通行证下载信息")
'测试链接:https://auth.mc-user.com:233/00000000000000000000000000000000/
- DownloadInfo = GetJson(NetGetCodeByDownload({
+ DownloadInfo = GetJson(NetGetCodeByLoader({
"https://auth.mc-user.com:233/" & Setup.Get("VersionServerNide", Version:=Version)}, IsJson:=True))
Catch ex As Exception
Log(ex, "获取统一通行证下载信息失败")
@@ -1985,7 +2002,7 @@ OnLoaded:
'获取下载信息
Try
Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
- DownloadInfo = GetJson(NetGetCodeByDownload({
+ DownloadInfo = GetJson(NetGetCodeByLoader({
"https://authlib-injector.yushi.moe/artifact/latest.json",
"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"
}, IsJson:=True))
@@ -2085,8 +2102,8 @@ OnLoaded:
Splited(0).Replace(".", "\") & "\" & Splited(1) & "\" & Splited(2) & "\" & Splited(1) & "-" & Splited(2) & ".jar"
'判断 OptiFine 是否应该使用 installer
If McLibGet.Contains("optifine\OptiFine\1.") AndAlso Splited(2).Split(".").Count > 1 Then
- Dim MajorVersion As Integer = Val(Splited(2).Split(".")(1))
- Dim MinorVersion As Integer = If(Splited(2).Split(".").Count > 1, Val(Splited(2).Split(".")(2).BeforeFirst("_")), 0)
+ Dim MajorVersion As Integer = Val(Splited(2).Split(".")(1).BeforeFirst("_"))
+ Dim MinorVersion As Integer = If(Splited(2).Split(".").Count > 2, Val(Splited(2).Split(".")(2).BeforeFirst("_")), 0)
If (MajorVersion = 12 OrElse (MajorVersion = 20 AndAlso MinorVersion >= 4) OrElse MajorVersion >= 21) AndAlso '仅在 1.12 (无法追溯) 和 1.20.4+ (#5376) 遇到此问题
File.Exists($"{CustomMcFolder}libraries\{Splited(0).Replace(".", "\")}\{Splited(1)}\{Splited(2)}\{Splited(1)}-{Splited(2)}-installer.jar") Then
McLaunchLog("已将 " & Original & " 替换为对应的 Installer 文件")
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
index 64cfcab..624a685 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
@@ -1,9 +1,7 @@
-Imports System.IO.Compression
-Imports System.Linq.Expressions
+Imports System.IO.Compression
Public Module ModModpack
-#Region "安装"
'触发整合包安装的外部接口
'''
''' 弹窗要求选择一个整合包文件并进行安装。
@@ -31,6 +29,9 @@ Public Module ModModpack
Dim Archive As ZipArchive = Nothing
Dim ArchiveBaseFolder As String = ""
Try
+ '字符校验
+ Dim TargetFolder As String = $"{PathMcFolder}versions\{VersionName}\"
+ If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("游戏路径中不能含有感叹号或分号:" & TargetFolder, HintType.Critical) : Throw New CancelledException
'获取整合包种类与关键 Json
Dim PackType As Integer = -1
Try
@@ -108,29 +109,7 @@ Public Module ModModpack
End Try
End Function
- '整合包缓存清理
- Private IsInstallCacheCleared As Boolean = False
- Private IsInstallCacheClearing As Boolean = False
Private Sub ExtractModpackFiles(InstallTemp As String, FileAddress As String, Loader As LoaderBase, LoaderProgressDelta As Double)
- '清理缓存文件夹
- If Not IsInstallCacheCleared Then
- IsInstallCacheCleared = True
- IsInstallCacheClearing = True
- Try
- Log("[ModPack] 开始清理整合包安装缓存")
- DeleteDirectory(PathTemp & "PackInstall\")
- Log("[ModPack] 已清理整合包安装缓存")
- Catch ex As Exception
- Log(ex, "清理整合包安装缓存失败")
- Finally
- IsInstallCacheClearing = False
- End Try
- ElseIf IsInstallCacheClearing Then
- '等待另一个整合包安装的清理步骤完成
- Do While IsInstallCacheClearing
- Thread.Sleep(1)
- Loop
- End If
'解压文件
Dim RetryCount As Integer = 1
Dim Encode = Encoding.GetEncoding("GB18030")
@@ -204,7 +183,7 @@ Retry:
End If
Next
'解压与配置文件
- Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
+ Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase)
Dim OverrideHome As String = If(Json("overrides"), "")
If OverrideHome <> "" Then
@@ -212,13 +191,15 @@ Retry:
Sub(Task As LoaderTask(Of String, Integer))
ExtractModpackFiles(InstallTemp, FileAddress, Task, 0.6)
Task.Progress = 0.6
- Dim OverridePath As String = InstallTemp & ArchiveBaseFolder & OverrideHome
'复制结果
- If Directory.Exists(OverridePath) Then
- CopyDirectory(OverridePath, PathMcFolder & "versions\" & VersionName, Sub(Delta) Task.Progress += Delta * 0.35)
- Log($"[ModPack] 整合包 override 复制:{OverridePath} -> {PathMcFolder & "versions\" & VersionName}")
+ Log("[ModPack] 整合包 override 目标:" & OverrideHome)
+ Dim OverrideRoot = InstallTemp & ArchiveBaseFolder & If(OverrideHome = "." OrElse OverrideHome = "./", "", OverrideHome) '#5613
+ If Directory.Exists(OverrideRoot) Then
+ Dim CopyTarget = $"{PathMcFolder}versions\{VersionName}"
+ CopyDirectory(OverrideRoot, CopyTarget, Sub(Delta) Task.Progress += Delta * 0.35)
+ Log($"[ModPack] 整合包 override 复制:{OverrideRoot} -> {CopyTarget}")
Else
- Log($"[ModPack] 整合包中未找到 override 文件夹:{OverridePath}")
+ Log($"[ModPack] 整合包中未找到 override 文件夹:{OverrideRoot}")
End If
Task.Progress = 0.95
'开启版本隔离
@@ -314,7 +295,7 @@ Retry:
Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
Sub(Task As LoaderTask(Of String, String))
'设置图标
- Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
+ Dim VersionFolder As String = $"{PathMcFolder}versions\{VersionName}\"
If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
@@ -328,6 +309,10 @@ Retry:
File.Delete(Target)
End If
Next
+ If File.Exists(FileAddress) AndAlso GetFileNameWithoutExtentionFromPath(FileAddress) = "modpack" Then
+ Log("[ModPack] 删除安装整合包文件:" & FileAddress)
+ File.Delete(FileAddress)
+ End If
End Sub) With {.ProgressWeight = 0.1, .Show = False})
'重复任务检查
@@ -392,7 +377,7 @@ Retry:
If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
End If
'解压和配置文件
- Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
+ Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer))
@@ -421,7 +406,7 @@ Retry:
If File("env") IsNot Nothing Then
Select Case File("env")("client").ToString
Case "optional"
- If MyMsgBox("是否要下载整合包中的可选文件 " & GetFileNameFromPath(File("path").ToString) & "?",
+ If MyMsgBox("是否要下载可选文件 " & GetFileNameFromPath(File("path").ToString) & "?",
"下载可选文件", "是", "否") = 2 Then
Continue For
End If
@@ -476,10 +461,14 @@ Retry:
File.Delete(Target)
End If
Next
+ If File.Exists(FileAddress) AndAlso GetFileNameWithoutExtentionFromPath(FileAddress) = "modpack" Then
+ Log("[ModPack] 删除安装整合包文件:" & FileAddress)
+ File.Delete(FileAddress)
+ End If
End Sub) With {.ProgressWeight = 0.1, .Show = False})
'重复任务检查
- Dim LoaderName As String = "Modrinth 整合包安装:" & VersionName & " "
+ Dim LoaderName As String = $"Modrinth 整合包安装:{VersionName} "
If LoaderTaskbar.Any(Function(l) l.Name = LoaderName) Then
Hint("该整合包正在安装中!", HintType.Critical)
Throw New CancelledException
@@ -510,7 +499,7 @@ Retry:
If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
'解压与配置文件
- Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
+ Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer))
@@ -593,7 +582,7 @@ Retry:
If VersionName = "" Then VersionName = MyMsgBoxInput("输入版本名称", "", "", New ObjectModel.Collection(Of Validate) From {Validate})
If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
'解压、配置设置文件
- Dim InstallTemp As String = $"{PathTemp}PackInstall\{RandomInteger(0, 100000)}\"
+ Dim InstallTemp As String = RequestTaskTempFolder()
Dim SetupFile As String = $"{PathMcFolder}versions\{VersionName}\PCL\Setup.ini"
Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
@@ -613,21 +602,14 @@ Retry:
Try
Dim MMCSetupFile As String = InstallTemp & ArchiveBaseFolder & "instance.cfg"
If File.Exists(MMCSetupFile) Then
- '部分等号替换为冒号,以符合 ini 文件格式
- '为JvmArgs添加排除项,防止参数中的等号被错误的替换
- Dim MMCSetupFileLines() As String = File.ReadAllLines(MMCSetupFile)
- For i As Integer = 0 To MMCSetupFileLines.Length - 1
- '按行读取,检查开头是否为"JvmArgs="
- If MMCSetupFileLines(i).StartsWith("JvmArgs=") Then
- '有则只替换这个等号,忽略参数部分
- MMCSetupFileLines(i) = "JvmArgs:" & MMCSetupFileLines(i).Substring("JvmArgs=".Length)
- Else
- '无则全部替换
- MMCSetupFileLines(i) = MMCSetupFileLines(i).Replace("=", ":")
- End If
+ '将其中的等号替换为冒号,以符合 ini 文件格式
+ Dim Lines As New List(Of String)
+ For Each Line In ReadFile(MMCSetupFile).Split({vbCr, vbLf}, StringSplitOptions.RemoveEmptyEntries)
+ If Not Line.Contains("=") Then Continue For
+ Lines.Add(Line.BeforeFirst("=") & ":" & Line.AfterFirst("="))
Next
- '修改后写回文件
- File.WriteAllLines(MMCSetupFile, MMCSetupFileLines)
+ WriteFile(MMCSetupFile, Join(Lines, vbCrLf))
+ '读取文件
If ReadIni(MMCSetupFile, "OverrideCommands", False) Then
Dim PreLaunchCommand As String = ReadIni(MMCSetupFile, "PreLaunchCommand")
If PreLaunchCommand <> "" Then
@@ -656,16 +638,17 @@ Retry:
CopyFile($"{InstallTemp}{ArchiveBaseFolder}{Logo}.png", $"{PathMcFolder}versions\{VersionName}\PCL\Logo.png")
Log($"[ModPack] 迁移 MultiMC 版本独立设置:版本图标({Logo}.png)")
End If
- 'JvmArgs参数
+ 'JVM 参数
Dim JvmArgs As String = ReadIni(MMCSetupFile, "JvmArgs", "")
If JvmArgs <> "" Then
- WriteIni(SetupFile, "VersionAdvanceJvm", JvmArgs)
- Log("[ModPack] 迁移 MultiMC 版本独立设置:JVM 参数:" & JvmArgs)
- End If
- '这段我看参数有就给加上了...
- If ReadIni(MMCSetupFile, "OverrideJavaArgs", False) Then
- WriteIni(SetupFile, "VersionAdvanceJavaArgs", True)
- Log("[ModPack] 迁移 MultiMC 版本独立设置:覆盖 JavaArgs")
+ If ReadIni(MMCSetupFile, "OverrideJavaArgs", False) Then
+ WriteIni(SetupFile, "VersionAdvanceJvm", JvmArgs)
+ Log("[ModPack] 迁移 MultiMC 版本独立设置:JVM 参数(覆盖):" & JvmArgs)
+ Else
+ JvmArgs += " " & Setup.Get("LaunchAdvanceJvm")
+ WriteIni(SetupFile, "VersionAdvanceJvm", JvmArgs)
+ Log("[ModPack] 迁移 MultiMC 版本独立设置:JVM 参数(追加):" & JvmArgs)
+ End If
End If
End If
Catch ex As Exception
@@ -740,7 +723,7 @@ Retry:
If String.IsNullOrEmpty(VersionName) Then Throw New CancelledException
End If
'解压与配置文件
- Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
+ Dim InstallTemp As String = RequestTaskTempFolder()
Dim InstallLoaders As New List(Of LoaderBase)
InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
Sub(Task As LoaderTask(Of String, Integer))
@@ -811,7 +794,6 @@ Retry:
MyMsgBox("接下来请选择一个空文件夹,它会被安装到这个文件夹里。", "安装", "继续", ForceWait:=True)
Dim TargetFolder As String = SelectFolder("选择安装目标(必须是一个空文件夹)")
If String.IsNullOrEmpty(TargetFolder) Then Throw New CancelledException
- If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Critical) : Throw New CancelledException
If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Critical) : Throw New CancelledException
'解压
Dim Loader As New LoaderCombo(Of String)("解压压缩包", {
@@ -839,8 +821,9 @@ Retry:
'尝试使用附带的启动器打开
If Launcher IsNot Nothing Then
Log("[Modpack] 找到压缩包中附带的启动器:" & Launcher)
- If MyMsgBox($"整合包中似乎自带了启动器,是否换用它继续安装?{vbCrLf}通常推荐这样做,以获得最佳体验。{vbCrLf}即将打开:{Launcher}", "换用整合包启动器?", "继续", "取消") = 1 Then
- ShellOnly(Launcher, "--wait")
+ If MyMsgBox($"整合包里似乎自带了启动器,是否换用它继续安装?{vbCrLf}即将打开:{Launcher}", "换用整合包启动器?", "换用", "不换用") = 1 Then
+ OpenExplorer(TargetFolder)
+ ShellOnly(Launcher, "--wait") '要求等待已有的 PCL 退出
Log("[Modpack] 为换用整合包中的启动器启动,强制结束程序")
FrmMain.EndProgram(False)
Return
@@ -848,15 +831,17 @@ Retry:
Else
Log("[Modpack] 未找到压缩包中附带的启动器")
End If
+ OpenExplorer(TargetFolder)
'加入文件夹列表
Dim VersionName As String = GetFolderNameFromPath(TargetFolder)
+ Directory.CreateDirectory(TargetFolder & ".minecraft\")
PageSelectLeft.AddFolder(
- TargetFolder & ArchiveBaseFolder.Replace("/", "\").TrimStart("\"), '格式例如:包裹文件夹\.minecraft\(最短为空字符串)
+ TargetFolder & ".minecraft\" & ArchiveBaseFolder.Replace("/", "\").TrimStart("\"), '格式例如:包裹文件夹\.minecraft\(最短为空字符串)
VersionName, False)
'调用 modpack 文件进行安装
Dim ModpackFile = Directory.GetFiles(TargetFolder, "modpack.*", SearchOption.AllDirectories).First
Log("[Modpack] 调用 modpack 文件继续安装:" & ModpackFile)
- ModpackInstall(ModpackFile, VersionName)
+ ModpackInstall(ModpackFile)
End Sub)
})
Loader.Start(TargetFolder)
@@ -906,340 +891,6 @@ Retry:
Return Loader
End Function
-#End Region
-#End Region
-
-#Region "导出"
- Private ExpTempDir As String = PathTemp & "PackExport\"
- '''
- ''' 整合包类型。
- '''
- Public Enum ModpackType
- Modrinth
- End Enum
- '''
- ''' 导出整合包的选项。
- '''
- Public Class ExportOptions
- '''
- ''' 要导出的版本。
- '''
- Public Version As McVersion
- '''
- ''' 要保存整合包的位置。
- '''
- Public Dest As String
- '''
- ''' 是否包括 PCL 文件。
- '''
- Public IncludePCL As Boolean
- '''
- ''' 整合包类型。
- '''
- Public Type As ModpackType
- '''
- ''' 要保留到整合包的文件。
- '''
- Public Additional As String()
- '''
- ''' 是否需要保留 PCL 全局设置。仅在包括 PCL 时有效。
- '''
- Public PCLSetupGlobal As Boolean = True
- '''
- ''' 整合包名称。
- '''
- Public Name As String = ""
- '''
- ''' 整合包描述。
- '''
- Public Desc As String = ""
- '''
- ''' 版本号。
- '''
- Public VerID As String = ""
- Public Sub New(Version As McVersion,
- Dest As String,
- IncludePCL As Boolean,
- Additional As String(),
- Optional PCLSetupGlobal As Boolean = True,
- Optional Name As String = "",
- Optional Desc As String = "",
- Optional VerID As String = "")
- Me.Version = Version
- Me.Dest = Dest
- Me.IncludePCL = IncludePCL
- Me.Additional = Additional
- Me.PCLSetupGlobal = PCLSetupGlobal
- Me.Name = Name
- Me.Desc = Desc
- Me.VerID = VerID
- End Sub
- End Class
- '''
- ''' 导出整合包,返回是否成功。
- '''
- Public Function ModpackExport(Task As LoaderTask(Of ExportOptions, Boolean)) As Boolean
- If Task.Input.IncludePCL Then Return ExportCompressed(Task)
- Return ExportModrinth(Task)
- End Function
-
-#Region "冗余"
- Private VersionRedundant As String() = {"^screenshots", "^backups", "^command_history\.txt$", '个人文件
- "^.*-natives", "^server-resource-packs", "^user.*cache\.json$", "^\.optifine", "^\.fabric", "^\.mixin\.out", '缓存
- "^.*\.jar$", "^downloads", "^realms_persistence.json$", "^\$\{natives_directory\}", "^essential", '可联网更新
- "^logs", "^crash-reports", ".*\.log$", "^debug", '日志
- ".*\.dat_old$", ".*\.old$", '备份
- "^\$\{quickPlayPath\}", '服务器
- "^\.replay_cache", "^replay_recordings", "^replay_videos", 'ReplayMod
- "^irisUpdateInfo\.json$", 'Iris
- "^modernfix", 'ModernFix 模组
- "^modtranslations", 'Mod 翻译
- "^schematics", 'schematics 模组
- "^journeymap\\data", 'JourneyMap 模组
- "^mods\\\.connector", '信雅互联
- "^.*\.BakaCoreInfo$", 'BakaXL 配置
- "^hmclversion\.cfg$", "^log4j2\.xml$", 'HMCL 配置
- "^assets$", "^libraries$", "^\$natives$", "^launcher_profiles\.json$", "^versions$" '开启版本隔离时排除的文件
- }
- Public Function IsVerRedundant(FilePath As String, PathIndie As String) As Boolean
- FilePath = FilePath.Replace("/", "\")
- For Each regex In VersionRedundant
- If RegexCheck(FilePath.Replace(PathIndie, ""), regex, RegularExpressions.RegexOptions.IgnoreCase) Then Return True
- Next
- Return False
- End Function
- Private MustExport As String() = {
- "mods", "PCL", ".*\.json"
- }
- Public Function IsMustExport(FilePath As String, PathIndie As String) As Boolean
- For Each regex In MustExport
- If RegexCheck(FilePath.Replace(PathIndie, ""), regex, RegularExpressions.RegexOptions.IgnoreCase) AndAlso Not (FilePath.EndsWithF(".zip") OrElse FilePath.EndsWithF("\")) Then Return True
- Next
- Return False
- End Function
-
-#End Region
-
-#Region "不同类型整合包的导出方法"
- Private Function ExportModrinth(Task As LoaderTask(Of ExportOptions, Boolean)) As Boolean
- Try
- Dim Version = Task.Input.Version
- Dim DestPath = Task.Input.Dest
- Dim Additional = Task.Input.Additional
- Dim Name = Task.Input.Name
- Dim Description = Task.Input.Desc
- Dim VerID = Task.Input.VerID
- Log($"[Export] 导出整合包(Modrinth):{Version.Path} -> {DestPath},额外版本文件 {Additional.Count} 个")
- Dim tempDir As String = $"{ExpTempDir}{GetUuid()}\"
- Log($"[Export] 缓存文件夹:{tempDir}")
- DeleteDirectory(tempDir)
- Directory.CreateDirectory(tempDir)
-
-#Region "0.04-0.40:从 Modrinth 获取 Mod 工程信息,得到 URL"
- Dim Mods As New Dictionary(Of String, McMod)
- If Directory.Exists(Version.Path & "mods\") Then
- For Each m In Directory.EnumerateFiles(Version.Path & "mods\")
- If m.EndsWithF(".jar") Then Mods.Add(GetFileNameFromPath(m), New McMod(m))
- Next
- End If
-
- '从 Modrinth 获取信息
- Dim ModrinthMapping As New Dictionary(Of String, JObject) 'Modrinth 获取到的 Mod
- Dim CurseForgeMapping As New Dictionary(Of String, JObject) 'CurseForge 获取到的 Mod
- Dim Failed = Mods.ToArray().ToDictionary(Function(a) a.Key, Function(a) a.Value) '获取失败的 Mod
- Dim ModrinthHashes = Mods.Select(Function(m) m.Value.ModrinthHash).ToList()
- If Mods.Count = 0 Then GoTo JumpMod
- Dim ModrinthRaw = CType(GetJson(NetRequestRetry("https://api.modrinth.com/v2/version_files", "POST",
- $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
- Log($"[Export] 从 Modrinth 获取到 {ModrinthRaw.Count} 个本地 Mod 的对应信息")
- Task.Progress = 0.35
-
- For Each Entry In Mods
- If (Not ModrinthRaw.ContainsKey(Entry.Value.ModrinthHash)) OrElse
- (ModrinthRaw(Entry.Value.ModrinthHash)("files")(0)("hashes")("sha1") <> Entry.Value.ModrinthHash) Then
- Continue For
- End If
- Failed.Remove(Entry.Key)
- ModrinthMapping.Add(Entry.Key, ModrinthRaw(Entry.Value.ModrinthHash)("files")(0))
- Next
- Task.Progress = 0.4
-#End Region
-
-#Region "0.40-0.80:从 CurseForge 继续获取"
- Dim CurseForgeHashes = Mods.Select(Function(m) m.Value.CurseForgeHash).ToList()
- Dim CurseForgeRaw = CType(CType(GetJson(NetRequestRetry("https://api.curseforge.com/v1/fingerprints/432/", "POST",
- $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
- Log($"[Export] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地 Mod 的对应信息")
- Task.Progress = 0.7
-
- For Each m In CurseForgeRaw
- Dim hash As String = ""
- For Each h In m("file")("hashes") '获取 Modrinth Hash
- If h("algo").ToString = 1 Then
- hash = h("value")
- Exit For
- End If
- Next
- If String.IsNullOrEmpty(hash) OrElse String.IsNullOrEmpty(m("file")("downloadUrl")) Then Continue For
-
- m("file")("ModrinthHash") = hash
- Dim f = Mods.ToArray()
- For Each file In f
- If file.Value.ModrinthHash = hash Then
- CurseForgeMapping.Add(file.Key, m("file"))
- Failed.Remove(file.Key)
- Exit For
- End If
- Next
- Next
- Task.Progress = 0.8
-#End Region
-
-JumpMod:
-#Region "0.80-0.90:合并下载链接,复制 Logo,写入 Json 文件"
- '获取作为检查目标的加载器和版本
- Dim ModLoaders = GetTargetModLoaders()
- Dim McVersion = Version.Version.McName
- Log($"[Export] 目标加载器:{ModLoaders.Join("/")},版本:{McVersion}")
-
- '合并下载链接
- Dim BothUrls As New Dictionary(Of String, String()) '两个下载源都有 URL 的 Mod
- For Each modrinth In ModrinthMapping
- Dim curseforge As New JObject
- If CurseForgeMapping.TryGetValue(modrinth.Key, curseforge) Then
- BothUrls.Add(modrinth.Key, {modrinth.Value("url"), curseforge("downloadUrl")})
- End If
- Next
- Task.Progress = 0.83
-
- Dim files As New JArray
- For Each m In ModrinthMapping
- files.Add(New JObject From {
- {"path", $"mods/{m.Key}"},
- {"hashes", m.Value("hashes")},
- {"env", New JObject From {{"client", "required"}, {"server", "required"}}},
- {"downloads", If(BothUrls.ContainsKey(m.Key), JArray.FromObject(BothUrls(m.Key)), New JArray From {m.Value("url")})},
- {"fileSize", m.Value("size")}
- })
- Next
- For Each m In CurseForgeMapping
- If Not BothUrls.ContainsKey(m.Key) Then
- files.Add(New JObject From {
- {"path", $"mods/{m.Key}"},
- {"hashes", New JObject From {{"sha1", m.Value("ModrinthHash").ToString}}},
- {"env", New JObject From {{"client", "required"}, {"server", "required"}}},
- {"downloads", New JArray From {m.Value("downloadUrl")}},
- {"fileSize", m.Value("fileLength")}
- })
- End If
- Next
- Task.Progress = 0.86
- 'If Not String.IsNullOrEmpty(Logo) Then
- ' Dim LogoObj As New MyBitmap(Logo)
- ' LogoObj.Save(tempDir & "Logo.png")
- 'End If
- 'Task.Progress = 0.88
-
- Dim depend As New JObject From {{"minecraft", McVersion}}
- If Version.Version.HasForge Then depend.Add("forge", Version.Version.ForgeVersion)
- If Version.Version.HasFabric Then depend.Add("fabric-loader", Version.Version.FabricVersion)
- If Version.Version.HasNeoForge Then depend.Add("neoforge", Version.Version.NeoForgeVersion)
-
- Dim json As New JObject From {
- {"game", "minecraft"},
- {"formatVersion", 1},
- {"versionId", VerID},
- {"name", If(String.IsNullOrEmpty(Name), Version.Name, Name)},
- {"summary", Description},
- {"files", files},
- {"dependencies", depend}
- }
- 'If Not String.IsNullOrEmpty(Logo) Then json.Add("logo", tempDir & "Logo.png")
-
- File.WriteAllText(tempDir & "modrinth.index.json", json.ToString)
- Task.Progress = 0.9
-#End Region
-
-#Region "0.90-0.99:将其余文件保存到 overrides 目录"
- For Each m In Failed
- CopyFile(m.Value.Path, tempDir & "overrides\mods\" & m.Value.FileName)
- Next
- Task.Progress = 0.92
-
- '额外文件
- For Each p In Additional
- If String.IsNullOrWhiteSpace(p) Then Continue For '传空字串进去会直接把整个版本文件夹拷过去
- If Not p.StartsWithF(GetPathFromFullPath(GetPathFromFullPath(Version.Path)), IgnoreCase:=True) Then Continue For
- Dim relative As String = p.Replace(Version.Path, "").Replace(GetPathFromFullPath(GetPathFromFullPath(Version.Path)), "")
- If File.Exists(p) AndAlso Not IsVerRedundant(p, Version.PathIndie) Then
- CopyFile(p, $"{tempDir}overrides\{relative}")
- End If
- Next
- Task.Progress = 0.96
-
- '如果 resourcepacks shaderpacks 文件夹中有文件夹格式的包,则先将文件夹压缩成 Zip 文件后再打包入对应位置
- '这可以避免安装时出现路径长度限制而出错
- If Directory.Exists($"{tempDir}overrides\resourcepacks\") Then
- For Each d In Directory.EnumerateDirectories($"{tempDir}overrides\resourcepacks\")
- If Not File.Exists(d + ".zip") Then ZipFile.CreateFromDirectory(d, d + ".zip")
- DeleteDirectory(d)
- Next
- End If
- If Directory.Exists($"{tempDir}overrides\shaderpacks\") Then
- For Each d In Directory.EnumerateDirectories($"{tempDir}overrides\shaderpacks\")
- If Not File.Exists(d + ".zip") Then ZipFile.CreateFromDirectory(d, d + ".zip")
- DeleteDirectory(d)
- Next
- End If
-#End Region
-
- If File.Exists(DestPath) Then File.Delete(DestPath) '选择文件的时候已经确认了要替换
- ZipFile.CreateFromDirectory(tempDir, DestPath)
- DeleteDirectory(tempDir)
- Return True
- Catch ex As Exception
- Log(ex, "导出整合包失败", LogLevel.Msgbox)
- Return False
- End Try
- End Function
- Private Function ExportCompressed(Task As LoaderTask(Of ExportOptions, Boolean)) As Boolean
- Try
- Dim Version = Task.Input.Version
- Dim DestPath = Task.Input.Dest
- Dim Additional = Task.Input.Additional
- Dim Name = Task.Input.Name
- Dim VerID = Task.Input.VerID
- Dim PCLSetupGlobal = Task.Input.PCLSetupGlobal
- Log($"[Export] 导出整合包(含启动器):{Version.Path} -> {DestPath},额外版本文件 {Additional.Count} 个,全局设置 {If(PCLSetupGlobal, "导出", "不导出")}")
- Dim tempDir As String = $"{ExpTempDir}{GetUuid()}\"
- Log($"[Export] 最终压缩包的缓存文件夹:{tempDir}")
- Directory.CreateDirectory(tempDir)
-
- Task.Progress = 0.04
-
- Log("[Export] 开始导出 Modrinth 整合包")
- Task.Input.Dest = $"{tempDir}modpack.mrpack"
- ExportModrinth(Task)
-
- Log($"[Export] 正在复制 PCL 本体")
- CopyFile(PathWithName, tempDir & GetFileNameFromPath(PathWithName))
-
- If PCLSetupGlobal Then
- Log($"[Export] 正在复制 PCL 全局配置")
- CopyFile(Path & "PCL\Setup.ini", tempDir & "PCL\Setup.ini")
- End If
-
- If File.Exists(DestPath) Then File.Delete(DestPath) '选择文件的时候已经确认了要替换
- ZipFile.CreateFromDirectory(tempDir, DestPath)
- DeleteDirectory(tempDir)
- Return True
- Catch ex As Exception
- Log(ex, "导出整合包失败", LogLevel.Msgbox)
- Return False
- End Try
- End Function
-#End Region
-
#End Region
End Module
diff --git a/Plain Craft Launcher 2/Modules/ModEvent.vb b/Plain Craft Launcher 2/Modules/ModEvent.vb
index be69955..996d465 100644
--- a/Plain Craft Launcher 2/Modules/ModEvent.vb
+++ b/Plain Craft Launcher 2/Modules/ModEvent.vb
@@ -20,7 +20,7 @@
Hint("正在开启中,请稍候……")
OpenWebsite(Data(0))
- Case "打开文件", "打开帮助"
+ Case "打开文件", "打开帮助", "执行命令"
RunInThread(
Sub()
Try
@@ -29,15 +29,25 @@
Dim Location = ActualPaths(0), WorkingDir = ActualPaths(1)
Log($"[Control] 打开类自定义事件实际路径:{Location},工作目录:{WorkingDir}")
'执行
- If Type = "打开文件" Then
+ If Type = "打开帮助" Then
+ PageOtherHelp.EnterHelpPage(Location)
+ Else
+ If Not Setup.Get("HintCustomCommand") Then
+ Select Case MyMsgBox(
+ "即将执行:" & Location & If(Data.Length >= 2, " " & Data(1), "") & vbCrLf &
+ "请在确认该操作没有安全隐患后继续。", "执行确认", "继续", "继续且今后不再要求确认", "取消")
+ Case 2
+ Setup.Set("HintCustomCommand", True)
+ Case 3
+ Exit Sub
+ End Select
+ End If
Dim Info As New ProcessStartInfo With {
.Arguments = If(Data.Length >= 2, Data(1), ""),
.FileName = Location,
- .WorkingDirectory = WorkingDir
+ .WorkingDirectory = ShortenPath(WorkingDir)
}
Process.Start(Info)
- Else '打开帮助
- PageOtherHelp.EnterHelpPage(Location)
End If
Catch ex As Exception
Log(ex, "执行打开类自定义事件失败", LogLevel.Msgbox)
@@ -140,8 +150,8 @@
Dim LocalTemp2 As String = PathTemp & "CustomEvent\" & RawFileName.Replace(".json", ".xaml")
Log("[Event] 转换网络资源:" & RelativeUrl & " -> " & LocalTemp1)
Try
- NetDownload(RelativeUrl, LocalTemp1)
- NetDownload(RelativeUrl.Replace(".json", ".xaml"), LocalTemp1.Replace(".json", ".xaml"))
+ NetDownloadByClient(RelativeUrl, LocalTemp1)
+ NetDownloadByClient(RelativeUrl.Replace(".json", ".xaml"), LocalTemp1.Replace(".json", ".xaml"))
Catch ex As Exception
Throw New Exception("下载指定的文件失败!" & vbCrLf &
"注意,联网帮助页面须指向一个帮助 JSON 文件,并在同路径下包含相应 XAML 文件!" & vbCrLf &
@@ -174,7 +184,7 @@
Location = PathTemp & "Help\" & RelativeUrl
WorkingDir = PathTemp & "Help\"
Log("[Control] 自定义事件中由相对 PCL 自带帮助文件夹的路径" & EventType & ":" & Location)
- ElseIf EventType = "打开文件" Then
+ ElseIf EventType = "打开文件" OrElse EventType = "执行命令" Then
'直接使用原有路径启动程序
Location = RelativeUrl
Log("[Control] 自定义事件中直接" & EventType & ":" & Location)
diff --git a/Plain Craft Launcher 2/Modules/ModMain.vb b/Plain Craft Launcher 2/Modules/ModMain.vb
index 3c877a1..ecf5ca4 100644
--- a/Plain Craft Launcher 2/Modules/ModMain.vb
+++ b/Plain Craft Launcher 2/Modules/ModMain.vb
@@ -811,6 +811,54 @@ NextFile:
Public Declare Function SetForegroundWindow Lib "user32" (hWnd As IntPtr) As Integer
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (hWnd As IntPtr, msg As UInteger, wParam As Long, lParam As Long) As Boolean
+#End Region
+
+#Region "任务缓存"
+
+ Private IsTaskTempCleared As Boolean = False
+ Private IsTaskTempClearing As Boolean = False
+
+ '''
+ ''' 尝试清理任务缓存文件夹。
+ ''' 在整次运行中只会实际清理一次。
+ '''
+ Public Sub TryClearTaskTemp()
+ If Not IsTaskTempCleared Then
+ IsTaskTempCleared = True
+ IsTaskTempClearing = True
+ Try
+ Log("[System] 开始清理任务缓存文件夹")
+ DeleteDirectory($"{OsDrive}ProgramData\PCL\TaskTemp\")
+ DeleteDirectory($"{PathTemp}TaskTemp\")
+ Log("[System] 已清理任务缓存文件夹")
+ Catch ex As Exception
+ Log(ex, "清理任务缓存文件夹失败")
+ Finally
+ IsTaskTempClearing = False
+ End Try
+ ElseIf IsTaskTempClearing Then
+ '等待另一个清理步骤完成
+ Do While IsTaskTempClearing
+ Thread.Sleep(1)
+ Loop
+ End If
+ End Sub
+
+ '''
+ ''' 申请一个可用于任务缓存的临时文件夹,以 \ 结尾。
+ ''' 这些文件夹无需进行后续清理。
+ '''
+ ''' 是否要求路径不包含空格。
+ Public Function RequestTaskTempFolder(Optional RequireNonSpace As Boolean = False) As String
+ TryClearTaskTemp()
+ If RequireNonSpace Then
+ RequestTaskTempFolder = $"{OsDrive}ProgramData\PCL\Install\{GetUuid()}-{RandomInteger(0, 1000000)}\"
+ Else
+ RequestTaskTempFolder = $"{PathTemp}TaskTemp\{GetUuid()}-{RandomInteger(0, 1000000)}\"
+ End If
+ Directory.CreateDirectory(RequestTaskTempFolder)
+ End Function
+
#End Region
Public DragControl = Nothing
diff --git a/Plain Craft Launcher 2/Modules/ModSecret.vb b/Plain Craft Launcher 2/Modules/ModSecret.vb
index c34f9d0..3e36f17 100644
--- a/Plain Craft Launcher 2/Modules/ModSecret.vb
+++ b/Plain Craft Launcher 2/Modules/ModSecret.vb
@@ -246,18 +246,18 @@ Friend Module ModSecret
FrmMain.PanForm.Background.Freeze()
End Sub)
End Sub
- Public Sub ThemeCheckAll(EffectSetup As Boolean)
+ Friend Sub ThemeCheckAll(EffectSetup As Boolean)
End Sub
- Public Function ThemeCheckOne(Id As Integer) As Boolean
+ Friend Function ThemeCheckOne(Id As Integer) As Boolean
Return True
End Function
Friend Function ThemeUnlock(Id As Integer, Optional ShowDoubleHint As Boolean = True, Optional UnlockHint As String = Nothing) As Boolean
Return False
End Function
- Public Function ThemeCheckGold(Optional Code As String = Nothing) As Boolean
+ Friend Function ThemeCheckGold(Optional Code As String = Nothing) As Boolean
Return False
End Function
- Public Function DonateCodeInput() As Boolean?
+ Friend Function DonateCodeInput() As Boolean?
Return Nothing
End Function
@@ -276,6 +276,13 @@ Friend Module ModSecret
End Sub
Public Sub UpdateReplace(ProcessId As Integer, OldFileName As String, NewFileName As String, TriggerRestart As Boolean)
End Sub
+ '''
+ ''' 确保 PathTemp 下的 Latest.exe 是最新正式版的 PCL,它会被用于整合包打包。
+ ''' 如果不是,则下载一个。
+ '''
+ Friend Sub DownloadLatestPCL(Optional LoaderToSyncProgress As LoaderBase = Nothing)
+ '注意:如果要自行实现这个功能,请换用另一个文件路径,以免与官方版本冲突
+ End Sub
#End Region
diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
index f678bce..a2893a7 100644
--- a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
+++ b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*”
-
-
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
index 79704cb..2875dbd 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
+++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
@@ -87,6 +87,7 @@ Public Module ModDownloadLib
Dim LoadersLib As New List(Of LoaderBase)
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
+ Thread.Sleep(50) '等待 JSON 文件实际写入硬盘(#3710)
Log("[Download] 开始分析原版支持库文件:" & VersionFolder)
Task.Output = McLibFix(New McVersion(VersionFolder))
End Sub) With {.ProgressWeight = 1, .Show = False})
@@ -271,7 +272,7 @@ pause"
Loaders.Add(New LoaderDownload("下载服务端文件", New List(Of NetFile)) With {.ProgressWeight = 5})
'启动
- Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 服务端下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 服务端下载", Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(Id)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -316,7 +317,7 @@ pause"
Loaders.Add(New LoaderDownload("下载核心 JAR 文件", New List(Of NetFile)) With {.ProgressWeight = 5})
'启动
- Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(Id)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -394,7 +395,7 @@ pause"
Private Sub McDownloadOptiFineSave(DownloadInfo As DlOptiFineListEntry)
Try
Dim Id As String = DownloadInfo.NameVersion
- Dim Target As String = SelectAs("选择保存位置", DownloadInfo.NameFile, "OptiFine Jar (*.jar)|*.jar")
+ Dim Target As String = SelectSaveFile("选择保存位置", DownloadInfo.NameFile, "OptiFine Jar (*.jar)|*.jar")
If Not Target.Contains("\") Then Exit Sub
'重复任务检查
@@ -404,7 +405,7 @@ pause"
Exit Sub
Next
- Dim Loader As New LoaderCombo(Of DlOptiFineListEntry)("OptiFine " & DownloadInfo.NameDisplay & " 下载", McDownloadOptiFineSaveLoader(DownloadInfo, Target)) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of DlOptiFineListEntry)("OptiFine " & DownloadInfo.NameDisplay & " 下载", McDownloadOptiFineSaveLoader(DownloadInfo, Target)) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(DownloadInfo)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -454,7 +455,7 @@ pause"
.CreateNoWindow = True,
.RedirectStandardError = True,
.RedirectStandardOutput = True,
- .WorkingDirectory = BaseMcFolderHome
+ .WorkingDirectory = ShortenPath(BaseMcFolderHome)
}
If Info.EnvironmentVariables.ContainsKey("appdata") Then
Info.EnvironmentVariables("appdata") = BaseMcFolderHome
@@ -857,7 +858,7 @@ Retry:
Private Sub McDownloadLiteLoaderSave(DownloadInfo As DlLiteLoaderListEntry)
Try
Dim Id As String = DownloadInfo.Inherit
- Dim Target As String = SelectAs("选择保存位置", DownloadInfo.FileName.Replace("-SNAPSHOT", ""), "LiteLoader 安装器 (*.jar)|*.jar")
+ Dim Target As String = SelectSaveFile("选择保存位置", DownloadInfo.FileName.Replace("-SNAPSHOT", ""), "LiteLoader 安装器 (*.jar)|*.jar")
If Not Target.Contains("\") Then Exit Sub
'重复任务检查
@@ -893,7 +894,7 @@ Retry:
End If
Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 1024))}) With {.ProgressWeight = 15})
'启动
- Dim Loader As New LoaderCombo(Of DlLiteLoaderListEntry)("LiteLoader " & Id & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of DlLiteLoaderListEntry)("LiteLoader " & Id & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(DownloadInfo)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -1037,7 +1038,7 @@ Retry:
Public Sub McDownloadForgelikeSave(Info As DlForgelikeEntry)
Try
- Dim Target As String = SelectAs("选择保存位置", $"{Info.LoaderName}-{Info.Inherit}-{Info.VersionName}.{Info.FileExtension}",
+ Dim Target As String = SelectSaveFile("选择保存位置", $"{Info.LoaderName}-{Info.Inherit}-{Info.VersionName}.{Info.FileExtension}",
$"{Info.LoaderName} 安装器 (*.{Info.FileExtension})|*.{Info.FileExtension}")
Dim DisplayName As String = $"{Info.LoaderName} {Info.Inherit} - {Info.VersionName}"
If Not Target.Contains("\") Then Exit Sub
@@ -1072,7 +1073,7 @@ Retry:
Loaders.Add(New LoaderDownload("下载主文件", Files) With {.ProgressWeight = 6})
'启动
- Dim Loader = New LoaderCombo(Of DlForgelikeEntry)(DisplayName & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader = New LoaderCombo(Of DlForgelikeEntry)(DisplayName & " 下载", Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(Info)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -1339,7 +1340,7 @@ Retry:
If Json("data") IsNot Nothing AndAlso Json("data")("MOJMAPS") IsNot Nothing Then
'下载原版 Json 文件
Task.Progress = 0.4
- Dim RawJson As JObject = GetJson(NetGetCodeByDownload(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
+ Dim RawJson As JObject = GetJson(NetGetCodeByLoader(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
'[net.minecraft:client:1.17.1-20210706.113038:mappings@txt] 或 @tsrg]
Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).BeforeFirst("@")
Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings." & Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(1))
@@ -1641,7 +1642,7 @@ Retry:
RunInNewThread(Sub()
Try
Log("[Download] 刷新 Forge 推荐版本缓存开始")
- Dim Result As String = NetGetCodeByDownload("https://bmclapi2.bangbang93.com/forge/promos")
+ Dim Result As String = NetGetCodeByLoader("https://bmclapi2.bangbang93.com/forge/promos")
If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
Dim ResultJson As JContainer = GetJson(Result)
'获取所有推荐版本列表
@@ -1789,7 +1790,7 @@ Retry:
Dim Url As String = DownloadInfo("url").ToString
Dim FileName As String = GetFileNameFromPath(Url)
Dim Version As String = GetFileNameFromPath(DownloadInfo("version").ToString)
- Dim Target As String = SelectAs("选择保存位置", FileName, "Fabric 安装器 (*.jar)|*.jar")
+ Dim Target As String = SelectSaveFile("选择保存位置", FileName, "Fabric 安装器 (*.jar)|*.jar")
If Not Target.Contains("\") Then Exit Sub
'重复任务检查
@@ -1807,7 +1808,7 @@ Retry:
Address.Add(Url)
Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
'启动
- Dim Loader As New LoaderCombo(Of JObject)("Fabric " & Version & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of JObject)("Fabric " & Version & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(DownloadInfo)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -1974,6 +1975,20 @@ Retry:
End Class
+ '''
+ ''' 在加载器状态改变后显示一条提示。
+ ''' 不会进行任何其他操作。
+ '''
+ Public Sub LoaderStateChangedHintOnly(Loader)
+ Select Case Loader.State
+ Case LoadState.Finished
+ Hint(Loader.Name & "成功!", HintType.Finish)
+ Case LoadState.Failed
+ Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
+ Case LoadState.Aborted
+ Hint(Loader.Name & "已取消!", HintType.Info)
+ End Select
+ End Sub
'''
''' 安装加载器状态改变后进行提示和重载文件夹列表的方法。
'''
@@ -1992,21 +2007,6 @@ Retry:
McInstallFailedClearFolder(Loader)
LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
End Sub
- '''
- ''' 安装加载器状态改变后进行提示的方法。它不会在失败时删除文件夹,也不会刷新 MC 文件夹。
- '''
- Public Sub DownloadStateSave(Loader)
- Select Case Loader.State
- Case LoadState.Finished
- Hint(Loader.Name & "成功!", HintType.Finish)
- Case LoadState.Failed
- Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
- Case LoadState.Aborted
- Hint(Loader.Name & "已取消!", HintType.Info)
- Case LoadState.Loading
- Exit Sub '不重新加载版本列表
- End Select
- End Sub
Public Sub McInstallFailedClearFolder(Loader)
Try
Thread.Sleep(1000) '防止存在尚未完全释放的文件,导致清理失败(例如整合包安装)
@@ -2053,26 +2053,12 @@ Retry:
'''
Public Function McInstallLoader(Request As McInstallRequest, Optional DontFixLibraries As Boolean = False) As List(Of LoaderBase)
'获取缓存目录
- Dim PathInstallTemp As String
- If PathTemp.Contains(" ") AndAlso (Request.OptiFineEntry IsNot Nothing OrElse Request.ForgeEntry IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing) Then
- PathInstallTemp = OsDrive & "ProgramData\PCL\Install\"
- Else
- PathInstallTemp = PathTemp & "Install\"
- End If
-
- '清理缓存
- Try
- If IsInstallTempCleared Then Exit Try
- IsInstallTempCleared = True
- DeleteDirectory(PathInstallTemp, True)
- Log("[Download] 已清理合并安装缓存")
- Catch ex As Exception
- Log(ex, "清理合并安装缓存失败")
- End Try
+ Dim TempMcFolder As String = RequestTaskTempFolder(
+ PathTemp.Contains(" ") AndAlso '要求路径不包含空格
+ (Request.OptiFineEntry IsNot Nothing OrElse Request.ForgeEntry IsNot Nothing OrElse Request.NeoForgeEntry IsNot Nothing))
'获取参数
Dim OutputFolder As String = PathMcFolder & "versions\" & Request.TargetVersionName & "\"
- Dim TempMcFolder As String = PathInstallTemp & Request.TargetVersionName.GetHashCode() & "\"
If Directory.Exists(TempMcFolder) Then DeleteDirectory(TempMcFolder)
Dim OptiFineFolder As String = Nothing
If Request.OptiFineVersion IsNot Nothing Then
@@ -2188,7 +2174,6 @@ Retry:
'总加载器
Return LoaderList
End Function
- Private IsInstallTempCleared As Boolean = False
'''
''' 将多个版本 Json 进行合并,如果目标已存在则直接覆盖。失败会抛出异常。
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
index 5ee8fd7..7a9b417 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
@@ -92,7 +92,6 @@
NewButton.LabText.Margin = New Thickness(-2, 0, 8, 0)
AddHandler NewButton.Check,
Sub(sender As MyRadioButton, raiseByMouse As Boolean)
- PanScroll.ScrollToHome()
VersionFilter = If(sender.Text = "全部", Nothing, sender.Text)
UpdateFilterResult()
End Sub
@@ -379,7 +378,7 @@
Sub()
'弹窗要求选择保存位置
Dim Target As String
- Target = SelectAs("选择保存位置", FileName,
+ Target = SelectSaveFile("选择保存位置", FileName,
Desc & "文件|" &
If(Project.Type = CompType.Mod,
If(File.FileName.EndsWith(".litemod"), "*.litemod", "*.jar"),
@@ -391,7 +390,7 @@
Dim Loaders As New List(Of LoaderBase)
Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
'启动
- Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
+ Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
Loader.Start(1)
LoaderTaskbarAdd(Loader)
FrmMain.BtnExtraDownload.ShowRefresh()
@@ -410,7 +409,7 @@
OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
End Sub
Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
- ClipboardSet(CompItem.LabTitle.Text)
+ ClipboardSet(CompItem.LabTitle.Text & CompItem.LabTitleRaw.Text)
End Sub
End Class
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
index 9b5a245..f6170d2 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadInstall.xaml.vb
@@ -64,9 +64,11 @@
End If
'如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
- For Each Card In GetAllAnimControls(PanSelect)
- Card.Opacity = 1
- Card.RenderTransform = New TranslateTransform
+ For Each Control In GetAllAnimControls(PanSelect)
+ Control.Opacity = 1
+ If Control.RenderTransform Is Nothing OrElse TypeOf Control.RenderTransform Is TranslateTransform Then
+ Control.RenderTransform = New TranslateTransform
+ End If
Next
'启动 Forge 加载
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb
index e860b13..6de3437 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/MySkin.xaml.vb
@@ -52,7 +52,7 @@
Exit Sub
End If
Try
- Dim FileAddress As String = SelectAs("选取保存皮肤的位置", GetFileNameFromPath(Address), "皮肤图片文件(*.png)|*.png")
+ Dim FileAddress As String = SelectSaveFile("选取保存皮肤的位置", GetFileNameFromPath(Address), "皮肤图片文件(*.png)|*.png")
If FileAddress.Contains("\") Then
File.Delete(FileAddress)
If Address.StartsWith(PathImage) Then
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
index 416bdcf..e0d56d8 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
@@ -53,10 +53,10 @@
'自动整合包安装
If PackInstallPath IsNot Nothing Then
Try
- Dim InstallLoader = ModpackInstall(PackInstallPath, GetFolderNameFromPath(Path))
+ Dim InstallLoader = ModpackInstall(PackInstallPath)
Log("[Launch] 自动安装整合包已开始:" & PackInstallPath)
InstallLoader.WaitForExit()
- If InstallLoader.State = LoadState.Finished Then
+ If InstallLoader.State = LoadState.Finished AndAlso File.Exists(PackInstallPath) Then
Log("[Launch] 自动安装整合包成功,删除安装包:" & PackInstallPath)
File.Delete(PackInstallPath)
End If
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
index 3a8643c..e727017 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchRight.xaml.vb
@@ -234,7 +234,7 @@ Download:
PanCustom.Children.Add(GetObjectFromXML(Content))
Catch ex As Exception
Log("[Page] 加载失败的自定义主页内容:" & vbCrLf & Content)
- If MyMsgBox($"自定义主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{ex.Message}", "加载自定义主页失败", "重试", "取消") = 1 Then
+ If MyMsgBox($"自定义主页内容编写有误,请根据下列错误信息进行检查:{vbCrLf}{GetExceptionSummary(ex)}", "加载自定义主页失败", "重试", "取消") = 1 Then
GoTo Refresh '防止 SyncLock 死锁
End If
End Try
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb
index e7ecc6c..29e865e 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLoginMsSkin.xaml.vb
@@ -113,6 +113,7 @@ Retry:
'获取新皮肤地址
Log("[Skin] 皮肤修改返回值:" & vbCrLf & Result)
Dim ResultJson As JObject = GetJson(Result)
+ If ResultJson.ContainsKey("errorMessage") Then Throw New Exception(ResultJson("errorMessage").ToString) '#5309
For Each Skin As JObject In ResultJson("skins")
If Skin("state").ToString = "ACTIVE" Then
MySkin.ReloadCache(Skin("url"))
diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
index 431ddfd..4b7051c 100644
--- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
+++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
@@ -104,120 +104,195 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb
index 2b5177a..c24e34a 100644
--- a/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageSelectLeft.xaml.vb
@@ -171,6 +171,9 @@
Log(ex, "添加文件夹失败(" & NewFolder & ")", LogLevel.Feedback)
End Try
End Sub
+ '''
+ ''' 将指定文件夹添加到 Minecraft 文件夹列表,并选中它。
+ '''
Public Shared Sub AddFolder(FolderPath As String, DisplayName As String, ShowHint As Boolean)
RunInThread(
Sub()
@@ -328,23 +331,24 @@
Next
Setup.Set("LaunchFolders", If(Not Folders.Any(), "", Join(Folders.ToArray, "|")))
End If
- RunInNewThread(Sub()
- '删除文件夹
- Try
- Hint("正在" & DeleteText & "文件夹 " & Folder.Name & "!", HintType.Info)
- DeleteDirectory(Folder.Path)
- If DeleteText = "清空" Then Directory.CreateDirectory(Folder.Path)
- Hint("已" & DeleteText & "文件夹 " & Folder.Name & "!", HintType.Finish)
- Catch ex As Exception
- Log(ex, DeleteText & "文件夹 " & Folder.Name & " 失败", LogLevel.Hint)
- Finally
- '刷新列表
- McFolderListLoader.Start(IsForceRestart:=True)
- End Try
- End Sub, "Folder Delete " & GetUuid(), ThreadPriority.BelowNormal)
+ RunInNewThread(
+ Sub()
+ '删除文件夹
+ Try
+ Hint("正在" & DeleteText & "文件夹 " & Folder.Name & "!", HintType.Info)
+ DeleteDirectory(Folder.Path)
+ If DeleteText = "清空" Then Directory.CreateDirectory(Folder.Path)
+ Hint("已" & DeleteText & "文件夹 " & Folder.Name & "!", HintType.Finish)
+ Catch ex As Exception
+ Log(ex, DeleteText & "文件夹 " & Folder.Name & " 失败", LogLevel.Hint)
+ Finally
+ '刷新列表
+ McFolderListLoader.Start(IsForceRestart:=True)
+ End Try
+ End Sub, "Folder Delete " & GetUuid(), ThreadPriority.BelowNormal)
End Sub
Public Sub Open_Click(sender As Object, e As RoutedEventArgs)
- OpenExplorer("""" & CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Info & """")
+ OpenExplorer(CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Info)
End Sub
Public Sub Refresh_Click(sender As Object, e As RoutedEventArgs)
Dim Data As McFolder = CType(CType(CType(sender.Parent, ContextMenu).Parent, Primitives.Popup).PlacementTarget, MyListItem).Tag
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb
index 6dbe50c..b23290e 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb
+++ b/Plain Craft Launcher 2/Pages/PageSetup/ModSetup.vb
@@ -20,11 +20,13 @@
{"HintBuy", New SetupEntry(False, Source:=SetupSource.Registry)},
{"HintClearRubbish", New SetupEntry(0, Source:=SetupSource.Registry)},
{"HintUpdateMod", New SetupEntry(False, Source:=SetupSource.Registry)},
+ {"HintCustomCommand", New SetupEntry(False, Source:=SetupSource.Registry)},
{"TestSetupReader", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)},
{"SystemEula", New SetupEntry(False, Source:=SetupSource.Registry)},
{"SystemCount", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
{"SystemLaunchCount", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
{"SystemLastVersionReg", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
+ {"SystemHighestSavedBetaVersionReg", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
{"SystemHighestBetaVersionReg", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
{"SystemHighestAlphaVersionReg", New SetupEntry(0, Source:=SetupSource.Registry, Encoded:=True)},
{"SystemSetupVersionReg", New SetupEntry(VersionSetup, Source:=SetupSource.Registry)},
@@ -37,6 +39,7 @@
{"SystemSystemCache", New SetupEntry("", Source:=SetupSource.Registry)},
{"SystemSystemUpdate", New SetupEntry(0)},
{"SystemSystemActivity", New SetupEntry(0)},
+ {"CacheExportConfig", New SetupEntry("", Source:=SetupSource.Registry)},
{"CacheSavedPageUrl", New SetupEntry("", Source:=SetupSource.Registry)},
{"CacheSavedPageVersion", New SetupEntry("", Source:=SetupSource.Registry)},
{"CacheMsOAuthRefresh", New SetupEntry("", Source:=SetupSource.Registry, Encoded:=True)},
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb
index a30267b..c36c123 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml.vb
@@ -165,7 +165,7 @@
Private Sub BtnSkinDelete_Click(sender As Object, e As EventArgs) Handles BtnSkinDelete.Click
Try
File.Delete(PathAppdata & "CustomSkin.png")
- RadioSkinType0.SetChecked(True, True, True)
+ RadioSkinType0.SetChecked(True, True)
Hint("离线皮肤已清空!", HintType.Finish)
Catch ex As Exception
Log(ex, "清空离线皮肤失败", LogLevel.Msgbox)
@@ -194,11 +194,11 @@
If LabRamGame Is Nothing OrElse LabRamUsed Is Nothing OrElse FrmMain.PageCurrent <> FormMain.PageType.Setup OrElse FrmSetupLeft.PageID <> FormMain.PageSubType.SetupLaunch Then Exit Sub
'获取内存情况
Dim RamGame As Double = GetRam(McVersionCurrent, False)
- Dim RamTotal As Double = Math.Round(My.Computer.Info.TotalPhysicalMemory / 1024 / 1024 / 1024 * 10) / 10
- Dim RamAvailable As Double = Math.Round(My.Computer.Info.AvailablePhysicalMemory / 1024 / 1024 / 1024 * 10) / 10
- Dim RamGameActual As Double = Math.Min(RamGame, RamAvailable)
- Dim RamUsed As Double = RamTotal - RamAvailable
- Dim RamEmpty As Double = Math.Round(MathClamp(RamTotal - RamUsed - RamGame, 0, 1000) * 10) / 10
+ Dim RamTotal As Double = Math.Round(My.Computer.Info.TotalPhysicalMemory / 1024 / 1024 / 1024, 1)
+ Dim RamAvailable As Double = Math.Round(My.Computer.Info.AvailablePhysicalMemory / 1024 / 1024 / 1024, 1)
+ Dim RamGameActual As Double = Math.Round(Math.Min(RamGame, RamAvailable), 5)
+ Dim RamUsed As Double = Math.Round(RamTotal - RamAvailable, 5)
+ Dim RamEmpty As Double = Math.Round(MathClamp(RamTotal - RamUsed - RamGame, 0, 1000), 1)
'设置最大可用内存
If RamTotal <= 1.5 Then
SliderRamCustom.MaxValue = Math.Max(Math.Floor((RamTotal - 0.3) / 0.1), 1)
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml.vb
index 55e5b6a..73fa03e 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLink.xaml.vb
@@ -45,11 +45,6 @@
End Sub
Private Sub BtnHiperLog_Click(sender As Object, e As EventArgs) Handles BtnHiperLog.Click
- If File.Exists(PageLinkHiper.PathHiper & "logs\hiper.log") Then
- OpenExplorer("/select,""" & PageLinkHiper.PathHiper & "logs\hiper.log""")
- Else
- Hint("没有找到 HiPer 联机模块的日志!")
- End If
End Sub
End Class
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml
index f05936f..3a4cc1f 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml
@@ -179,7 +179,7 @@
-
+
@@ -247,7 +247,7 @@
-
+
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb
index 862c701..1f697f4 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupUI.xaml.vb
@@ -189,7 +189,7 @@
'背景图片
Private Sub BtnUIBgOpen_Click(sender As Object, e As EventArgs) Handles BtnBackgroundOpen.Click
- OpenExplorer("""" & Path & "PCL\Pictures""")
+ OpenExplorer(Path & "PCL\Pictures\")
End Sub
Private Sub BtnBackgroundRefresh_Click(sender As Object, e As EventArgs) Handles BtnBackgroundRefresh.Click
BackgroundRefresh(True, True)
@@ -335,7 +335,7 @@ Refresh:
Private Sub BtnLogoDelete_Click(sender As Object, e As EventArgs) Handles BtnLogoDelete.Click
Try
File.Delete(Path & "PCL\Logo.png")
- RadioLogoType1.SetChecked(True, True, True)
+ RadioLogoType1.SetChecked(True, True)
Hint("标题栏图片已清空!", HintType.Finish)
Catch ex As Exception
Log(ex, "清空标题栏图片失败", LogLevel.Msgbox)
@@ -344,7 +344,7 @@ Refresh:
'背景音乐
Private Sub BtnMusicOpen_Click(sender As Object, e As EventArgs) Handles BtnMusicOpen.Click
- OpenExplorer("""" & Path & "PCL\Musics""")
+ OpenExplorer(Path & "PCL\Musics\")
End Sub
Private Sub BtnMusicRefresh_Click(sender As Object, e As EventArgs) Handles BtnMusicRefresh.Click
MusicRefreshPlay(True)
@@ -407,7 +407,7 @@ Refresh:
End If
WriteFile(Path & "PCL\Custom.xaml", GetResources("Custom"))
Hint("教学文件已生成!", HintType.Finish)
- OpenExplorer("/select,""" & Path & "PCL\Custom.xaml""")
+ OpenExplorer(Path & "PCL\Custom.xaml")
Catch ex As Exception
Log(ex, "生成教学文件失败", LogLevel.Feedback)
End Try
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml
index 8e1a2ab..11ca11c 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml
@@ -5,117 +5,267 @@
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" x:Class="PageVersionExport"
PanScroll="{Binding ElementName=PanBack}">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml.vb
index 13b514e..e01176e 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionExport.xaml.vb
@@ -1,346 +1,719 @@
-Public Class PageVersionExport
- Private CurrentVer As String = ""
- Private Sub PageVersionExport_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
- Reload()
- End Sub
+Imports System.IO.Compression
- Public ItemVersion As MyListItem
- Public Sub Reload()
+Public Class ExportOption
+ Public Property Title As String
+ Public Property Description As String
+ Public Property Rules As String
+ '''
+ ''' 如果 Rules 为空,则根据 ShowRules 的内容判断是否应该显示这个复选框。
+ ''' 如果 ShowRules 也为空,则始终显示。
+ '''
+ Public Property ShowRules As String
+ Public Property DefaultChecked As Boolean
+ Public Property RequireModLoader As Boolean = False
+ Public Property RequireOptiFine As Boolean = False
+ Public Property RequireModLoaderOrOptiFine As Boolean = False
+End Class
+
+Public Class PageVersionExport
+ Implements IRefreshable
+
+ Private CurrentVersion As String = ""
+ Private Sub PageVersionExport_Loaded() Handles Me.Loaded
AniControlEnabled += 1
-
- If CurrentVer <> PageVersionLeft.Version.Path Then
- '说明切换到另一个版本了,所有的设置都要重置
- CurrentPath = ""
- Selected = New List(Of String)
- CurrentVer = PageVersionLeft.Version.Path
-
- TbExportName.Text = ""
- TbExportVersion.Text = ""
- 'ComboDisplayLogo.SelectedIndex = 0
- CustomLogoVal = Nothing
- TbExportDesc.Text = ""
- End If
-
- TbExportName.ShowValidateResult = True
- TbExportName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFileName() With {.AllowNull = True}}
- TbExportName.Validate()
-
- TbExportName.HintText = PageVersionLeft.Version.Name
- TbExportDesc.HintText = PageVersionLeft.Version.Info
- PanDisplayItem.Children.Clear()
- ItemVersion = PageSelectRight.McVersionListItem(PageVersionLeft.Version.Load())
- ItemVersion.IsHitTestVisible = False
- If Not String.IsNullOrEmpty(CustomLogo) Then ItemVersion.Logo = CustomLogo
- PanDisplayItem.Children.Add(ItemVersion)
- ItemVersion.Title = If(String.IsNullOrWhiteSpace(TbExportName.Text), PageVersionLeft.Version.Name, TbExportName.Text)
- ItemVersion.Info = If(String.IsNullOrWhiteSpace(TbExportDesc.Text), PageVersionLeft.Version.Info, TbExportDesc.Text)
-#If BETA Then
- CheckIncludePCL.IsEnabled = True
-#Else
- CheckIncludePCL.IsEnabled = False
- TbHint.Text = "你可以在此勾选需要包含进整合包的文件或文件夹,其他文件或文件夹在高级选项添加。" & vbCrLf & "你当前的 PCL 不是正式版,有二次分发的限制,因此整合包不能包含 PCL 程序。"
-#End If
- CheckIncludeSetup.IsEnabled = CheckIncludePCL.IsEnabled
- ReloadFileList(CurrentPath)
-
+ If CurrentVersion <> PageVersionLeft.Version.Path Then RefreshAll() '切换到了另一个版本,重置页面
+ BtnAdvancedHelp.EventData = If(VersionBranchName = "Release", "指南/整合包制作 - Public.json", "指南/整合包制作 - Snapshot.json")
AniControlEnabled -= 1
End Sub
+ Public Sub RefreshAll() Implements IRefreshable.Refresh
+ Log($"[Export] 刷新导出页面")
+ HintOptiFine.Visibility = If(PageVersionLeft.Version.Version.HasOptiFine, Visibility.Visible, Visibility.Collapsed)
+ CurrentVersion = PageVersionLeft.Version.Path
+ TextExportName.Text = ""
+ TextExportName.HintText = PageVersionLeft.Version.Name
+ TextExportVersion.Text = ""
+ TextExportVersion.HintText = "1.0.0"
+ GetExportOption(CheckOptionsBasic).Description = PageVersionLeft.Version.GetDefaultDescription()
+ ResetConfigOverrides()
+ ReloadAllSubOptions()
+ RefreshAllOptionsUI()
+ PanBack.ScrollToHome()
+ End Sub
+
+#Region "子选项"
+ Private SubOptionBlackList As String() = {"Quark Programmer Art.zip", "+ EuphoriaPatches_"}
-#Region "文件列表"
'''
- ''' 当前所在的文件夹。注意是绝对路径!以 \ 结尾。
+ ''' 动态生成子文件夹下的选项,例如资源包、存档等。
'''
- Private CurrentPath As String = ""
- Private Sub ReloadFileList(Optional NewPath As String = "")
- If String.IsNullOrWhiteSpace(NewPath) OrElse NewPath = PathMcFolder Then NewPath = PageVersionLeft.Version.Path
- If Not NewPath.EndsWithF("\") Then NewPath &= "\"
- Dim tb As TextBlock = PanFileList.Children(0)
- PanFileList.Children.Clear()
- PanFileList.Children.Add(tb)
+ Private Sub ReloadAllSubOptions()
+ ReloadSubOptions(PanOptionsResourcePacks, True, True, "resourcepacks", "texturepacks")
+ ReloadSubOptions(PanOptionsSaves, False, True, "saves")
+ ReloadSubOptions(PanOptionsShaderPacks, True, True, "shaderpacks")
+ End Sub
- If NewPath <> PageVersionLeft.Version.Path Then
- Dim listItem As New MyListItem With {.Title = "返回上一级目录", .Tag = "..", .Height = 35, .Type = MyListItem.CheckType.Clickable}
- AddHandler listItem.Click, AddressOf ListItem_Click
- PanFileList.Children.Add(listItem)
- End If
-
- Dim fileCount As Integer = 0
- Dim directories = Directory.EnumerateDirectories(NewPath)
- Dim files = Directory.EnumerateFiles(NewPath)
- If (NewPath = PageVersionLeft.Version.Path) AndAlso PageVersionLeft.Version.PathIndie = PathMcFolder Then
- '未开启版本隔离时 .minecraft 下的文件
- directories = directories.Concat(Directory.EnumerateDirectories(PathMcFolder))
- files = files.Concat(Directory.EnumerateFiles(PathMcFolder))
- End If
-
- For Each d In directories
- If Directory.EnumerateFileSystemEntries(d).Count = 0 Then Continue For
- If IsVerRedundant(d, PageVersionLeft.Version.PathIndie) OrElse IsMustExport(d, PageVersionLeft.Version.PathIndie) Then Continue For
- Dim title As String = ""
- Dim listItem As New MyListItem
- If Titles.TryGetValue(GetFolderNameFromPath(d), title) Then 'title 作为引用类型传入
- listItem.Title = title
- Else
- listItem.Title = GetFolderNameFromPath(d)
+ Private Sub ReloadSubOptions(Panel As StackPanel, AcceptCompressedFile As Boolean, AcceptFolder As Boolean, ParamArray Folders As String())
+ Panel.Children.Clear()
+ For Each Folder In Folders
+ Dim TargetFolder As New DirectoryInfo(PageVersionLeft.Version.PathIndie & Folder)
+ If Not TargetFolder.Exists() Then Continue For
+ '查找文件夹下的对应项
+ If AcceptCompressedFile Then
+ For Each File In TargetFolder.EnumerateFiles("*.zip").Concat(TargetFolder.EnumerateFiles("*.rar"))
+ If SubOptionBlackList.Any(Function(b) File.Name.ContainsF(b)) Then Continue For
+ Panel.Children.Add(New MyCheckBox With {. _
+ Tag = New ExportOption With {.Title = File.Name, .DefaultChecked = True, .Rules = EscapeLikePattern($"{Folder}/{File.Name}")}})
+ Next
End If
- listItem.Info = d
- '图标按钮
- Dim btnSwap As New MyIconButton
- btnSwap.Logo = Logo.IconButtonRight
- btnSwap.LogoScale = 1.0
- btnSwap.Tag = d & "\"
- AddHandler btnSwap.Click, AddressOf BtnSwap_Click
- listItem.Buttons = {btnSwap}
- listItem.Type = MyListItem.CheckType.CheckBox
- listItem.Height = 35
- listItem.Logo = Logo.IconButtonOpen
- listItem.Tag = d & "\"
- Select Case IsSelected(d & "\")
- Case 1
- listItem.Half = True
- Case 2
- listItem.Checked = True
- End Select
- AddHandler listItem.Click, AddressOf ListItem_Click
- PanFileList.Children.Add(listItem)
- fileCount += 1
- Next
-
- For Each f In files
- If IsVerRedundant(f, PageVersionLeft.Version.PathIndie) OrElse IsMustExport(f, PageVersionLeft.Version.PathIndie) Then Continue For
- Dim title As String = ""
- Dim listItem As New MyListItem
- If Titles.TryGetValue(f, title) Then 'title 作为引用类型传入
- listItem.Title = title
- Else
- listItem.Title = GetFileNameFromPath(f)
- End If
- listItem.Info = f
- listItem.Type = MyListItem.CheckType.CheckBox
- listItem.Height = 35
- listItem.Tag = f
- listItem.Checked = (IsSelected(f) = 2)
- AddHandler listItem.Click, AddressOf ListItem_Click
- PanFileList.Children.Add(listItem)
- fileCount += 1
- Next
-
- CurrentPath = NewPath
- CardMore.Title = If(NewPath = PageVersionLeft.Version.Path, "高级选项", $"高级选项 ({NewPath.Replace(PageVersionLeft.Version.Path, "").TrimEnd("\")})")
- End Sub
- Private Titles As New Dictionary(Of String, String) From {
- {"saves", "存档"},
- {"resourcepacks", "资源包"},
- {"config", "Mod 配置"},
- {"shaderpacks", "光影包"},
- {"options.txt", "游戏配置"},
- {"servers.dat", "服务器列表"}
- }
- Private Sub BtnSwap_Click(sender As MyIconButton, e As Object)
- Dim actualPath As String = sender.Tag
- 'Dim relative As String = actualPath.Replace(PageVersionLeft.Version.Path, "")
-
- If Not Directory.Exists(actualPath) Then
- Hint($"找不到目录:{actualPath}", HintType.Critical)
- Exit Sub
- End If
- ReloadFileList(actualPath)
- End Sub
- '''
- ''' 选中的文件列表,绝对路径。
- '''
- Private Selected As New List(Of String)
- Private Sub SelectedChange(IsAdd As Boolean, TargetPath As String)
- If File.Exists(TargetPath) Then
- If IsAdd Then Selected.Add(TargetPath) Else Selected.Remove(TargetPath)
- Exit Sub
- End If
- If Directory.Exists(TargetPath) Then
- For Each d In Directory.EnumerateDirectories(TargetPath)
- SelectedChange(IsAdd, d)
- Next
- For Each f In Directory.EnumerateFiles(TargetPath)
- If IsAdd Then Selected.Add(f) Else Selected.Remove(f)
- Next
- End If
- Selected = Selected.Distinct().ToList()
- End Sub
- '''
- ''' 获取 TargetPath 是否选中。
- '''
- ''' 全部未选中为 0,部分选中为 1,全部选中为 2。
- Private Function IsSelected(TargetPath As String, Optional AllowEmpty As Boolean = False) As Integer
- If File.Exists(TargetPath) Then
- Return If(Selected.Contains(TargetPath), 2, 0)
- End If
- If Directory.Exists(TargetPath) Then
- Dim di As New DirectoryInfo(TargetPath)
- If di.GetDirectories.Length + di.GetFiles.Length = 0 Then Return If(AllowEmpty, 2, 0)
- '如果有一个文件不在列表,则 hasExclude = True
- Dim hasExclude As Boolean = False
- '如果有一个文件在列表,则 hasInclude = True
- Dim hasInclude As Boolean = False
- For Each d In Directory.EnumerateDirectories(TargetPath)
- If Directory.EnumerateFileSystemEntries(d).Count = 0 Then Continue For
- Select Case IsSelected(d, True)
- Case 0
- hasExclude = True
- Case 1
- Return 1
- Case 2
- hasInclude = True
- End Select
- If hasInclude AndAlso hasExclude Then Return 1 '如果有文件在、有文件不在列表中,可以断定部分选中
- Next
- For Each f In Directory.EnumerateFiles(TargetPath)
- If Selected.Contains(f) Then hasInclude = True Else hasExclude = True
- If hasInclude AndAlso hasExclude Then Return 1 '如果有文件在、有文件不在列表中,可以断定部分选中
- Next
- If hasExclude AndAlso Not hasInclude Then Return 0
- If hasInclude AndAlso Not hasExclude Then Return 2
- End If
- Return 0
- End Function
- Private Sub ListItem_Click(sender As MyListItem, e As Object)
- If sender.Tag = ".." Then '回到上一级目录
- ReloadFileList(GetPathFromFullPath(CurrentPath))
- Exit Sub
- End If
- SelectedChange(Not sender.Checked, '非常难绷的逻辑,很难描述清楚,自己打断点看 Checked 属性值吧……
- sender.Tag)
- End Sub
-
- 'Private ReadOnly Property Saves As List(Of String)
- ' Get
- ' If Directory.Exists(PageVersionLeft.Version.Path & "saves\") Then
- ' Return Directory.EnumerateDirectories(PageVersionLeft.Version.Path & "saves\").ToList
- ' End If
- ' Return New List(Of String)
- ' End Get
- 'End Property
-
- '复选框与高级选项同步
- Private Sub PanCommonFiles_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles PanCommonFiles.MouseLeftButtonUp
- For Each c In PanCommonFiles.Children
- If String.IsNullOrEmpty(c.Tag) Then Continue For
- For Each l In PanFileList.Children
- If TypeOf l Is MyListItem Then
- If If(l.Tag.EndsWith("\"), GetFolderNameFromPath(l.Tag), GetFileNameFromPath(l.Tag)) = c.Tag Then
- l.Checked = c.Checked
- SelectedChange(c.Checked, PageVersionLeft.Version.Path & c.Tag)
- End If
- End If
- Next
- Next
- End Sub
- '高级选项与复选框同步
- Private Sub PanFileList_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles PanFileList.MouseLeftButtonUp
- For Each c In PanCommonFiles.Children
- If TypeOf c Is MyCheckBox Then
- c.Checked = IsSelected(PageVersionLeft.Version.Path & c.Tag) = 2 OrElse IsSelected(PathMcFolder & c.Tag) = 2
+ If AcceptFolder Then
+ For Each SubFolder In TargetFolder.EnumerateDirectories().OrderByDescending(Function(f) f.LastWriteTime)
+ If SubOptionBlackList.Any(Function(b) SubFolder.Name.ContainsF(b)) Then Continue For
+ If Not SubFolder.EnumerateFileSystemInfos().Any() Then Continue For
+ Dim NewCheckBox As New MyCheckBox With {. _
+ Tag = New ExportOption With {.Title = SubFolder.Name, .DefaultChecked = True, .Rules = EscapeLikePattern($"{Folder}/{SubFolder.Name}/")}}
+ If Panel Is PanOptionsSaves Then GetExportOption(NewCheckBox).Description = SubFolder.LastWriteTime.ToString("yyyy'/'MM'/'dd HH':'mm")
+ Panel.Children.Add(NewCheckBox)
+ Next
End If
Next
End Sub
+
#End Region
-#Region "整合包信息 & 预览栏"
- Private Sub TbExportName_TextChanged(sender As MyTextBox, e As TextChangedEventArgs) Handles TbExportName.TextChanged
- If String.IsNullOrEmpty(TbExportName.ValidateResult) Then
- ItemVersion.Title = If(String.IsNullOrWhiteSpace(sender.Text), PageVersionLeft.Version.Name, sender.Text)
- End If
- End Sub
+#Region "选项"
- Private Sub TbExportDesc_TextChanged(sender As MyTextBox, e As TextChangedEventArgs) Handles TbExportDesc.TextChanged
- ItemVersion.Info = If(String.IsNullOrWhiteSpace(sender.Text), PageVersionLeft.Version.Info, sender.Text)
- End Sub
-
- Private CustomLogoVal As String
'''
- ''' 自定义 Logo 的路径。
+ ''' 重新确认是否应该显示每个选项,并将 ExportOption 同步到 UI。
'''
- Public Property CustomLogo() As String
+ Private Sub RefreshAllOptionsUI()
+ '预先归纳所有至多二级的文件/文件夹
+ Dim AllEntries As New List(Of String)
+ Dim IsValidDirectory = '检查文件夹不为空
+ Function(Folder As DirectoryInfo) As Boolean
+ Try
+ Return Folder.Exists AndAlso
+ Folder.EnumerateFileSystemInfos().Any(Function(i) Not SubOptionBlackList.Any(Function(b) i.Name.ContainsF(b)))
+ Catch '一般是由于无法访问,或是一个指向已不存在的文件夹的链接(例如使用 mklink 创造的 resource 文件夹链接)
+ Return False
+ End Try
+ End Function
+ Dim PathInfo As New DirectoryInfo(PageVersionLeft.Version.PathIndie)
+ AllEntries.AddRange(PathInfo.EnumerateFiles().Select(Function(f) f.Name))
+ For Each SubFolder In PathInfo.EnumerateDirectories().Where(IsValidDirectory)
+ AllEntries.Add($"{SubFolder.Name}\")
+ AllEntries.AddRange(SubFolder.EnumerateFiles().Select(Function(f) $"{SubFolder.Name}\{f.Name}"))
+ AllEntries.AddRange(SubFolder.EnumerateDirectories().Where(IsValidDirectory).Select(Function(d) $"{SubFolder.Name}\{d.Name}\"))
+ Next
+ Log($"[Export] 共发现 {AllEntries.Count} 个可行的二级文件/文件夹")
+ '确认选项是否应该被显示
+ Dim IsVisible =
+ Function(TargetOption As ExportOption) As Boolean
+ '检查需要 OptiFine 或 Mod 加载器
+ If TargetOption.RequireOptiFine AndAlso Not PageVersionLeft.Version.Version.HasOptiFine Then Return False
+ If TargetOption.RequireModLoader AndAlso Not PageVersionLeft.Version.Modable Then Return False
+ If TargetOption.RequireModLoaderOrOptiFine AndAlso Not PageVersionLeft.Version.Version.HasOptiFine AndAlso Not PageVersionLeft.Version.Modable Then Return False
+ '粗略检查是否可能有符合规则的文件/文件夹
+ Return StandardizeLines(If(TargetOption.Rules, TargetOption.ShowRules).Split("|"c), True).Any(
+ Function(Rule As String)
+ If Rule.StartsWithF("!") Then Return False '只看正向规则
+ '检查前两级
+ Try
+ If AllEntries.Any(Function(Entry) Entry Like Rule) Then Return True
+ Catch ex As Exception
+ Log(ex, $"错误的规则:{Rule}", LogLevel.Hint)
+ Return False
+ End Try
+ '粗略检查所有级
+ Rule = Rule.Trim("*?".ToCharArray)
+ If Rule.Split({"\"c}, StringSplitOptions.RemoveEmptyEntries).Count >= 3 Then
+ If Rule.EndsWithF("\") Then
+ Return IsValidDirectory(New DirectoryInfo(PageVersionLeft.Version.PathIndie & Rule)) '文件夹有效
+ Else
+ Return File.Exists(PageVersionLeft.Version.PathIndie & Rule) '文件有效
+ End If
+ Else
+ Return False
+ End If
+ End Function)
+ End Function
+ '逐个检查选项
+ For Each CheckBox In GetAllOptions(True)
+ Dim TargetOption = GetExportOption(CheckBox)
+ '名称与简介
+ CheckBox.Inlines.Clear()
+ CheckBox.Inlines.Add(New Run(TargetOption.Title))
+ If Not String.IsNullOrEmpty(TargetOption.Description) Then
+ CheckBox.Inlines.Add(New Run(" " & TargetOption.Description) With {.Foreground = ColorGray5})
+ End If
+ '可见性、默认勾选
+ If String.IsNullOrEmpty(TargetOption.Rules) AndAlso String.IsNullOrEmpty(TargetOption.ShowRules) Then
+ CheckBox.Visibility = Visibility.Visible
+ CheckBox.Checked = TargetOption.DefaultChecked
+ Else
+ Dim Pass As Boolean = IsVisible(TargetOption)
+ CheckBox.Visibility = If(Pass, Visibility.Visible, Visibility.Collapsed)
+ CheckBox.Checked = TargetOption.DefaultChecked AndAlso Pass
+ End If
+ Next
+ End Sub
+
+ '''
+ ''' 对文本行进行标准化处理,以便使用 Like 进行匹配。
+ '''
+ Private Iterator Function StandardizeLines(Raw As IEnumerable(Of String), AddSuffixStarToFolderPath As Boolean) As IEnumerable(Of String)
+ For Each IgnoreLine In Raw
+ IgnoreLine = IgnoreLine.Trim
+ If IgnoreLine = "" OrElse IgnoreLine.StartsWithF("#") OrElse IgnoreLine.StartsWithF("=") Then Continue For
+ IgnoreLine = IgnoreLine.Replace("/", "\")
+ Yield IgnoreLine & If(IgnoreLine.EndsWithF("\") AndAlso AddSuffixStarToFolderPath, "*", "")
+ Next
+ End Function
+
+ '''
+ ''' 获取所有可作为选项的 CheckBox。
+ '''
+ Private Iterator Function GetAllOptions(IncludeHidden As Boolean) As IEnumerable(Of MyCheckBox)
+ For Each Element In PanOptions.Children
+ If Not IncludeHidden AndAlso Element.Visibility <> Visibility.Visible Then Continue For
+ If TypeOf Element Is MyCheckBox Then
+ Yield Element
+ ElseIf TypeOf Element Is StackPanel Then
+ For Each SubElement In DirectCast(Element, StackPanel).Children
+ If Not IncludeHidden AndAlso SubElement.Visibility <> Visibility.Visible Then Continue For
+ If TypeOf SubElement Is MyCheckBox Then Yield SubElement
+ Next
+ End If
+ Next
+ End Function
+
+ '''
+ ''' 获取该 CheckBox 对应的 ExportOption。
+ '''
+ Private Function GetExportOption(CheckBox As MyCheckBox) As ExportOption
+ Return CType(CheckBox.Tag, ExportOption)
+ End Function
+
+#End Region
+
+#Region "配置文件"
+ Private Const Sperator As String = "=============================================================="
+
+ ' ================ 导出内容段 ================
+
+ '''
+ ''' 从配置文件中读取的规则。
+ ''' 如果不为 Nothing,则会覆写当前勾选的规则并禁用对应 UI。
+ '''
+ Private Property RulesOverrides As List(Of String)
Get
- Return CustomLogoVal
+ Return _RulesOverrides
End Get
- Set(value As String)
- If AniControlEnabled <> 0 Then Exit Property
- CustomLogoVal = value
- ItemVersion.Logo = value
- Reload()
+ Set(value As List(Of String))
+ _RulesOverrides = value
+ If value Is Nothing Then
+ BtnOverrideCancel.Visibility = Visibility.Collapsed
+ PanOptions.Visibility = Visibility.Visible
+ CardOptions.Inlines.Clear()
+ CardOptions.Inlines.Add(New Run("导出内容列表") With {.FontWeight = FontWeights.Bold})
+ Else
+ BtnOverrideCancel.Visibility = Visibility.Visible
+ PanOptions.Visibility = Visibility.Collapsed
+ CardOptions.Inlines.Clear()
+ CardOptions.Inlines.Add(New Run("导出内容列表: ") With {.FontWeight = FontWeights.Bold})
+ CardOptions.Inlines.Add(New Run("从配置文件中读取") With {.FontWeight = FontWeights.Normal})
+ End If
End Set
End Property
+ Private _RulesOverrides As List(Of String) = Nothing
+ '''
+ ''' 获取当前实际生效的所有规则。
+ '''
+ Private Iterator Function GetAllRules() As IEnumerable(Of String)
+ If RulesOverrides IsNot Nothing Then
+ '返回覆盖的列表
+ For Each Rule In RulesOverrides
+ Yield Rule
+ Next
+ Else
+ '从当前勾选的所有选项中获取所有规则行
+ Yield ""
+ Yield "# 修改下方的规则以控制需要导出的内容。"
+ Yield "# 以 ! 开头以反选。可以使用 *、?、[] 通配符。靠后的行覆盖靠前的。"
+ Yield ""
+ For Each CheckBox In GetAllOptions(False)
+ If Not CheckBox.Checked Then Continue For
+ Dim TargetOption = GetExportOption(CheckBox)
+ If TargetOption.Rules Is Nothing Then Continue For
+ Yield $"# {TargetOption.Title}"
+ For Each Rule In TargetOption.Rules.Split("|"c)
+ Yield Rule
+ Next
+ Yield ""
+ Next
+ Yield "# 排除的文件"
+ Yield "!*.log"
+ Yield "!*.dat_old"
+ Yield "!*.BakaCoreInfo"
+ Yield ""
+ End If
+ End Function
- 'Private Sub ComboDisplayLogo_SelectionChanged(sender As MyComboBox, e As SelectionChangedEventArgs) Handles ComboDisplayLogo.SelectionChanged
- ' If Not AniControlEnabled = 0 Then Exit Sub
- ' '选择 自定义 时修改图片
- ' If ComboDisplayLogo.SelectedItem Is ItemDisplayLogoCustom Then
- ' Dim FileName As String = SelectFile("常用图片文件(*.png;*.jpg;*.gif)|*.png;*.jpg;*.gif", "选择图片")
- ' If String.IsNullOrWhiteSpace(FileName) Then Exit Sub
- ' CustomLogo = FileName
- ' Else
- ' CustomLogo = sender.SelectedItem.Tag
- ' End If
- 'End Sub
-#End Region
+ ' ================ 追加内容段 ================
-#Region "基本信息"
- Private Sub CheckIncludePCL_Change(sender As Object, user As Boolean) Handles CheckIncludePCL.Change
- CheckIncludeSetup.IsEnabled = CheckIncludePCL.IsEnabled
+ Private ExtraFiles As List(Of String) = Nothing
+ '''
+ ''' 获取当前实际生效的追加内容。
+ '''
+ Private Iterator Function GetExtraFileLines() As IEnumerable(Of String)
+ If ExtraFiles IsNot Nothing Then
+ '返回覆盖的列表
+ For Each File In ExtraFiles
+ Yield File
+ Next
+ Else
+ '从当前勾选的所有选项中获取所有规则行
+ Yield ""
+ Yield "# 如果想将额外的文件自动放到压缩包根目录中,可以将它们的路径写在下方。"
+ Yield "# 必须是完整路径。每行中,若以 \ 结尾则代表是文件夹,不以 \ 结尾则代表是文件。"
+ Yield ""
+ End If
+ End Function
+
+ ' ================ 重置 ================
+
+ '''
+ ''' 重置配置文件所带来的影响。
+ '''
+ Private Sub ResetConfigOverrides()
+ RulesOverrides = Nothing
+ ConfigPackPath = Nothing
+ ExtraFiles = Nothing
+ PanBack.ScrollToHome()
End Sub
+ Private Sub CardOptions_MouseLeftButtonDown() Handles CardOptions.MouseLeftButtonDown
+ If RulesOverrides Is Nothing Then Return
+ ResetConfigOverrides()
+ End Sub
+
+ ' ================ 保存 / 读取 ================
+
+ '保存配置文件
+ Private Sub ExportConfig() Handles BtnAdvancedExport.Click
+ Try
+ Dim ConfigPath As String = SelectSaveFile("选择文件位置", "export_config.txt", "整合包导出配置(*.txt)|*.txt", Setup.Get("CacheExportConfig"))
+ If String.IsNullOrEmpty(ConfigPath) Then Return
+ Setup.Set("CacheExportConfig", ConfigPath)
+ Dim ConfigLines As New List(Of String)
+ 'ini 段
+ ConfigLines.Add("Name:" & TextExportName.Text)
+ ConfigLines.Add("Version:" & TextExportVersion.Text)
+ ConfigLines.Add("")
+ ConfigLines.Add("# 是否打包正式版 PCL,以便没有启动器的玩家安装整合包。")
+ ConfigLines.Add("IncludeLauncher:" & CheckOptionsPcl.Checked)
+ ConfigLines.Add("")
+ ConfigLines.Add("# 是否打包 PCL 个性化内容,例如功能隐藏设置、主页、背景音乐和图片等。")
+ ConfigLines.Add("IncludeLauncherCustom:" & CheckOptionsPclCustom.Checked)
+ ConfigLines.Add("")
+ ConfigLines.Add("# 是否将 Mod、资源包、光影包的文件直接放入整合包中,这样在导入时就无需联网下载它们。")
+ ConfigLines.Add("# 建议仅在无法稳定连接 CurseForge 或 Modrinth 时才考虑启用。")
+ ConfigLines.Add("# 二次分发可能违反使用协议,请尽量不要公开发布包含资源文件的整合包!")
+ ConfigLines.Add("DontCheckHostedAssets:" & CheckAdvancedInclude.Checked)
+ ConfigLines.Add("")
+ ConfigLines.Add("# 如果你想要打包上传到 Modrinth,启用此项会生成完全符合 Modrinth 要求的整合包文件。")
+ ConfigLines.Add("# 由于 Modrinth 要求,只能从 CurseForge 下载的资源将无法联网下载,会被直接放入整合包中。")
+ ConfigLines.Add("# 此选项与 IncludeLauncher、IncludeLauncherCustom、DontCheckHostedAssets 冲突。")
+ ConfigLines.Add("ModrinthUploadMode:" & CheckAdvancedModrinth.Checked)
+ ConfigLines.Add("")
+ ConfigLines.Add("# 导出的文件的存放位置。")
+ ConfigLines.Add("# 若设置了此项,在导出时会直接将文件放到此路径,不会弹窗要求选择。")
+ ConfigLines.Add("# 若 IncludeLauncher 为 True,应以 .zip 结尾;若为 False,应以 .mrpack 结尾。")
+ ConfigLines.Add("PackPath:" & If(ConfigPackPath, ""))
+ ConfigLines.Add("")
+ '导出内容段
+ ConfigLines.Add(Sperator)
+ ConfigLines.AddRange(GetAllRules())
+ '追加内容段
+ ConfigLines.Add(Sperator)
+ ConfigLines.AddRange(GetExtraFileLines())
+ '结束
+ WriteFile(ConfigPath, ConfigLines.Join(vbCrLf))
+ Hint("已保存配置文件:" & ConfigPath, HintType.Finish)
+ OpenExplorer(ConfigPath)
+ Catch ex As Exception
+ Log(ex, "保存配置失败", LogLevel.Msgbox)
+ End Try
+ End Sub
+ '读取配置文件
+ Private Sub ImportConfig() Handles BtnAdvancedImport.Click
+ Try
+ Dim ConfigPath As String = SelectFile("整合包导出配置(*.txt)|*.txt", "选择配置文件", Setup.Get("CacheExportConfig"))
+ If String.IsNullOrEmpty(ConfigPath) Then Return
+ Setup.Set("CacheExportConfig", ConfigPath)
+ Dim Segments As String() = ReadFile(ConfigPath).Split(Sperator)
+ 'ini 段
+ Dim Ini As New Dictionary(Of String, String)
+ For Each Line In Segments(0).Split(vbCrLf.ToCharArray())
+ Line = Line.Trim
+ If Line = "" OrElse Line.StartsWithF("#") OrElse Line.StartsWithF("=") Then Continue For
+ Dim Index As Integer = Line.IndexOfF(":")
+ If Index > 0 Then Ini(Line.Substring(0, Index)) = Line.Substring(Index + 1)
+ Next
+ TextExportName.Text = Ini.GetOrDefault("Name", "")
+ TextExportVersion.Text = Ini.GetOrDefault("Version", "")
+ CheckOptionsPcl.Checked = Ini.GetOrDefault("IncludeLauncher", True)
+ CheckOptionsPclCustom.Checked = Ini.GetOrDefault("IncludeLauncherCustom", True)
+ CheckAdvancedModrinth.Checked = Ini.GetOrDefault("ModrinthUploadMode", False)
+ CheckAdvancedInclude.Checked = Ini.GetOrDefault("DontCheckHostedAssets", False)
+ ConfigPackPath = Ini.GetOrDefault("PackPath", Nothing)
+ '导出内容段
+ RulesOverrides = Segments(1).Replace(vbCr, vbLf).Replace(vbLf & vbLf, vbLf).Split(vbLf).ToList
+ '追加内容段
+ If Segments.Length > 2 Then
+ ExtraFiles = Segments(2).Replace(vbCr, vbLf).Replace(vbLf & vbLf, vbLf).Split(vbLf).ToList
+ Else
+ ExtraFiles = Nothing
+ End If
+ '结束
+ Hint("已读取配置文件:" & ConfigPath, HintType.Finish)
+ Catch ex As Exception
+ Log(ex, "读取配置失败", LogLevel.Msgbox)
+ End Try
+ End Sub
+
#End Region
#Region "导出"
- 'Private Sub ComboExportType_Change(sender As ComboBox, e As SelectionChangedEventArgs) Handles ComboExportType.SelectionChanged
- ' If AniControlEnabled <> 0 Then Exit Sub
- ' Type = sender.SelectedIndex
- ' Select Case Type
- ' Case ModpackType.Modrinth
- ' If IncludePCL() Then 'TODO:修改这里的条件
- ' If GridCompressed IsNot Nothing Then GridCompressed.Visibility = Visibility.Visible
- ' If GridModrinth IsNot Nothing Then GridModrinth.Visibility = Visibility.Collapsed
- ' Else
- ' If GridCompressed IsNot Nothing Then GridCompressed.Visibility = Visibility.Collapsed
- ' If GridModrinth IsNot Nothing Then GridModrinth.Visibility = Visibility.Visible
- ' End If
- ' End Select
- ' CardExport.TriggerForceResize()
- 'End Sub
- Private Sub BtnExportExport_Click() Handles BtnExportExport.Click
- Dim savePath As String = SelectAs(
- "选择导出位置", $"整合包 - {If(String.IsNullOrWhiteSpace(TbExportName.Text), PageVersionLeft.Version.Name, TbExportName.Text)}" & If(CheckIncludePCL.Checked, ".zip", ".mrpack"),
- If(CheckIncludePCL.Checked, "整合包文件(*.zip)|*.zip", "Modrinth 整合包(*.mrpack)|*.mrpack"))
- If String.IsNullOrEmpty(savePath) Then Exit Sub
- '获取需要包含的文件
- Dim contains As List(Of String) = Selected
- For Each c In PanCommonFiles.Children
- If TypeOf c IsNot MyCheckBox Then Continue For
- If String.IsNullOrEmpty(c.Tag) Then Continue For
- If c.Checked Then contains.Add(PageVersionLeft.Version.Path & c.Tag)
- Next
+ '''
+ ''' 配置文件中指定的导出位置。
+ '''
+ Private ConfigPackPath As String = Nothing
+
+ '''
+ ''' 开始导出。
+ '''
+ Private Sub StartExport() Handles BtnExport.Click
+ Dim PackName As String = If(String.IsNullOrEmpty(TextExportName.Text), TextExportName.HintText, TextExportName.Text)
+ Dim PackVersion As String = If(String.IsNullOrEmpty(TextExportVersion.Text), "1.0.0", TextExportVersion.Text)
+
'重复任务检查
+ Dim LoaderName As String = $"导出整合包:" & PackName
For Each OngoingLoader In LoaderTaskbar
- If OngoingLoader.Name <> $"导出整合包 {PageVersionLeft.Version.Name}" Then Continue For
- Hint("正在处理中,请稍候!", HintType.Critical)
- Exit Sub
+ If OngoingLoader.Name <> LoaderName Then Continue For
+ FrmMain.PageChange(FormMain.PageType.DownloadManager)
+ Return
Next
- '启动!
- Dim Loader As New LoaderTask(Of ExportOptions, Boolean)($"导出整合包 {PageVersionLeft.Version.Name}",
- Sub(Task As LoaderTask(Of ExportOptions, Boolean))
- Task.Output = ModpackExport(Task)
- Task.Progress = 1.0
- If Task.Output Then
- Hint($"{Task.Name} 成功!", HintType.Finish)
- OpenExplorer($"/select,""{Task.Input.Dest}""")
+
+ '确认导出位置
+ Dim Extension As String = If(CheckOptionsPcl.Checked, ".zip", ".mrpack")
+ Dim PackPath As String = Nothing
+ If Not String.IsNullOrWhiteSpace(ConfigPackPath) AndAlso
+ (Not ConfigPackPath.EndsWithF("\") AndAlso Not ConfigPackPath.EndsWithF("/")) Then
+ Try
+ Directory.CreateDirectory(GetPathFromFullPath(ConfigPackPath))
+ PackPath = ConfigPackPath
+ Log($"[Export] 使用配置文件中指定的导出路径:{ConfigPackPath}")
+ Catch ex As Exception
+ Log(ex, $"无法使用配置文件中指定的导出路径({ConfigPackPath})", LogLevel.Debug)
+ If MyMsgBox($"指定的路径:{ConfigPackPath}{vbCrLf}{vbCrLf}{GetExceptionDetail(ex)}", "无法使用配置文件中指定的导出路径", "确定", "取消") = 2 Then Return
+ End Try
+ End If
+ If PackPath Is Nothing Then
+ PackPath = SelectSaveFile("选择导出位置",
+ PackName & If(String.IsNullOrEmpty(TextExportVersion.Text), "", " " & TextExportVersion.Text),
+ $"整合包文件(*{Extension})|*{Extension}")
+ Log($"[Export] 手动指定的导出路径:{PackPath}")
+ End If
+ If String.IsNullOrEmpty(PackPath) Then Return
+
+ '缓存所需参数
+ Dim CacheFolder = RequestTaskTempFolder()
+ Dim OverridesFolder = CacheFolder & "modpack\overrides\"
+ Dim McVersion = PageVersionLeft.Version
+ Dim PathIndie As String = McVersion.PathIndie
+ Dim CheckHostedAssets As Boolean = Not CheckAdvancedInclude.Checked
+ Dim ModrinthUploadMode As Boolean = CheckAdvancedModrinth.Checked
+ Dim IncludePCL As Boolean = CheckOptionsPcl.Checked
+ Dim IncludePCLCustom As Boolean = IncludePCL AndAlso CheckOptionsPclCustom.Checked
+ Dim AllRules = StandardizeLines(GetAllRules(), True).ToList()
+ Dim AllExtraFiles = StandardizeLines(GetExtraFileLines(), False).ToList()
+ Log($"[Export] 准备导出整合包,共有 {AllRules.Count} 条规则,{AllExtraFiles.Count} 条追加内容行")
+
+ '构造步骤加载器
+ Dim Loaders As New List(Of LoaderBase)
+
+#Region "准备 PCL 文件"
+
+#If Not BETA Then
+ If IncludePCL Then
+ Loaders.Add(New LoaderTask(Of Integer, Integer)("下载 PCL 正式版",
+ Sub(Loader As LoaderTask(Of Integer, Integer))
+ DownloadLatestPCL(Loader)
+ CopyFile(PathTemp & "Latest.exe", CacheFolder & "Plain Craft Launcher.exe")
+ End Sub) With {.ProgressWeight = 0.5, .Block = False})
+ End If
+#End If
+
+#End Region
+
+#Region "复制文件"
+
+ Loaders.Add(New LoaderTask(Of Integer, List(Of McMod))("复制导出内容",
+ Sub(Loader As LoaderTask(Of Integer, List(Of McMod)))
+ Loader.Output = New List(Of McMod)
+ '复制版本文件
+ Dim Progress As Integer = 0
+ Dim SearchFolder As Action(Of DirectoryInfo)
+ SearchFolder =
+ Sub(Folder As DirectoryInfo)
+ '文件夹:进一步搜索
+ For Each SubFolder In Folder.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ '跳过部分又没用文件又多的文件夹,加快搜索
+ If Folder.FullName = PathIndie AndAlso {"assets", "versions", "libraries"}.Contains(SubFolder.Name) Then Continue For
+ If {"structureCacheV1", ".fabric", ".git", "avatar-cache", "cosmetic-cache"}.Contains(SubFolder.Name) Then Continue For
+ SearchFolder(SubFolder)
+ Next
+ '文件:检查规则并复制
+ For Each Entry In Folder.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+ Dim RelativePath As String = Entry.FullName.AfterFirst(PathIndie)
+ '检查规则
+ Dim ShouldKeep As Boolean = False
+ For Each Rule In AllRules
+ Dim Revert = Rule.StartsWith("!")
+ If RelativePath Like Rule.TrimStart("!") Then ShouldKeep = Not Revert
+ Next
+ If Not ShouldKeep Then Continue For
+ Dim TargetPath As String = OverridesFolder & RelativePath
+ CopyFile(Entry.FullName, TargetPath)
+ '若为压缩包,考虑联网获取路径
+ If CheckHostedAssets AndAlso
+ {".zip", ".rar", ".jar", ".disabled", ".old"}.Contains(Entry.Extension.ToLower) AndAlso
+ {"mods", "packs", "openloader", "resource"}.Any(Function(s) RelativePath.Contains(s)) Then
+ Dim ModFile As New McMod(TargetPath)
+ Dim Unused = ModFile.ModrinthHash '提前计算 Hash
+ Unused = ModFile.CurseForgeHash
+ Loader.Output.Add(ModFile)
+ End If
+ '更新进度(进度并不准确,主要突出一个我还没似)
+ Progress += 1
+ If Progress = 25 Then
+ Loader.Progress += (0.94 - Loader.Progress) * 0.012
+ Progress = 0
+ End If
+ Next
+ End Sub
+ SearchFolder(New DirectoryInfo(PathIndie))
+ Log($"[Export] 复制 overrides 文件完成,有 {Loader.Output.Count} 个文件需要联网检查")
+ Loader.Progress = 0.95
+ '复制追加内容到根目录
+ Dim BaseFolder As String = If(IncludePCL, CacheFolder, CacheFolder & "modpack\")
+ For Each Line In AllExtraFiles
+ If Line.EndsWithF("\") OrElse Line.EndsWithF("/") Then
+ If Directory.Exists(Line) Then
+ CopyDirectory(Line, BaseFolder & GetFolderNameFromPath(Line) & "\")
+ Else
+ Hint($"未找到配置文件中指定的文件夹:{Line}", HintType.Critical)
+ End If
+ Else
+ If File.Exists(Line) Then
+ CopyFile(Line, BaseFolder & GetFileNameFromPath(Line))
+ Else
+ Hint($"未找到配置文件中指定的单个文件:{Line}", HintType.Critical)
+ End If
+ End If
+ Next
+ Loader.Progress = 0.97
+ '复制 PCL 版本设置
+ CopyDirectory(McVersion.Path & "PCL\", OverridesFolder & "PCL\")
+#If BETA Then
+ '复制 PCL 本体
+ If IncludePCL Then CopyFile(PathWithName, CacheFolder & "Plain Craft Launcher.exe")
+#End If
+ '复制 PCL 个性化内容
+ If IncludePCLCustom Then
+ If Directory.Exists(Path & "PCL\Pictures\") Then CopyDirectory(Path & "PCL\Pictures\", CacheFolder & "PCL\Pictures\")
+ If Directory.Exists(Path & "PCL\Musics\") Then CopyDirectory(Path & "PCL\Musics\", CacheFolder & "PCL\Musics\")
+ If Directory.Exists(Path & "PCL\Help\") Then CopyDirectory(Path & "PCL\Help\", CacheFolder & "PCL\Help\")
+ If File.Exists(Path & "PCL\Custom.xaml") Then CopyFile(Path & "PCL\Custom.xaml", CacheFolder & "PCL\Custom.xaml")
+ If File.Exists(Path & "PCL\Setup.ini") Then CopyFile(Path & "PCL\Setup.ini", CacheFolder & "PCL\Setup.ini")
+ If File.Exists(Path & "PCL\hints.txt") Then CopyFile(Path & "PCL\hints.txt", CacheFolder & "PCL\hints.txt")
+ If File.Exists(Path & "PCL\Logo.png") Then CopyFile(Path & "PCL\Logo.png", CacheFolder & "PCL\Logo.png")
End If
- End Sub)
- Dim Combo = New LoaderCombo(Of ExportOptions)($"导出整合包 {PageVersionLeft.Version.Name}", {Loader})
- Combo.Start(New ExportOptions(PageVersionLeft.Version, savePath, CheckIncludePCL.Checked, contains.Distinct.ToArray,
- PCLSetupGlobal:=CheckIncludeSetup.Checked, Name:=TbExportName.Text, Desc:=TbExportDesc.Text,
- VerID:=TbExportVersion.Text))
- LoaderTaskbarAdd(Combo)
+ End Sub) With {.ProgressWeight = 5})
+
+#End Region
+
+#Region "联网检查"
+
+ Loaders.Add(New LoaderTask(Of List(Of McMod), Dictionary(Of McMod, List(Of String)))("联网获取文件信息",
+ Sub(Loader As LoaderTask(Of List(Of McMod), Dictionary(Of McMod, List(Of String))))
+ Loader.Output = New Dictionary(Of McMod, List(Of String))
+ If Not CheckHostedAssets Then Log($"[Export] 要求跳过联网获取步骤") : Return
+ If Not Loader.Input.Any Then Log($"[Export] 没有需要联网检查的文件,跳过联网获取步骤") : Return
+
+ '分平台获取下载地址
+ Dim EndedThreadCount As Integer = 0, FailedExceptions As New List(Of Exception)
+
+ '从 Modrinth 获取信息
+ RunInNewThread(
+ Sub()
+ Try
+ Dim ModrinthHashes = Loader.Input.Select(Function(m) m.ModrinthHash)
+ Dim ModrinthRaw = CType(GetJson(DlModRequest("https://api.modrinth.com/v2/version_files", "POST",
+ $"{{""hashes"": [""{ModrinthHashes.Join(""",""")}""], ""algorithm"": ""sha1""}}", "application/json")), JObject)
+ For Each ModFile In Loader.Input
+ '查找对应的文件
+ If Not ModrinthRaw.ContainsKey(ModFile.ModrinthHash) Then Continue For
+ If ModrinthRaw(ModFile.ModrinthHash)?("files")?(0)("hashes")?("sha1") <> ModFile.ModrinthHash Then Continue For
+ '写入下载地址
+ Loader.Output.AddToList(ModFile, ModrinthRaw(ModFile.ModrinthHash)("files")(0)("url"))
+ Next
+ Log($"[Export] 从 Modrinth 获取到 {ModrinthRaw.Count} 个本地资源项的对应信息")
+ Catch ex As Exception
+ Log(ex, "从 Modrinth 获取本地 Mod 信息失败")
+ FailedExceptions.Add(ex)
+ Finally
+ EndedThreadCount += 1
+ Loader.Progress += 0.45
+ End Try
+ End Sub, "Modrinth - " & LoaderName)
+
+ '从 CurseForge 获取信息
+ RunInNewThread(
+ Sub()
+ Try
+ If ModrinthUploadMode Then Return 'Modrinth 上传模式下,不能从 CurseForge 获取信息
+ Dim CurseForgeHashes = Loader.Input.Select(Function(m) m.CurseForgeHash)
+ Dim CurseForgeRaw = CType(CType(GetJson(DlModRequest("https://api.curseforge.com/v1/fingerprints/432/", "POST",
+ $"{{""fingerprints"": [{CurseForgeHashes.Join(",")}]}}", "application/json")), JObject)("data")("exactMatches"), JContainer)
+ For Each ResultJson As JObject In CurseForgeRaw
+ If Not ResultJson.ContainsKey("file") Then Continue For
+ Dim File As JObject = ResultJson("file")
+ If String.IsNullOrEmpty(File("downloadUrl")) Then Continue For
+ '查找对应的文件
+ Dim ModFile As McMod = Loader.Input.FirstOrDefault(Function(m) m.CurseForgeHash = File("fileFingerprint").ToObject(Of UInteger))
+ If ModFile Is Nothing Then Continue For
+ '写入下载地址
+ For Each Address In CompFile.HandleCurseForgeDownloadUrls(File("downloadUrl").ToString)
+ Loader.Output.AddToList(ModFile, Address)
+ Next
+ Next
+ Log($"[Export] 从 CurseForge 获取到 {CurseForgeRaw.Count} 个本地资源项的对应信息")
+ Catch ex As Exception
+ Log(ex, "从 CurseForge 获取本地 Mod 信息失败")
+ FailedExceptions.Add(ex)
+ Finally
+ EndedThreadCount += 1
+ Loader.Progress += 0.45
+ End Try
+ End Sub, "CurseForge - " & LoaderName)
+
+ '等待线程结束
+ Do Until EndedThreadCount = 2
+ If Loader.IsAborted Then Exit Sub
+ Thread.Sleep(10)
+ Loop
+
+ '若失败,确认是否继续
+ If FailedExceptions.Count = 1 Then
+ If MyMsgBox("联网获取部分文件信息失败,是否继续导出?" & vbCrLf & vbCrLf &
+ "若继续,无法获取信息的文件将被直接打包。" & vbCrLf &
+ "由于二次分发可能违反使用协议,请尽量不要公开发布导出的整合包!",
+ "部分文件信息获取失败", "继续", "取消") = 2 Then Throw FailedExceptions.First
+ ElseIf FailedExceptions.Count > 1 Then
+ If MyMsgBox("联网获取文件信息失败,是否继续导出?" & vbCrLf & vbCrLf &
+ "若继续,所有文件都将被直接打包。" & vbCrLf &
+ "由于二次分发可能违反使用协议,请尽量不要公开发布导出的整合包!",
+ "文件信息获取失败", "继续", "取消") = 2 Then Throw FailedExceptions.First
+ End If
+ End Sub) With {.Show = CheckHostedAssets, .ProgressWeight = If(CheckHostedAssets, 2, 0.01)})
+
+#End Region
+
+#Region "生成压缩包"
+
+ Loaders.Add(New LoaderTask(Of Dictionary(Of McMod, List(Of String)), Integer)("生成压缩包",
+ Sub(Loader As LoaderTask(Of Dictionary(Of McMod, List(Of String)), Integer))
+ '整理文件列表
+ Dim Files As New JArray
+ For Each Pair In Loader.Input
+ Dim ModFile As McMod = Pair.Key
+ Files.Add(New JObject From {
+ {"path", ModFile.Path.AfterFirst(OverridesFolder).Replace("\", "/")},
+ {"hashes", New JObject From {{"sha1", ModFile.ModrinthHash}, {"sha512", GetFileSHA512(ModFile.Path)}}},
+ {"downloads", New JArray(Pair.Value.OrderByDescending(Function(u) u.Contains("modrinth.com")))},
+ {"fileSize", New FileInfo(ModFile.Path).Length}
+ })
+ File.Delete(ModFile.Path)
+ Next
+ Loader.Progress = 0.2
+ '导出最终 JSON 文件
+ Dim Dependencies As New JObject From {{"minecraft", McVersion.Version.McName}}
+ If McVersion.Version.HasForge Then Dependencies.Add("forge", McVersion.Version.ForgeVersion)
+ If McVersion.Version.HasFabric Then Dependencies.Add("fabric-loader", McVersion.Version.FabricVersion)
+ If McVersion.Version.HasNeoForge Then Dependencies.Add("neoforge", McVersion.Version.NeoForgeVersion)
+ Dim ResultJson As New JObject From {
+ {"game", "minecraft"},
+ {"formatVersion", 1},
+ {"versionId", PackVersion},
+ {"name", PackName},
+ {"summary", McVersion.Info},
+ {"files", Files},
+ {"dependencies", Dependencies}
+ }
+ File.WriteAllText(CacheFolder & "modpack\modrinth.index.json", ResultJson.ToString(Newtonsoft.Json.Formatting.Indented))
+ '打包
+ Directory.CreateDirectory(GetPathFromFullPath(PackPath))
+ If File.Exists(PackPath) Then File.Delete(PackPath)
+ If IncludePCL Then
+ '首次压缩整合包
+ ZipFile.CreateFromDirectory(CacheFolder & "modpack\", CacheFolder & "modpack.mrpack")
+ Loader.Progress = 0.5
+ Directory.Delete(CacheFolder & "modpack\", True)
+ Loader.Progress = 0.6
+ '二次压缩整合包
+ ZipFile.CreateFromDirectory(CacheFolder, PackPath)
+ Loader.Progress = 0.9
+ Else
+ '直接压缩整合包
+ ZipFile.CreateFromDirectory(CacheFolder & "modpack\", PackPath)
+ Loader.Progress = 0.8
+ End If
+ Directory.Delete(CacheFolder, True)
+ OpenExplorer(PackPath)
+ End Sub) With {.ProgressWeight = 6})
+
+#End Region
+
+ '启动
+ Dim MainLoader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
+ MainLoader.Start()
+ LoaderTaskbarAdd(MainLoader)
+ FrmMain.BtnExtraDownload.ShowRefresh()
+ FrmMain.BtnExtraDownload.Ribble()
+ FrmMain.PageChange(FormMain.PageType.DownloadManager)
End Sub
#End Region
+ '自动填写整合包名称
+ Private Sub TextExportName_GotFocus() Handles TextExportName.GotFocus
+ If TextExportName.Text = "" Then
+ TextExportName.Text = TextExportName.HintText
+ TextExportName.SelectionStart = TextExportName.Text.Length
+ End If
+ End Sub
+
+ '勾选 Modrinth 上传模式时,禁止打包 PCL
+ Private Sub CheckAdvancedModrinth_Change(sender As Object, user As Boolean) Handles CheckAdvancedModrinth.Change
+ If CheckAdvancedModrinth.Checked Then CheckOptionsPcl.Checked = False
+ CheckOptionsPcl.IsEnabled = Not CheckAdvancedModrinth.Checked
+ End Sub
+
+ '勾选打包资源文件时,禁止开启 Modrinth 上传模式
+ Private Sub CheckAdvancedInclude_Change(sender As Object, user As Boolean) Handles CheckAdvancedInclude.Change
+ If CheckAdvancedInclude.Checked Then CheckAdvancedModrinth.Checked = False
+ CheckAdvancedModrinth.IsEnabled = Not CheckAdvancedInclude.Checked
+ End Sub
+
End Class
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml
index 45eb800..acba205 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml
@@ -23,8 +23,13 @@
-
+
+
+
+
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb
index 30742bc..0383111 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionLeft.xaml.vb
@@ -1,4 +1,5 @@
Public Class PageVersionLeft
+ Implements IRefreshable
'''
''' 当前显示设置的 MC 版本。
@@ -91,7 +92,20 @@
#End Region
Public Sub Refresh(sender As Object, e As EventArgs) '由边栏按钮匿名调用
- PageVersionMod.Refresh()
+ Refresh(Val(sender.Tag))
+ End Sub
+ Public Sub Refresh() Implements IRefreshable.Refresh
+ Refresh(FrmMain.PageCurrentSub)
+ End Sub
+ Public Sub Refresh(SubType As FormMain.PageSubType)
+ Select Case SubType
+ Case FormMain.PageSubType.VersionMod
+ PageVersionMod.Refresh()
+ ItemMod.Checked = True
+ Case FormMain.PageSubType.VersionExport
+ If FrmVersionExport IsNot Nothing Then FrmVersionExport.RefreshAll()
+ ItemExport.Checked = True
+ End Select
End Sub
Public Sub Reset(sender As Object, e As EventArgs)
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
index f2450b6..61fd384 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
@@ -289,7 +289,7 @@
Private Sub BtnManageOpen_Click(sender As Object, e As EventArgs) Handles BtnManageOpen.Click, BtnHintOpen.Click
Try
Directory.CreateDirectory(PageVersionLeft.Version.PathIndie & "mods\")
- OpenExplorer("""" & PageVersionLeft.Version.PathIndie & "mods\""")
+ OpenExplorer(PageVersionLeft.Version.PathIndie & "mods\")
Catch ex As Exception
Log(ex, "打开 Mods 文件夹失败", LogLevel.Msgbox)
End Try
@@ -856,10 +856,8 @@ Install:
'打开文件所在的位置
Public Sub Open_Click(sender As MyIconButton, e As EventArgs)
Try
-
Dim ListItem As MyLocalModItem = sender.Tag
- OpenExplorer("/select,""" & ListItem.Entry.Path & """")
-
+ OpenExplorer(ListItem.Entry.Path)
Catch ex As Exception
Log(ex, "打开 Mod 文件位置失败", LogLevel.Feedback)
End Try
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb
index 133138a..7a975a4 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionOverall.xaml.vb
@@ -235,21 +235,21 @@
OpenVersionFolder(PageVersionLeft.Version)
End Sub
Public Shared Sub OpenVersionFolder(Version As McVersion)
- OpenExplorer("""" & Version.Path & """")
+ OpenExplorer(Version.Path)
End Sub
'存档文件夹
Private Sub BtnFolderSaves_Click() Handles BtnFolderSaves.Click
Dim FolderPath As String = PageVersionLeft.Version.PathIndie & "saves\"
Directory.CreateDirectory(FolderPath)
- OpenExplorer("""" & FolderPath & """")
+ OpenExplorer(FolderPath)
End Sub
'Mod 文件夹
Private Sub BtnFolderMods_Click() Handles BtnFolderMods.Click
Dim FolderPath As String = PageVersionLeft.Version.PathIndie & "mods\"
Directory.CreateDirectory(FolderPath)
- OpenExplorer("""" & FolderPath & """")
+ OpenExplorer(FolderPath)
End Sub
#End Region
@@ -260,7 +260,7 @@
Private Sub BtnManageScript_Click() Handles BtnManageScript.Click
Try
'弹窗要求指定脚本的保存位置
- Dim SavePath As String = SelectAs("选择脚本保存位置", "启动 " & PageVersionLeft.Version.Name & ".bat", "批处理文件(*.bat)|*.bat")
+ Dim SavePath As String = SelectSaveFile("选择脚本保存位置", "启动 " & PageVersionLeft.Version.Name & ".bat", "批处理文件(*.bat)|*.bat")
If SavePath = "" Then Exit Sub
'检查中断(等玩家选完弹窗指不定任务就结束了呢……)
If McLaunchLoader.State = LoadState.Loading Then
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
index bc2ed25..f7a23c0 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
@@ -153,11 +153,11 @@
If LabRamGame Is Nothing OrElse LabRamUsed Is Nothing OrElse FrmMain.PageCurrent <> FormMain.PageType.VersionSetup OrElse FrmVersionLeft.PageID <> FormMain.PageSubType.VersionSetup Then Exit Sub
'获取内存情况
Dim RamGame As Double = GetRam(PageVersionLeft.Version)
- Dim RamTotal As Double = Math.Round(My.Computer.Info.TotalPhysicalMemory / 1024 / 1024 / 1024 * 10) / 10
- Dim RamAvailable As Double = Math.Round(My.Computer.Info.AvailablePhysicalMemory / 1024 / 1024 / 1024 * 10) / 10
- Dim RamGameActual As Double = Math.Min(RamGame, RamAvailable)
- Dim RamUsed As Double = RamTotal - RamAvailable
- Dim RamEmpty As Double = Math.Round(MathClamp(RamTotal - RamUsed - RamGame, 0, 1000) * 10) / 10
+ Dim RamTotal As Double = Math.Round(My.Computer.Info.TotalPhysicalMemory / 1024 / 1024 / 1024, 1)
+ Dim RamAvailable As Double = Math.Round(My.Computer.Info.AvailablePhysicalMemory / 1024 / 1024 / 1024, 1)
+ Dim RamGameActual As Double = Math.Round(Math.Min(RamGame, RamAvailable), 5)
+ Dim RamUsed As Double = Math.Round(RamTotal - RamAvailable, 5)
+ Dim RamEmpty As Double = Math.Round(MathClamp(RamTotal - RamUsed - RamGame, 0, 1000), 1)
'设置最大可用内存
If RamTotal <= 1.5 Then
SliderRamCustom.MaxValue = Math.Max(Math.Floor((RamTotal - 0.3) / 0.1), 1)
diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
index 71699f6..40b3e50 100644
--- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
+++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
@@ -186,6 +186,9 @@
+
+ MyExtraTextButton.xaml
+
@@ -385,6 +388,10 @@
PageSpeedRight.xaml
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
@@ -479,10 +486,10 @@
MyRadioButton.xaml
-
FormMain.xaml
Code
+
MSBuild:Compile
Designer
@@ -565,8 +572,8 @@
Designer
- MSBuild:Compile
Designer
+ MSBuild:Compile
Designer
@@ -917,11 +924,11 @@
-
+
diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml
index 73b7714..2352ee4 100644
--- a/Plain Craft Launcher 2/Resources/Custom.xaml
+++ b/Plain Craft Launcher 2/Resources/Custom.xaml
@@ -87,22 +87,22 @@
-
+
+ Text="将 EventType 设置为 执行命令(或 打开文件),EventData 设置为文件路径,即可点击打开特定文件或启动程序。" />
+ Text="打开记事本" EventType="执行命令" EventData="notepad.exe" />
+ Text="打开 PCL 日志" EventType="执行命令" EventData="Log1.txt" ToolTip="PCL 的日志文件就在 PCL 文件夹内,且名为 Log1.txt" />
+ Text="定位游戏启动脚本" EventType="执行命令" EventData="explorer.exe|/select,LatestLaunch.bat" ToolTip="PCL 会将上一次启动 MC 的脚本保存在 PCL 文件夹内,且名为 LatestLaunch.bat。
点击按钮会打开文件管理器,并且借助参数要求它选中这个文件。" />
diff --git a/Plain Craft Launcher 2/Resources/Help.zip b/Plain Craft Launcher 2/Resources/Help.zip
index c36f288..d204879 100644
Binary files a/Plain Craft Launcher 2/Resources/Help.zip and b/Plain Craft Launcher 2/Resources/Help.zip differ
diff --git a/Plain Craft Launcher 2/Resources/ModData.txt b/Plain Craft Launcher 2/Resources/ModData.txt
index a44a30e..a1a591e 100644
--- a/Plain Craft Launcher 2/Resources/ModData.txt
+++ b/Plain Craft Launcher 2/Resources/ModData.txt
@@ -295,7 +295,7 @@ lucky-block-official|幸运方块 (Lucky Block)
dynamic-lights|动态光源*
yarr-cute-mob-models-remake|怪物娘化 (Yarr Cute Mob Models - Remake)
monster-hunter-frontier-craft|怪物猎人边境工艺*
-zombie-awareness|僵尸意识*
+zombie-awareness@|僵尸意识*
remote-io|远程接口 (Remote IO)
electrical-age|电力时代*
@@ -462,7 +462,7 @@ magneticraft|磁场工艺 (MagnetiCraft)
chromaticraft|缤纷纪元 (ChromatiCraft)
immersive-engineering@immersiveengineering|沉浸工程*
armourers-workshop@|时装工坊 (Armourer's Workshop)
-
+plants-to-ores|植物矿石*
slash|命令强化*
et-futurum|把未来带回现实*
cooking-for-blockheads@|傻瓜烹饪/懒人厨房 (Cooking for Blockheads)¨cooking-for-blockheads-fabric|傻瓜烹饪/懒人厨房 (Cooking for Blockheads)
@@ -471,7 +471,7 @@ psi@
good-nights-sleep|夜眠梦境/晚安的梦境 (Good Night's Sleep)
binnies-mods@|宾尼的模组合集 (Binnie's Mods)
baubles|饰品栏*
-more-swords-mod|更多剑 (MoreSwords)
+more-swords-mod@|更多剑 (MoreSwords)
thaumic-bases|神秘基础学*¨thaumic-bases-unofficial|神秘基础学 (Thaumic Bases)
essentialcraft-3|源质魔法3 (EssentialCraft 3)
@@ -642,7 +642,7 @@ apocalypse-mod|启示录 (Apocalypse)
bookshelf-api-library
movingworld
-botania-garden-of-glass|水晶花园 (Garden of Glass)
+botania-garden-of-glass@gardenofglass|水晶花园 (Garden of Glass)
@tubestuff|RP增强 (Tube Stuff)
tiny-progressions|微型自动化*
@@ -940,7 +940,7 @@ bookshelf@bookshelf-lib
random-things|随意作品*
netherex|下界拓展 (NetherEX)
-engender-mod|召唤师 (Engender - The Age of Minecraft)
+engender-mod|召唤师 (The Engender Mod)
better-questing|更好的任务*
@@ -1002,7 +1002,7 @@ storage-boats-mod|存储船 (Storage Boat Mod)
mo-withers|更多凋灵 (Mo' Withers)
jingames-naruto-c|火影忍者C (Naruto C)
the-autologin-mod-for-authme-server|自动登录 (AutoLogin)
-ROOST|鸡窝 (Roost)
+roost|鸡窝*
croparia@|矿石作物/魔种之咏*
simple-corn|简单玉米*
thaumic-jei@
@@ -1042,7 +1042,7 @@ toxicrain|有毒的雨 (ToxicRain)
cannibalism|割肉小刀/同类相食*
quick-consume|快速消耗*
-planting-dirt-for-saplings|自动植树机 (Planting dirt for saplings)
+planting-dirt-for-saplings@auto-planter|自动植树机 (Auto Planter / Planting dirt for saplings)
custom-crosshair-mod@|自定义准心*
better-blink|更好的传送*
@@ -1079,7 +1079,7 @@ rough-tweaks|难度调整*
the-disenchanter-mod|祛魔台 (The Disenchanter)
carry-on@|搬运*
-better-than-llamas|更好看的羊驼*¨better-than-llamas-fabric|更好看的羊驼 (Better Than Llamas)
+better-than-llamas@|更好看的羊驼*¨better-than-llamas-fabric|更好看的羊驼 (Better Than Llamas)
dynamic-surroundings@dynamicsurroundingsfabric|动态环境/动态环绕*¨dynamic-surroundings-fabric-edition|动态环境/动态环绕 (Dynamic Surroundings)
alfheim@|亚尔夫海姆*
mob-battle-mod@|怪物大乱斗*¨mob-battle-mod-fabric|怪物大乱斗 (Mob Battle Mod)
@@ -1159,7 +1159,7 @@ server-properties-for-lan|简单联机 (Server.Properties for LAN)¨@server-prop
jaopcaadditions|JAOPCA附属 (JAOPCAAdditions)
cloche-call
-mouse-tweaks@|鼠标手势*
+mouse-tweaks|鼠标手势*¨@mouse-tweaks|鼠标手势 (Mouse Tweaks) - 仅部分
jargca|JAOPCA神秘农业附属 (Just A Resource Generation Compatibility Attempt)
sky-resources|空岛资源2 (Sky Resources2)
hexxit-gear|Hexxit 装备*¨hexxit-gear-restored|Hexxit 装备 (Hexxit Gear)
@@ -1205,7 +1205,7 @@ stellar-sky|群星璀璨*
stellar-api|恒星API (Stellar API)
nei-addons|NEI扩充 (NEI Addons)
cucumber@
-hunting-dimension|狩猎维度*
+hunting-dimension@|狩猎维度*
runes-of-wizardry|符文魔法 (Runes-of-Wizardry)
coffee-workshop|咖啡工坊*
@@ -1222,7 +1222,7 @@ tough-expansion|意志坚定:热力附属*
mystical-agradditions@|神秘农业扩展*
vanillafix@|原版修复 (VanillaFix)
just-enough-harvestcraft|更多潘马斯配方查询 (Just Enough HarvestCraft)
-charcoal-pit@charcoal_pit
+charcoal-pit-4@charcoal_pit¨charcoal-pit-3¨charcoal-pit
uncomplication|IC2exp还原*
the-wings-of-alfheim-1-7-10|亚尔夫海姆之翼 (The Wings of Alfheim)
@@ -1252,7 +1252,7 @@ level-up-legacy|升级!前版 (Level Up!Legacy)
moar-boats|模块化船*
numina|模块化动力装甲前置*
tinkers-complement@|匠魂补充 (Tinkers' Complement)
-everlastingabilities@everlasting-abilities|永恒能力 (EverlastingAbilities)
+everlastingabilities@everlasting-abilities|永恒能力 (Everlasting Abilities)
abyssalcraft-heads|深渊国度头颅 (AbyssalCraft Heads)
overlord@|不死者之王*
multifarm-crops|多功能农场兼容*
@@ -1391,8 +1391,8 @@ gases-framework
ranged-pumps@|范围泵*
bagginses|背包*
ic2-classic|工业时代2经典版 (IC2 Classic)
-opencubicchunks
-cubicworldgen
+opencubicchunks@cubicchunks
+cubicworldgen@cwg
rally-health
open-terrain-generator|开放式地形生成器*
@@ -1449,7 +1449,7 @@ cxlibrary
integrated-tunnels@|集成管道*
unmending|经验不修补*
-the-creeping-nether|蔓延的下界*
+the-creeping-nether|蔓延的下界* - 已失效¨@creeping-nether|蔓延的下界 (The Creeping Nether)
random-psideas|Psi随想 (Random PSIdeas)
expetrum|真实世界 (ExPetrum)
equivalent-energistics|等价能源学*¨equivalent-energistics-patched|等价能源学 (Equivalent Energistics) - patch版
@@ -1525,7 +1525,7 @@ better-questing-standard-expansion|更好的任务-基础扩展 (Better Questing
exchangers|方块交换器*
flexible-tools|灵活的工具*
dimensional-control|维度控制*
-valkyrien-warfare@valkyrien-skies|瓦尔基里天空/瓦尔基里战争 (Valkyrien Skies/Valkyien Warfare)
+valkyrien-warfare@valkyrien-skies|瓦尔基里天空/瓦尔基里战争 (Valkyrien Skies/Valkyrien Warfare)
item-scroller@|物品滚轮*
better-advancements@|更好的进度*
lucraft-core
@@ -1561,7 +1561,7 @@ ptrlib
creeper-awareness|爬行者意识*
what-fluid-fix
blue-skies@|蔚蓝浩空*
-project-vibrant-journeys|活力之旅 (Project: Vibrant Journeys)
+project-vibrant-journeys@|活力之旅 (Project: Vibrant Journeys)
bed-bugs|修复卡床*
biometweaker|生物群系修改器 (BiomeTweaker)
actuallycomputers|实用电脑 (ActuallyComputers)
@@ -1692,10 +1692,10 @@ custom-selection-box-port|自定义选择框 (Custom Selection Box)¨customselec
librikka-api
not-enough-wands@|更多魔杖*
deep-mob-learning-blood-magic-addon|深度怪物知识-血魔法插件 (Deep Mob Learning - Blood Magic Addon)
-informed-load-fabric|更多加载信息 (Informed Load)
+informed-load|更多加载信息*
huajiage-infinite-galaxy|滑稽纪元II:无尽星河 (Huaji Age II:Infinite Galaxy)
-cqrepoured|寻找巧克力重铸版 (Chocolate Quest Repoured)
+cqrepoured@chocolate-quest-repoured|寻找巧克力重铸版 (Chocolate Quest Repoured)
fabricproxy@|Fabric 群组支持 (FabricProxy)
ex-sartagine|锅具*
xaeros-minimap|Xaero的小地图 (Xaero's Minimap) - 默认版(功能完整)¨xaeros-minimap-fair-play-edition|Xaero的小地图 (Xaero's Minimap) - 公平竞争版(功能部分缺失)¨@xaeros-minimap|Xaero的小地图 (Xaero's Minimap) - 完整版¨@xaeros-minimap-fair|Xaero的小地图 (Xaero's Minimap) - 公平竞争版
@@ -2022,7 +2022,7 @@ create@|机械动力*¨create-fabric@|机械动力 (Create)
energeticsheep|脉冲羊 (EnergeticSheep)
-ensorcellation|万象*
+ensorcellation@|万象*
inventory-sorter|物品分拣*
speedster-heroes|闪电侠*
@@ -2085,7 +2085,7 @@ zetta-industries|泽它重工*
kekztech
by-the-gods|神迹*
bee-angry-est|愤怒的蜜蜂 (Bee Angry-est)
-titanium|钛*
+titanium@|钛*
chemlib¨chemlib-fabric
carrots-lib|胡萝卜库 (Carrots Library)
no-hostiles-around-campfire@|围火无怪*¨no-hostiles-around-campfire-fabric|围火无怪 (No Hostiles Around Campfire)
@@ -2302,7 +2302,7 @@ amun-ra
mysterious-mountain-lib|妖怪之山通用库 (MysteriousMountainLib)
loottweaker
-rediscovered|特性重现*
+rediscovered@|特性重现*
baubles-reborn|饰品栏重置版*
@@ -2434,7 +2434,7 @@ tooltweaks|禁用原版工具 (ToolTweaks)
atop|超多生物群系护甲 (Armor for Biomes O' Plenty)
tinkers-forging|工匠锻造 (Tinker's Forging)
gravity-gun|重力枪*
-mekanism-additions@|通用机械附加 (Mekanism Addition)
+mekanism-additions@|通用机械附加*
signpicture|告示牌图片 (Sign Picture)
vampirism-integrations@|吸血鬼联动*
gottschcore@
@@ -2669,7 +2669,7 @@ more-discs-mod|更多唱片 (More discs mod)
edible-honeycombs
emerald-and-ruby
craftable-nametag|可合成的命名牌*
-dyeable-flower-pots|染色花盆*
+dyeable-flower-pots|染色花盆 (More Flower Pots / Dyeable Flower Pots)¨more-flower-pots|染色花盆 (More Flower Pots / Dyeable Flower Pots)
graffiti|涂鸦*
bunny-boots
backported-flora
@@ -2734,7 +2734,7 @@ screenshot-to-clipboard@|截图到剪贴板 (Screenshot to Clipboard)¨screensho
hide-armor|隐藏盔甲*
hunterillager@hunters-return|邪猎者 (HunterIllager)
-pocket-apocalypse-mod|便携式天灾 (Pocket Apocalypse mod)
+pocket-apocalypse-mod|便携式天灾*
compass|指南针 (compass)
tinkers-evolution@|匠魂进化 (Tinkers' Evolution)
tinkers-exporter|匠魂数据导出器*
@@ -2951,7 +2951,7 @@ better-ping-display@|更好的延迟显示*¨better-ping-display-fabric@|更好
turned-the-changed-mod
survival-tweaks|生存微调*
melon-slabs@|西瓜台阶*
-lambdynamiclights@|Lambd 的动态光源 (LambDynamicLights)
+lambdynamiclights@|Lambd的动态光源 (LambDynamicLights)
realistic-torches@|真实火把*
durability-viewer|耐久显示器*
@@ -3031,7 +3031,7 @@ treasure2|寻宝记2 (Treasure2!)
@autoreconnector-fabric|自动断线重连 (AutoReconnector-Fabric)
structure-gel-api@
-the-conjurer|魔术师*
+the-conjurer@|魔术师*
ganys-nether|Gany的下界 (Gany's Nether)
ganys-end|Gany的末地 (Gany's End)
epic-fight-mod@epic-fight|史诗战斗 (Epic Fight)
@@ -3091,7 +3091,7 @@ better-questing-triggerer
Broken-Wings
compacter|压缩者*
-simple-cc|简单清除聊天 (Simple Clear Chat)
+
not-enough-potions
framed-compacting-drawers|镶框压缩抽屉*
discordsuite
@@ -3114,7 +3114,7 @@ feed-a-friend
the-legend-of-herobrine@
-extra-origins|起源扩展*
+extra-origins@|起源扩展*
caves-and-cliffs
miner-golems|矿工傀儡*
bakadanmaku@|直播弹幕模组 (BakaDanmaku)
@@ -3542,7 +3542,7 @@ dynamic-sound-filters@|动态声音滤波器*
binniepatcher
subterranean-wilderness
-construction-wand|建筑手杖*
+construction-wand@|建筑手杖*
squeedometer
forgery@fabrication¨fabrication
@@ -3702,7 +3702,7 @@ sodium-extra@|钠 · 扩展*
grind-enchantments@|砂轮增强*
voice-chat|语音聊天*
-xnet-gases
+xnet-gases@
filing-cabinets-refurbished
dropz@
@@ -3762,7 +3762,7 @@ astromine-core|天体矿工:核心 (Astromine: Core)
dungeondq
dynamic-trees-integrated-dynamics|动态的树:动态联合/集成动力附属 (Dynamic Trees - Integrated Dynamics)
skyblock-builder@|空岛建立者*
-antiportals|反传送门 (AntiPortals)
+antiportals@|反传送门 (AntiPortals)
trash-cans@|垃圾桶*
mattdahepic-core
mob-farm-helpers-forge|刷怪塔小帮手 (Mob Farm Helpers)¨mob-farm-helpers-fabric|刷怪塔小帮手 (Mob Farm Helpers)
@@ -4031,7 +4031,7 @@ sonicraft@|索尼克工艺 (SoniCraft)
islands
origins-forge@|起源非官方Forge版 (Origins (Forge))
magic-feather
-alet
+alet@a-little-extra-tiles
netheriteplus
the-survivalists-mod
campfire-overhaul
@@ -4052,7 +4052,7 @@ se-sign-shop|简单经济:牌子商店 (SE: Sign Shop)
fancymenu@¨fancymenu-fabric
halogen|卤素*
-zetamod@zetaforged|泽塔 (ZetaForged/Zeta's Mod)
+zetamod@zetaforged|泽塔 (ZetaForged / Zeta's Mod)
wholetreeaxe
@@ -4091,7 +4091,7 @@ afkpeace|AFKPeace - 停止更新¨@afkpeace
smooth-chunks|平滑区块加载*¨@smooth-chunks|平滑区块加载 (Smooth Chunks) - 已失效
-saturationoverflow|饱和度溢出 (Saturation Over flow)
+saturationoverflow|饱和度溢出 (SaturationOverflow)
phantom-blood@phantomblood|幻影之血*
@@ -4263,7 +4263,7 @@ x-hp@
mr-pineapples-toy-guns
magickcore@|魔法核心 (MagickCore)
-feywild
+feywild|仙灵荒野*
The-final-sword|最终之剑重置版 (The final sword)
gog-skybox
@@ -4383,7 +4383,7 @@ simple-emerald-tools-fabric¨easy-emerald-tools
cold-sweat@|冷汗*
witchery-rewitched|巫术:重生 (Witchery: Rewitched)
majou-densetsu-3d|魔城传说3D (Majou Denetsu 3D)
-icarus-wings|伊卡洛斯之翼*
+icarus-wings@|伊卡洛斯之翼*
keyboard-wizard|按键精灵*
piscary
@@ -4468,7 +4468,7 @@ plumed-belt
better-with-minecolonies
calemis-utilities|Calemi的实用设备 (Calemi's Utilities)
botanical-cowardice
-travelers-titles@¨travelers-titles-fabric¨travelers-titles-neoforge
+travelers-titles@|旅人标题 (Traveler's Titles)¨travelers-titles-fabric|旅人标题 (Traveler's Titles)¨travelers-titles-neoforge|旅人标题 (Traveler's Titles)
expand-chinese-sword|中式剑拓展 (Arsenal Integration)
littleframes@littlepictureframes|小画 (LittlePictureFrames)
@@ -4659,7 +4659,7 @@ whisperwoods|低语之森*
dimensional-dungeons@|维度地牢*
bosses-of-mass-destruction@|祸乱鬼魅 (Bosses of Mass Destruction)
neoelfeos-medieval-pub-decoration
-dustrial-decor
+dustrial-decor@dustrial-decor-forge
feders-scarecrows|Feder的稻草人 (Feder's Scarecrows)
zetter@zetter-painting-mod
mystical-pumpkins|神秘南瓜*
@@ -4687,7 +4687,7 @@ increasemobs
scarecrows-territory@|稻草人领域 (Scarecrows' Territory)
dungeons-mod¨dungeons-mod-lite
-loot-beams@|战利品光束*
+loot-beams@|战利品光束 (Loot Beams: Relooted!)
theurgy@
better-badlands|恶地改善*
@@ -4820,7 +4820,7 @@ presence-footsteps-forge@resence-footsteps-forge|脚步声Forge版 (Presence Foo
aardvarks-weird-wonderful-wild
-
+little-delicacy-cafes
ee3-helper
save-your-pets|拯救你的宠物*
main-menu-scale
@@ -5018,7 +5018,7 @@ npc-variety-port@npc-variety|NPC变体 (NPC Variety)
just-enough-effects
incorporeal-2-rhododendrite|幻想多媒体2 (Incorporeal 2 + Rhododendrite)
-einsteins-library
+einsteins-library@
worldshape
rslargepatterns|精致存储大样板 (Refined Storage Large Patterns)
gendustry-jei-addon
@@ -5056,7 +5056,7 @@ environmentz@
homing-ender-eye@|回归之眼*
magical-crops-regrowth-1-16-5|魔法作物:重生 (Magical Crops: Regrowth)
-voidz
+voidz@
mining-master@
victus@
@@ -5252,7 +5252,7 @@ stupid-horse-stand-still
ceramic-bucket@|陶瓷桶*
durability101@
ceramic-shears|陶瓷剪刀*
-torch-bandolier|火把弹药袋*
+torch-bandolier@|火把弹药袋*
rapid-leaf-decay@
netherite-horse-armor-mod|下界合金马铠*
ambient-environment@
@@ -5365,7 +5365,7 @@ urns
pylons
on-soul-fire@
wizard-staff
-kambrik
+kambrik@
additional-bars@additionalbars|更多的栏杆*¨additional-bars-fabric|更多的栏杆 (Additional Bars)
leap@
cameraoverhaul@
@@ -5435,7 +5435,7 @@ immersive-industry|沉浸工业*
lightmans-currency@¨lightmans-currency-fabric
hellfire-fireball-launcher|地狱火:火焰弹发射器 (Hellfire: Fireball Launcher)¨hellfire-fireball-launcher-fabric|地狱火:火焰弹发射器 (Hellfire: Fireball Launcher)
guild
-endless-deep-space|无尽深空*
+endless-deep-space|无尽深空* - 已弃用¨@endless-deep-space|无尽深空 (Endless Deep Space)
dynamic-trees-for-sakura|动态的树:樱附属 (Dynamic Trees for Sakura)
advanced-fishing
@@ -5461,7 +5461,7 @@ just-enough-effect-descriptions-jeed@|JEI药水效果 (Just Enough Effect Descri
hostile-neural-networks
mo-cakes|更多蛋糕 (Mo' Cake)
fishing-made-better|更好的钓鱼*
-neon-craft-2-mod|霓虹灯艺2*
+neon-craft-ultimate|霓虹灯艺2 (Neon Craft Ultimate/Neon Craft 2 Mod)
architects-dream|建筑师之梦 (Architect's Dream)
ignition-enderbags
correlated@
@@ -5506,7 +5506,7 @@ wdl|世界下载器 (World Downloader)
domum-ornamentum
methodicalmilking
-save-and-load-inventories-fabric¨save-and-load-inventories
+save-and-load-inventories-fabric@save-and-load-inventories¨save-and-load-inventories
forge-config-api-port-fabric@forge-config-api-port
pams-cookables
end-metals
@@ -5720,7 +5720,7 @@ platypuses
hostile-villages|敌对村庄*
throwability|投掷*
biomeinfo@|生物群系信息 (BiomeInfo)¨biomeinfo-fabric|生物群系信息 (BiomeInfo)
-clickable-advancements|可点击的进度 (Clickable advancements)
+clickable-advancements|可点击的进度*
factory0-resources
epic-knights-armor-and-weapons@epic-knights-shields-armor-and-weapons|史诗骑士:盾牌,盔甲和武器 (Epic Knights: Shields, Armor and Weapons)
youre-an-expert-harry|你是个专家,哈利! (You're an Expert, Harry!)
@@ -6028,7 +6028,7 @@ cheese-dragon
boatjump@
torched
-nyfs-quivers|Nyf的箭袋 (Nyf's Quivers)
+nyfs-quivers@|Nyf的箭袋 (Nyf's Quivers)
party-parrots@¨party-parrots-fabric
patina-pipeworks|古铜管道*
brewevolution|酿造进化🍺 (Brewevolution 🍺)
@@ -6062,7 +6062,7 @@ glymworld-lush-caves
colored-crafting-stations
harvest-scythes
croptosis
-weeping-angels-mod¨the-lonely-assassins
+weeping-angels-mod@weeping-angels¨the-lonely-assassins
hardcore-item-stages
game-stages-viewer
mpm-lotr|更多玩家模型:魔戒兼容 (MPM LOTR)
@@ -6324,7 +6324,7 @@ roughly-enough-professions-rep@|REI工作方块 (Roughly Enough Professions)
sophisticated-core@|精妙核心*
edibles@¨edibles-fabric
reach-fix
-lootjs-forge
+lootjs-forge@lootjs
chat-sounds@chatsounds
advancement-screenshot@¨advancement-screenshot-fabric
mc-mods-loot-drops@all-loot-drops
@@ -6391,7 +6391,7 @@ tfctreatedwood|群峦·防腐木 (TFC Treated Wood)
retro64¨retro64-fabric
double-hotbar|双重快捷栏*
lavatech|熔岩科技 (Lava Tech)
-pineapple-psychic@with-pineapple-psychic|凤梨通灵术*
+pineapple-psychic@|凤梨通灵术*
fragile-torches
@time-changer
come-closer
@@ -6523,7 +6523,7 @@ aquatic-torches@
better-sponges
easyexcavate
wooden-shears@|木剪刀*
-brick-furnace|砖熔炉*
+brick-furnace@|砖熔炉*
brick-hopper|砖漏斗*
human-companions
odysseyhud
@@ -6566,7 +6566,7 @@ ae2-fluid-crafting-rework@ae2-fluid-crafting|AE2流体合成套件重置 (AE2 Fl
moreculling@
-better-village-forge@better-villages-forge|更好的村庄 (Better Village)¨better-village-fabric@better-villages-fabric|更好的村庄 (Better Village)
+better-village-forge@bettervillages|更好的村庄 (Better Villages)¨better-village-fabric|更好的村庄 (Better Villages)
trinkets-curios-theme@
banilla-claws-forge@banilla-claws¨banilla-claws@banilla-claws-forge
@@ -6642,7 +6642,7 @@ the-chicken-came-first|先有鸡*
hardcore-dungeons
block-relocation-forge¨block-relocation
-norecipebook-fabric|没有配方书Fabric版 (NoRecipeBook (Fabric))¨@norecipebook-fabric|没有配方书Fabric版 (NoRecipeBook (Fabric)) - 已失效
+norecipebook-fabric@norecipebook|没有配方书Fabric版 (NoRecipeBook (Fabric))
@sort
fpsdisplay@
@where-items-owo
@@ -6826,7 +6826,7 @@ arte-magicum
ocean-blender@|海洋搅拌机 (OceanBlender)
buildcraft-fluxified
-brewin-and-chewin|饮酒作乐 (Brewin' And Chewin')
+brewin-and-chewin@|饮酒作乐 (Brewin' And Chewin')
infinite-source-of-water
dramatic-doors@
recipe-gui
@@ -6883,7 +6883,7 @@ forgotten-treasures
player-health-indicators@playerhealthindicators
displaycase@|陈列柜 (Display Case)
auto-look
-grimoire-of-gaia|盖亚魔典 (Grimoire of Gaia)
+grimoire-of-gaia@|盖亚魔典 (Grimoire of Gaia)
ftb-jar-mod-forge
dimensional-paintings|维度绘画*
item-recycler
@@ -7040,7 +7040,7 @@ greenery|绿色植物🌿 (Greenery🌿)
butchers-delight@|屠夫乐事 (Butcher's Delight)
large-meals-an-add-on-for-farmers-delight
explorers-delight@
-the-irregular-chef
+the-irregular-chef@irregular-chef
compat-o-plenty@compatoplenty
snow-pig@snowpig|雪猪*¨snow-pig-fabric@snowpig-fabric|雪猪 (Snow Pig)
wandering-collector@
@@ -7174,7 +7174,7 @@ aeinfinitybooster
endertech-infinity|Endertech Infinity - 已失效¨@endertech-infinity
more-frogs-fabric@morefrogs|更多青蛙 (More Frogs)
-
+@blue-skies-delight|蔚蓝乐事 (Blue Skies Delight)
create-big-cannons@|机械动力:火炮*
fastanim@
rubidium-extra@|铷 · 扩展 (Embeddium (Rubidium) Extra)
@@ -7281,7 +7281,7 @@ the-squid-game-mod
datapack-portals@datapackportals|数据包传送门*
more-beautiful-walls@
iris-flywheel-compat@iris-flw-compat
-prism-lib¨prism-fabric
+prism-lib@¨prism-fabric
map-atlases-forge@map-atlases|地图册 (Map Atlases)¨map-atlases|地图册*
@eprr|额外玩家渲染Rift版 (ExtraPlayerRenderer-Rift)
@@ -7346,7 +7346,7 @@ solar-flux|日光通量*
custom-background-color@cbc|自定义背景颜色*
simple-shulker-preview|Simple Shulker Preview - 已弃用¨@simple-shulker-preview
-bygone-nether¨bygone-nether-fabric
+bygone-nether|逝去的下界*¨bygone-nether-fabric|逝去的下界 (Bygone Nether)
@clickopener
kokoalinux-backport
mechanical-clothesline|机械晾衣绳*
@@ -7461,7 +7461,7 @@ infinity-with-mending@|无限与经验修补*
infinity-craft
hookshot@
custom-spawns
-create-misc-and-things|机械动力:实用物件 (Create : Misc & Things)
+create-misc-and-things@|机械动力:实用物件 (Create : Misc & Things)
low-fire@
infinity-buttons@infinitybuttons|无限按钮*
i-know-what-im-doing-fabric
@@ -7628,10 +7628,10 @@ beaming-drops@loot-beams-backport|掉落物光束 (Beaming drops)
protocharset@protocharset-rift
the-hordes@
-@inventory-management
+inventory-management@
soul-fired@soul-fire-d
faithfulbackrooms
-ex-machinis@
+ex-machinis@¨ex-machinis-divitiae-deorum
goety@|诡厄巫法*
torch-hit
@@ -7800,7 +7800,7 @@ tip-the-gui|界面尺寸设置优化 (GUI Scale)
copper-overhaul@
epic-fight-nbt-integration
login-protection|登录保护*
-swem@
+swem@|星虫马术 (Star Worm Equestrian Mod)
metal-barrels-legacy|金属储物桶:传承 (Metal Barrels: Legacy)
inventoryfullalert@
lead-beyond|引向超越*
@@ -7927,7 +7927,7 @@ camera-utils@
roughly-enough-items-server-component@
daves-building-extended¨daves-building-extended-fabric
@rechiseled-bookshelves
-dimensional-pockets-ii|四次元口袋2 (Dimensional Pockets II)
+dimensional-pockets-ii@|四次元口袋2 (Dimensional Pockets II)
@calculatorrinf-uwu
black-hole-storage|黑洞存储*
@@ -7953,7 +7953,7 @@ changed-minecraft-mod@
hello-crosshair@
bendy-lib@
-enchantment-transfer-forge
+enchantment-transfer-forge@enchantment-transfer
golden-crops
lil-wings@
@@ -8075,7 +8075,7 @@ custom-shop|自定义商店 (CustomShop)
another-liquid-milk@
dark-mode-everywhere-fabric@|夜间模式无处不在Fabric版 (Dark Mode Everywhere - Fabric)
-bartering-station@
+bartering-station@|易物站*
dark-paintings@
filtpick@|拾取筛 (FiltPick)
@@ -8104,7 +8104,7 @@ toomanyorigins@
evil-regeneration
food-treasure-box|食物宝盒*
revolution
-apugli@
+apugli|Apugli - 在此处更新¨@apugli|Apugli - 已停更
harvest-season
@ic2fix
@extended-bookshelves
@@ -8120,7 +8120,7 @@ mystical-nature|神秘自然*
@formula-addition-fabric|配方追加 (Formula addition)
randomlib
@kinventory
-pullup@|飞行警报
+pullup@|飞行警报*
id-primeval@primeval
colorfulazaleas@colorful-azaleas
pigeon-post@
@@ -8240,7 +8240,7 @@ qfapi-and-qsl-biomefix-1-18-2@qsl-biomefix
honey-expansion-add-on-for-farmers-delight|蜂蜜拓展 (Honey Expansion)
moguns@mo-guns
dayz-minecraft
-infusion-table@
+infusion-table|灌注祛魔台* - 已失效¨@infusion-table|灌注祛魔台 (Infusion Table)
backroomtastic
xps-things|经验之谈 (XP's Things)
gravityapi@gravity-api
@@ -8283,7 +8283,7 @@ elemental-combat|元素战斗*
roasted@|烘烤*
lotr-attack-indicator-addon@lotr-attack-indicator|魔戒攻击指示器 (LOTR Attack Indicator Addon)
steels-delight
-lotr-pouch-viewer-addon@lotr-pouch-viewer|魔戒口袋内容展示 (LOTR Pouch Viewer Addon)
+lotr-pouch-viewer-addon|魔戒口袋内容展示 (LOTR Pouch Viewer Addon)¨@lotr-pouch-viewer|魔戒口袋内容展示 (LOTR Pouch Viewer Addon) - 已失效
fastfood-delight|快餐乐事 (FastFood Delight)
festive-delight@|节日乐事*
project-arsenal
@@ -8353,8 +8353,8 @@ mssql-jdbc
packages@|自定义包裹*
giant-spawn@¨giant-spawn-fabric
@mtr-nte|纸板箱的交通扩展 (Nemo's Transit Expansion)
-serene-shrubbery
-gardens-of-the-dead@
+serene-shrubbery@
+gardens-of-the-dead@|死亡花园 (Gardens of the Dead)
pirate-hats
decor4fabric
mob-skills
@@ -8387,7 +8387,7 @@ atom-sweep|基石-清理 (Atom-Sweep)
the-gold-rush
optifabric-origins@
lng-deco
-darkrpg-reborn|黑暗RPG:重生 (DarkRPG: REBORN)
+darkrpg-reborn|黑暗 RPG:重生 (DarkRPG: REBORN)
modpack-manager@|模组管理器*
rocket-connector
hot-and-cold
@@ -8574,7 +8574,7 @@ tales-of-villagers-the-infiltrators|村民之书:渗透者 (Tales of Villagers
@morecullingextra
carpet-fixes@
er-core|ER 核心 (ER Core)
-rings-of-ascension@¨fabric-rings-of-ascension
+rings-of-ascension@|提升戒指 (Rings of Ascension)¨fabric-rings-of-ascension|提升戒指 (Rings of Ascension)
@@ -8630,7 +8630,7 @@ endemic@
lucys-sloths
@gdtweaker
drop-the-rock-no-tree-punching-compat@
-macaws-bridges-abnormals|Macaw的桥梁:Abnormals 附属 (Macaw's Bridges - Abnormals)
+macaws-bridges-abnormals@|Macaw的桥梁:Abnormals 附属 (Macaw's Bridges - Abnormals)
macaws-bridges-shroomed|Macaw的桥梁:Shroomed 附属 (Macaw's Bridges - Shroomed)
macaws-bridge-betterlands|Macaw的桥梁:Betterlands 附属 (Macaw's Bridge - Betterlands)
shroomed
@@ -8698,7 +8698,7 @@ divinerpg-processing-compat
weapon-master@|武器大师*
your-ideas|你的想法*
vanity-ancient-tool-variants@
-motion-capture-mod-mocap|动作捕捉 (Motion Capture)
+motion-capture-mod-mocap@motion-capture|动作捕捉 (Motion Capture)
wormhole-portals@wormhole|虫洞 (Wormhole)
dragon-magic-and-relics@|神龙魔法与遗物*
yummy-foods-forked
@@ -8707,7 +8707,7 @@ fullblock-energistics@
youmatter@
vanity_battle_axes
markdownmanual@|Markdown 手册(Markdown Manual)
-powershot
+powershot@
@crockpot-tweaker
automatic-tool-swap@
knight-quest
@@ -8738,7 +8738,7 @@ harvest-day|丰收之日*
hungteens-lib@pangteens-lib
more-tool|更多工具*
@ex-hud
-
+@hoarding|囤积 (Hoarding)
optifine-crash-fix
@@ -8760,7 +8760,7 @@ fxs-rail-optimization@rail-optimization
morejs@
@moresnowballls|更多雪球 (MoreSnowballs)
-valkyrien-skies-control
+valkyrien-skies-control@
the-end-expanded-mod-ice-and-fire-addon
vault-patcher@|保险库补丁*
projectile-damage-attribute@
@@ -8776,7 +8776,7 @@ containersearcher@csearcher
screencapper@
-valkyrien-skies-world
+valkyrien-skies-world@
lucky-tnt-lib@
waffles-placeable-foods@
otg-the-void
@@ -8842,7 +8842,7 @@ brewin-and-chewin-fabric@|饮酒作乐(Fabric版) (Brewin' And Chewin' [Fabr
particular
longer-login-times
broglis-plants
-rlmixins
+rlmixins@
simplehats@simple-hats|简单帽子 (Simple Hats)
timeoutout-fabric@timeoutout
fabric-furnaces
@@ -8883,7 +8883,7 @@ botanical-addons
@scoreboards-backup|计分板备份 (Scoreboards Backup)
hodgepodge@
fast-projectile-fix
-edivadlib
+edivadlib@
systeams|热力系列:蒸汽锅炉 (Thermal Systeams)
waymaker@
@@ -8964,7 +8964,7 @@ structure-floater-fabric
applecore-unofficial@|苹果核非官方版*
drilling-veins@
server-chat-history@|服务端聊天历史*
-scriptor-magicae|字符魔法师*
+scriptor-magicae@|字符魔法师*
json-entity-models-jems@|JSON实体模型 (JSON Entity Models)
@@ -9016,11 +9016,11 @@ boss-rifts
random-teleport-altar
syncpotioneffects
aobd-ore-chunks
-evasive-items
+evasive-items@
spagheters-enchants
factory-api@
tempad@|任意门*
-structure-void-toggle|结构空位切换*
+structure-void-toggle@|结构空位切换*
@cactusfix
morezombies|更多僵尸 (More Zombies)
mushroom-quest
@@ -9076,7 +9076,7 @@ golden-delicacies
hightiercrops
commandspy
slimyfloor
-daily-dad@|每日冷笑话*¨daily-dad-server@|每日冷笑话 (Daily Dad) - Daily Dad - Server
+daily-dad@|每日冷笑话* - 客户端版¨daily-dad-server@|每日冷笑话 (Daily Dad) - 服务端版
good-ending@
creeper-nuggets
@@ -9197,7 +9197,7 @@ tis-the-season
@baubles-expanded
cobblehaters
-
+mob-captains|怪物队长*
@sound-track
snowballing
@@ -9302,7 +9302,7 @@ mysterious-biomes
head-in-the-clouds@
fdmc
-rpg-style-more-weapons
+rpg-style-more-weapons@rpg_style_more_weapons
easy-experience
@@ -9319,7 +9319,7 @@ darkmenu-darkrpg-legacy-menu@darkrpg-menu
rl-artifacts|奇异饰品-RLCraft版 (RLArtifacts)
azurelib@
-harder-monster-boats-finer-boat-control
+harder-monster-boats-finer-boat-control@harder-monster-boats
spyglass-improvements@|望远镜改进*
vanilla-zoom@
time-in-a-bottled
@@ -9343,7 +9343,7 @@ amethyst-tools-mod
@grate-blocks
lush-forests
-retraining
+retraining@
ender-ore@enderore
ars-instrumentum
easier-sleeping@
@@ -9581,9 +9581,9 @@ lava-crystal
literal-sky-block
@literal-sky-block-fabric
aniskin
-mob-stacker
-mob-stacker-integration
-mob-stacker-farming
+mob-stacker@
+mob-stacker-integration@
+mob-stacker-farming@
lightspeedmod@lightspeed
coffee-delight@|咖啡乐事*
simple-sculk-dimension-deeper-dark
@@ -9611,7 +9611,7 @@ durts-nether-dungeons@nether-dungeons
health-and-hunger-tweaks
@create-track-map
improved-hoes
-orehider
+orehider@
full-grown-crop-marker@
@@ -9650,7 +9650,7 @@ bundled-delight@
@ijo-pona-poki
sk1llful@|身怀绝技 (Skillful)
-salt
+salt@
monobank
sooty-chimneys
areas-undefined
@@ -9717,7 +9717,7 @@ itemupgrader-core
itemupgrader
screenfx@
dense-ores-abundant@dense-ores
-tinkers-thinking@|匠心独运 (Tinkers' Thinking)
+tinkers-thinking@|工匠巧思 (Tinkers' Thinking)
simple-musket@
cash-ores@
stacked-dimensions
@@ -9897,7 +9897,7 @@ magnum-torch-forge@magnum-torch|卓越火炬 (Magnum Torch)¨magnum-torch-fabric
universal-bone-meal-forge@universal-bone-meal|通用骨粉 (Universal Bone Meal)¨universal-bone-meal-fabric|通用骨粉 (Universal Bone Meal)
@bottle-of-gender
-leave-my-bars-alone@
+leave-my-bars-alone@|改进的骑乘HUD*
mc-mods-enchantments-for-trade
i-need-block
spiky-spikes@
@@ -10141,7 +10141,7 @@ dont-drop-it@
nicemod-new-blocks@nicemod
forgematica@|全息蓝图 (Forgematica / Litematica-Forge)
framland
-@blahaj-legacy|布罗艾:旧版 (Blåhaj:Legacy)
+blahaj-legacy@|布罗艾:旧版 (Blåhaj:Legacy)
deep-aether@|深入天境*
the-forgotten-dimensions@
@the-sink|水槽 (The Sink)
@@ -10172,11 +10172,11 @@ blue-archive-halos
@mana-visualizer
mhcraft
-irons-spells-n-spellbooks@
+irons-spells-n-spellbooks@|Iron的法术与魔法书 (Iron's Spells 'n Spellbooks)
infernal-enchantments
reimagined-world-menu@
origins-assassin
-peculia@
+peculia¨@peculia|Peculia - 已失效
koratio
promans-origins
mermaid-mythical-origins
@@ -10252,8 +10252,8 @@ jer-ore-integration
adorkable-dogs-pocky|DK狗的Pocky (Adorkable dog's Pocky)
realistic-explosion-physics@
-nitwitification
-mutated-items
+nitwitification@
+mutated-items@
homespun@
primitive-start@
@@ -10326,7 +10326,7 @@ lands-of-icaria
wta@
@gtnh-no-nerf
pickable-villagers@
-pickable-piglins
+pickable-piglins@
book-of-blocks-fabric@mc-book-of-blocks
create-blue-skies-compat
dawncraft-mobs
@@ -10336,7 +10336,7 @@ netherracked
the-toolkits@
immersive-structures-ii-nether-edition
-mossier-deepslate
+mossier-deepslate@
celestial@
quest-giver
@@ -10685,7 +10685,7 @@ archeology-plus@
mobification
trimseffects@
tough-glass@
-celestisynth|天神剑技*
+celestisynth@|天神剑技*
golems-plus-plus@
more-axolotls@more-axolotl
perfect-accuracy@
@@ -11289,7 +11289,7 @@ saljus-quill@
derelict
@charged-charms
custom-villagers|自定义村民*
-turtlematic
+turtlematic@
remove-base-origins
@cobblemon-delights|方块宝可梦乐事 (Cobblemon Delights)
redirector@|异引定址*¨redirectionor-retro|异引定址 (Redirector)
@@ -11301,7 +11301,7 @@ thirdlife-rts@
real-peaceful-mode@|真正的和平模式*
niftyblocks
elfs-dark-dimension
-xaeros-minimap-world-map-waystones-compability
+xaeros-minimap-world-map-waystones-compability@xaeros-minimap-world-map-waystones-compatibility-forge
jagms-kiwis@
@masaadditions
improved-trident@
@@ -11335,7 +11335,7 @@ tac-craft-tactical-armor-pack
@flexible-arms|灵巧双手 (Flexible Arms)
boat-tweaks|船只调整*
new-effect-descriptions@effect-descriptions
-@diagonal-windows
+diagonal-windows@
illager-invasion@|灾厄侵扰*
@@ -11352,7 +11352,7 @@ tech-decorators@|科技装饰商*
dynamic-trees-ars-nouveau|动态的树:新生魔艺附属 (Dynamic Trees - Ars Nouveau)
@kazzmons-magecraft
-@metal-bundles
+metal-bundles@
tidal-towns|潮汐小镇*
@@ -11660,7 +11660,7 @@ roots-classic-fabric
justenoughbreeding@
simple-drills
peculiarpieces@
-loot-stages|战利品阶段*
+loot-stages@|战利品阶段*
player-stages|玩家阶段*
generation-stages|生成阶段*
ars-oscura@|黑暗魔艺*
@@ -11724,7 +11724,7 @@ invisible-frames
ultimatebackportmod
-
+true-ending
@sorted-enchants|附魔排序 (Sorted Enchants)
constructs-armory-1-18-2-port@construct-armory-updated
@@ -11783,7 +11783,7 @@ totally-enough-pain@
custom-village|村民调教*
animaticareforged
shear-cows
-better-fps
+fps-boost¨better-fps|FPS Boost - 已失效
@afkfish
@rend
@@ -11934,7 +11934,7 @@ prehistoric-nature-cambrian-dimension
@mushroom-mod
better-end-sky¨better-end-sky-fabric
valkyrie@
-soaring-structures-2
+soaring-structures-2|Soaring Structures 2 - 完整版¨soaring-structures-2-clean-version-fabric|Soaring Structures 2 - 轻量版
prehistoric-nature-ordovician-dimension
fadeless@
create-tweaked-controllers@
@@ -11967,7 +11967,7 @@ forest-x-reborn@
exlines-bark-carpets@bark-carpets
exlines-more-carpets
ne|猫艺:附魔 (NekoEnchantment)
-unearthed-journey|出土之旅*
+unearthed-journey@|出土之旅*
prehistoric-nature-devonian-dimension
mwtis-stone-expansion@stone-expansion
smooth-entity-light
@@ -12054,7 +12054,7 @@ oryls-adventures
elden-craft
ruok@|钌 (RuOK)
deep-dark-regrowth
-textrues-embeddium-options@|TexTrue的Embeddium视频界面 (TexTrue's Embeddium Options)
+textrues-embeddium-options@|TexTrue 的 Embeddium 视频界面 (TexTrue's Embeddium Options)
silent-mine
headhunter-mod@shadows-formidable-foes-the-headhunter
yungs-better-jungle-temples@|YUNG的丛林神庙优化 (YUNG's Better Jungle Temples)¨yungs-better-jungle-temples-fabric|YUNG的丛林神庙优化 (YUNG's Better Jungle Temples)¨yungs-better-jungle-temples-neoforge|YUNG的丛林神庙优化 (YUNG's Better Jungle Temples)
@@ -12190,7 +12190,7 @@ modification-of-critical-hit@|暴击改革*
improved-damage-framework@idf
@shulker+
@biome-tag-villagers
-combat-bash
+combat-bash@
@simpleprivatechest
marbleds-villager-hats@
@@ -12221,7 +12221,7 @@ akkamaddis-hadite-coal|Hadite Coal - 新
akkamaddis-arsenic-and-lace|Arsenic and Lace - 新
-xiyus-exchanted-book|饩雨的附魔书 (xiyu's Exchanted book)
+xiyus-exchanted-book@xiyus-enchanted-book|饩雨的附魔书 (xiyu's Exchanted book)
la-abuelita
rotted-forge@rotted
rustic-furniture-compat
@@ -12292,11 +12292,11 @@ super-better-grass
configurable-boss-bars@
nuclearcraft-neoteric@
beastly-beacons
-creeplets@
+creeplets|Creeplets - 已失效¨@creeplets
thermoo@
restapi@rest-api
@advanced-ui-scale
-@cfb
+@umines
health-disease|健康&疾病 (health&disease)
@@ -12508,7 +12508,7 @@ oculus-particle-fix@
urkaz-moon-tools@
the-midnight-lurker@
realm-rpg-imps-demons
-multiverse
+multiverse@multiverse_mod
strangecreatures
@@ -12528,8 +12528,8 @@ uniquedungeons
@cobbledex
cobblemon-info@
enderman-overhaul@|末影人革新*
-cloak-dagger
-oworld2create|别创建新世界! (0World2Create)
+cloak-dagger@cloak-and-dagger
+oworld2create|别创建新世界! (0World2Create)¨oworld2create-refoxed|别创建新世界! (0World2Create)
dungeons-in-dimensions
infernal-bugz
@cobblemon-gts
@@ -12690,7 +12690,7 @@ autofire-bow-enchant
rideableravagers@
passive-searchbar|被动搜索栏 (Passive SearchBar)
flower-mimics@
-vegan-delight|纯素乐事*
+vegan-delight@|纯素乐事*
bloodmooncmsfix-bmcmsfix
evoodooers
giant-overhaul
@@ -12828,7 +12828,7 @@ localized-chat@
prominent-create
bad-lightning-no-cookie
equipment-standard@|装备标准 (EquipmentStandard)
-snowly@
+terra-armory@
advanced-enchantments@|进阶附魔*
oaks-nature@
geophilic-reforged
@@ -12853,7 +12853,7 @@ instant-village-building
venus@
nbt@
-separated-leaves@
+separated-leaves@separatedleaves
pangteens-immortal-adventure@|PangTeen的平凡冒险 (PangTeen's Immortal Adventure)
simple-shops@simpleshops|简单商店*
forgiving-border|仁慈的世界边界 (ForgivingBorder)
@@ -12902,7 +12902,7 @@ eldritch-end@
quit-or-not
spelled-mobs
let-fish-love
-exposure@
+exposure@|底片*
@fast-portals
pick-up-me-please@pickup-me
bouncy-creepers
@@ -13055,7 +13055,7 @@ wool-collection@|羊毛装饰方块 (The Wool Collection)
random-mob-sizes@|随机生物模型大小*
unexperienced
-balanced-crates
+balanced-crates@
ducky-periphs@
@norefreshscroll
@@ -13182,8 +13182,8 @@ notrample
industrial-foregoing-souls@
fuelgoeshere@
initial-dimension
-youkais-homecoming|妖怪们的归家 (Youkai's Homecoming)
-utilitarian
+youkais-homecoming@youkaishomecoming|妖怪们的归家 (Youkai's Homecoming)
+utilitarian@
classic-sword-blocking|剑之格挡*
@flutterandflounder
@@ -13229,7 +13229,7 @@ item-despawn-timer@
@is-this-mod-legit
aylyth@
no-shade@
-dimensional-threads@
+dimensional-threads@dimthreads
jonns-trophies@
it-follows-exclamation@it-follows!
@loggerhead-luminancies
@@ -13413,7 +13413,7 @@ create-oxidized@create_oxidized
green-soul
everyxdance@|一起跳舞! (EveryXDance)
-applied-flux
+applied-flux|应用通量*
@tinkers-delight|匠魂乐事 (Tinkers' Delight)
unnamed-desert-structures@
@@ -13430,7 +13430,7 @@ macos-input-fixes@|MacOS输入修复 (MacOS Input Fixes)
@astronomical
-ned|猫艺:无尽难度 (Neko Endless Difficulty)
+ned|猫艺:终焉 (Neko End)
magical-torches@
dynamical-compass@
better-questing-lord-of-the-rings-addon|更好的任务-魔戒扩展 (Better Questing - Lord of the Rings addon)
@@ -13452,7 +13452,7 @@ abyssal-decor
reddens-stone-lanterns|Redden的石灯笼 (Redden's Stone Lanterns)
heaven-burns-red|红烧天堂*
-apotheosis-modern-ragnarok|神化:现代诸神黄昏*
+apotheosis-modern-ragnarok|神化:现代诸神黄昏 (Apotheosis Modern Ragnarok: Zero)
@bedgoboom
block-properties|方块属性*
custom-chest-loot|自定义战利品箱*
@@ -13636,7 +13636,7 @@ osog-city-cars
immersive-vehicles-iav-parts-pack
tfmg-virtual-oil-deposit|工业长路:虚拟油田 (TFMG: Virtual Oil Deposit)
@alinlib
-croparia-if@|矿石作物/魔种之咏 IF版 (Croparia-IF)
+croparia-if@|矿石魔种 (矿石作物/魔种之咏 IF版) (Croparia-IF)
@actionlib
simple-close-button@
@@ -13720,7 +13720,7 @@ everymole@
mutant-iceologer@mutant-iceologer-mod
@days-gone
@extra-loot|额外战利品 (Extra Loot)
-relics-of-gaming-continued@
+relics-of-gaming-continued@|游戏遗物:延续*
endermen-plus@
tiptapshow@
villager-gunners
@@ -13730,7 +13730,7 @@ prodigy-mechanics
just-an-end-anchor@
memory-usage-title@|标题显示内存*
-
+string-utilities@|字符串工具*
sweeper-maid@|清洁女仆*
more-enchantments_|更多附魔 (More Enchantments)
@@ -13764,7 +13764,7 @@ sword-of-gods
horse-stonks@
pillarger
-ars-additions
+ars-additions@
visualsound
@cyclic-crash-fixer
lavender-api@lavender
@@ -13784,7 +13784,7 @@ heterorustichromia
video-extension-for-fancymenu-forge¨video-extension-for-fancymenu-fabric
easy-npc@
-the-dawn-era-delight@
+the-dawn-era-delight@|黎明纪元乐事*
dynamic-craft
startinginventory
wireless-speakers|无线扬声器*
@@ -13817,7 +13817,7 @@ hearths
prey-effect
venturer@
wheres-my-wither-storm
-alexs-mobs-interaction@
+alexs-mobs-interaction@|Alex 的生物:交互*
@@ -13837,7 +13837,7 @@ thermals-delight
better-farmers-combat
simple-butchery¨@simple-butchery|Simple Butchery - 已失效
-butchery
+butchery@
enchantment-reveal@
@@ -13888,7 +13888,7 @@ ad-astra-extra-additions@
infinite-fluid-bucket@|无限流体桶*
@voxy
-vmod@
+vmod|VMod - 已失效¨@vmod
minestuck
star-wars-planets-ad-astra@
@@ -13934,7 +13934,7 @@ vocalized
@advanced-framework|高级框架 (Advanced Framework)
-kubejs-irons-spells
+kubejs-irons-spells@isskjs
revelationaryjs@
arche
caverns-and-chasms@
@@ -14032,7 +14032,7 @@ natural-nether-portals
hotbaaaar@|忄夬扌疌木兰 (HotBaaaar)
ui@ui-lib
@random-items
-lucky-clover
+lucky-clover|Lucky Clover - 已失效¨@lucky-clover
@better-portals|更好的传送门 (Better Portals)
social-distance@|社交距离*
gt@gtnn
@@ -14040,7 +14040,7 @@ hunger-plus@|饥饿+*
melee-ex-machina-meexma@melee-ex-machina
arc@
-amlib
+cores@
@citymod|我的城市 (City Mod)
too-hard-to-dig@|隐岩砥凿 (Too Hard to Dig)
@@ -14123,7 +14123,7 @@ temporal-api@
@underworld-magic-craftsmanship|幽法工艺 (Underworld Magic Craftsmanship)
callable-horse-unofficial-fabric-port|召之马来 Fabric版 (CallableHorse Unofficial Fabric port)
-
+@better-combat-experience|更好的战斗体验 (Better Combat Experience)
shiny-horses-forge-enchantable-horse-armor@shiny-horses-forge
spartan-weaponry-simpleores|斯巴达的武器:简单矿石 (Spartan Weaponry: SimpleOres)
@@ -14143,7 +14143,7 @@ ae2-import-export-card@
@repairable-disc-11
-
+@fabric-permissions-api
@khinkali-mod
configured-defaults@
bens-sharks@
@@ -14213,7 +14213,7 @@ tragicmc|悲惨世界1 (TragicMC)
auditory-continued@
create-more-copycats@
-farlands-reborn@modern-farlands-mod
+farlands-reborn@
momento@
@@ -14338,7 +14338,7 @@ pet-collecting@
totallybalancedfallflying
giacomos-experience-seedling
@server-notify
-config-api
+config-api¨@config-api|Config API - 缺少部分
nethers-delight-refabricated@|下界乐事:重织*
@pumpkin-pie-delight
tipsylib@
@@ -14372,7 +14372,7 @@ disguiselib@
@mtr-resource-protector|MTR 资源包保护工具 (MTR Resource Protector)
station-architecture
-enchantmentrequirements
+enchantmentrequirements@enchantment-requirements
enchantment-enhancements@
mo-enchantments
soul-like-enchantment
@@ -14426,7 +14426,7 @@ tfc-barrens@
chiselse-bits-for-tfc@chisels-bits-for-tfc
tfc-fresh-water-everywhere@
tfc-more-swords
-galleria@
+galleria¨@galleria|Galleria - 已失效
constructs-casting@
tinkers-innovation@tinkersinnovation|工匠创新 (Tinkers' Innovation)
@@ -14454,7 +14454,7 @@ create-connected-fabric-experimental@create-connected-fabric
wastify@
starter-structure@|初始建筑*
-mobblocker@
+mobblocker¨@mobblocker|MobBlocker - 已失效
giacomos-foundry
@blocky-bass
@toms-mobs
@@ -14490,7 +14490,7 @@ nether-overload
ender-relay@ender-relay-ff
enchanted-effects
emi-create-schematics@
-the-silence@
+the-silence|The Silence - 已失效¨@the-silence
infinite-music-fabric@infinite-music
kakapos@
@tome-reader
@@ -14595,7 +14595,7 @@ huge-structure-blocks@
bodyhygiene
cafes-birding@
kafs-valentine-special@
-create-deepfried@
+create-deepfried@|机械动力:油炸 (Create: Deepfried)
sleepable-create
create-railways-navigator@|机械动力:铁路导航*
speed-drifter@speed_drifters|MC飞车 (MCSpeed)
@@ -14611,7 +14611,7 @@ built@
diyotweaks
extra-spell-attributes@extra-rpg-attributes
@create-contraption-creatures
-immersive-vehicles-iv-mts-texel-arsenal-pack@mts-ivv
+immersive-vehicles-iv-mts-texel-arsenal-pack
creature-whisperer
@@ -14664,7 +14664,7 @@ telepass@
offhandbanning|禁手令 (Off Hand Banning)
fantasy-craft|幻想工艺 (FantasyCraft)
breeding-potion@|繁殖药水*
-
+@servuxforged
loading-tips|加载提示 (Loading Tips / Pineapple Tips)
@highernether|更高的下界 (Higher Nether)
better-one-block|更好的单方块*
@@ -14966,7 +14966,7 @@ extra-addition-s@extra-additions
@herbal-delight
-@compatlib
+@compatlib|旧版本兼容工具 (LegacyCompatibilityUtils / CompatLib)
warriors-of-past-epoch
the-ghast-cow@
@alternate-current-unofficial
@@ -15102,7 +15102,7 @@ realm-rpg-quests-rewards
unlucky-tnt@
fastboot@
-tardis-refined@
+tardis-refined@|改良的TARDIS (TARDIS Refined)
ocean-villager-trader@
@precise-manufacturing|机械动力:精制工艺 (Create: Precise Manufacturing)
@create-new-age-alexs-caves-integration
@@ -15225,7 +15225,7 @@ leadable-bats
canes-wonderful-spiders
-@scribble
+scribble@
@text-formatting-everywhere
extended-item-information
mod-sound-volume-options@mod-volume-options
@@ -15239,12 +15239,12 @@ wither-totem@wither-skeleton-totems
@player-bones
more-apples-hyj|更多苹果 (More Apples)
-fancy-enchantments|奇异附魔*
+fancy-enchantments@|奇异附魔*
ash-of-sin-adventure-dimension|罪业余烬:冒险维度 (Ash Of Sin: Adventure Dimension)
push-forward@|猪突猛进*
ymn@|你现在是我的了! (You're Mine Now!)
thermal-endergy@
-mekanism-turrets-fences
+mekanism-turrets-fences@mekanism-turrets
nuka-cola-classic
draconic-machinery
@voxelprints-halloween
@@ -15287,7 +15287,7 @@ monochromatic-blocks@
@inventive-inventory
boxlib@
-mokels-bossfight-saphyra
+the-shattered-goddess-mokels-bossfight-saphyra
resource-explorer@resource_explorer
@patcher
autoreconnectrf@
@@ -15402,7 +15402,7 @@ jcraft-eyes-of-ender
@reset-keys-confirmation-screen
idle-boost@
-fabricbettergrass@|更好的草地 (FabricBetterGrass)
+bettergrassify@|更好的草地 (BetterGrassify)
@lightweight-inventory-sorting
@stonecutter-gui-remastered
@loom-gui-remastered
@@ -15458,10 +15458,10 @@ lush-cave-trader@
oceans-enhancements@
xp-jellies
aromatic
-onekeyminerforge|一键连锁Forge版 (OneKeyMinerForge)
+onekeyminerforge@|一键连锁Forge版 (OneKeyMinerForge)
retargetato@|约谊敌众 (ReTargetato)
-iceandfire-fabric@|冰火传说Fabric版 (IceAndFire Fabric)
+iceandfire-ce@|冰火传说社区版 (IceAndFire Community Edition)
tomanos-vehicle-mod
ancient-sands@
hs-bosses
@@ -15518,7 +15518,7 @@ nebulus-jungle-trader@nebulus-trader-for-the-jungle
mushroom-villager-trader@
nebulus-better-portals¨@nebulus-better-portals|Nebulus Better Portals - 已失效
resource-ghouls
-avaritianeo|无尽贪婪Neo (AvaritiaNeo)
+avaritianeo@|无尽贪婪Neo (AvaritiaNeo)
create-legacy@
@create-bronze-age
wind-bottle@|风瓶 (wind bottle)
@@ -15582,7 +15582,7 @@ the-rise-of-minerals|矿石崛起*
gregtech-steam-additions
gregtech-nomifactory-edition
create-ethium@ethium
-create-contraption-terminals@
+create-contraption-terminals@|机械动力:动态终端*
strata-recipes
strayed-fates-forsaken
@@ -15619,7 +15619,7 @@ yamazakura-reforked|山樱之刃重置版 (Yamazakura reforked)
apothic-spawners
-sinocore@|华夏核心 (SinoCore)
+sinocore|华夏核心 (SinoCore)
tinted-dyes@
dynamic-trees-ecologics@|动态的树:丰富的生态附属 (Dynamic Trees - Ecologics)
craftablecapes@|可合成的披风 (Craftable Capes)
@@ -15627,7 +15627,7 @@ tamago@
@clickthrough2.0
cat-jammies-fabric@cat-jammies
open-computer-resurrected
-@advancements-reloaded
+advancements-reloaded@
oc2r
eu-p2p-tunnel@eup2p|EU能源P2P通道 (EU P2P Tunnel)
@hold-onto-everything
@@ -15659,7 +15659,7 @@ esf-entity-sound-features@esf|实体声音特性 (Entity Sound Features)
nomoreglowingpots@
smooth-particles@
-5544292@unloaded-activity
+unloaded-activity@
@improperui
neverenoughanimation
reds-stagger-parry
@@ -15786,7 +15786,7 @@ ftb-quest-localizer|FTB Quest 本地化 (FTB Quest Localizer)
modpack-update-checker@
minimotd-fabric
effecttimerplus@
-thaumic-concilium@
+thaumic-concilium@|神秘议会*
equivalentexchange
coral-up@
@@ -15838,7 +15838,7 @@ modular-flower-pots|模组化花盆*
repurposed-structures-floralis-compat-mod|结构变体:Floralis 兼容模组 (Repurposed Structures - Floralis Compat Mod)
balanced-shield@
-more-potion-effects@|更多药水效果 (More Potion effects)
+more-potion-effects@|更多药水效果*
jungle-villages
truecherry
@revamped-trades
@@ -15857,13 +15857,13 @@ gt-wireless|格雷无线科技 (GT Wireless)
forcejava21@|强制 Java21 (ForceJava21)
blessings@|祝福*
crafted-core@
-call-from-the-depths@|深渊召唤*
+call-from-the-depths|深渊召唤*¨@call-from-the-depths|深渊召唤 (Call From The Depths) - 落后 更新不及时
@clover-fabric|四叶草Fabric版 (Clover Fabric)
champions-unofficial@|冠军/强敌再续 (Champions-Unofficial)
aetherworks-refracted@
eidolon-repraised@eidolonrepraised
create-northstar@
-create-pattern-schematics@
+create-pattern-schematics@|机械动力:模式蓝图 (Create: Pattern Schematics)
magic-merchant
aether-villages@|天境村庄*
tricky-trials@tricky-trials-1.21
@@ -15909,7 +15909,7 @@ reairdrop-supply@|空投供应:重生 (ReAirdrop Supply)
@simply-create-model-mod|简单动力 (Simply Create Model)¨@simply-create-model|简单动力 (Simply Create Model) - 资源包
mountains-poem|峰峦辞 (Mountain's Poem)
@playtime-reborn|游戏时长计数器:重生 (Playtime Reborn)
-@flavor-immersed-daily|烟火凡人心 (Flavor Immersed Daily)
+
moon-between-medicine|在月亮之间:药学拓展 (Moon Between : medicine)
craftable-chainmail-reborn|可制作的锁链甲:重生*
@@ -15948,7 +15948,7 @@ horde-moon@
laser-mod-2
mekanism-lasers
wizards-reborn@
-
+tinkervillager|工匠村民 (Tinker Villager)
customnpc-contentback
debug-stick
useful-bat
@@ -15974,7 +15974,7 @@ lets-do-wildernature@|野性自然 ([Let's Do] WilderNature)
neodymium-unofficial@nd1710|钕:非官方版*
@carbon-performance|碳 (Carbon)
coloredglowlib@
-@better-snow-coverage|更好的雪覆盖
+@better-snow-coverage|更好的雪覆盖 (Better Snow Coverage)
trade-enchantment-display@|交易附魔显示*
eternal-starlight@|永恒星光*
@@ -16000,7 +16000,7 @@ fired-pots@
under-the-moon@|驻月夜下 (Under the Moon)
coffeegs-colorful-world
jeb_-wool@|jeb_羊毛 (jeb_ Wool)
-illage-and-spillage-finally-ported
+illage-and-spillage-finally-ported@illage-and-spillage-respillaged
more-runes-more-magic-series@|更多符文 (More Runes)
battlemages-more-magic-series@
eidolon-rebrewed-data-recipes
@@ -16034,7 +16034,7 @@ aaa-particles@
@anno
compass3d@
@clockin
-diggus-maximus-reborn@
+diggus-maximus-reborn@|连锁挖掘重制版*
satisfying-buttons@
item-on-chest-fabric@item-on-chest
industrialreborn@
@@ -16152,7 +16152,7 @@ hold-easy-place@|保持简单放置*
-
+endlesscompat|无尽贪婪·伪附属 (EndlessCompat)
spartan-and-fire-lightning-edition|斯巴达之冰与火-雷电版 (Spartan and Fire - Lightning Edition)
the-twilight-forest-deforestation-edition|暮色森林 - Deforestation 版 (The Twilight Forest - Deforestation Edition)
@@ -16163,7 +16163,7 @@ ynet-an-xnet-fork
rosatech
nutrition-gtceu@
nutrition-unofficial-extended-life@|营养学非官方延续版*
-
+@manametalmod-tweaks|魔金微调 (ManaMetalMod Tweaks)
ae2-crafting-tree|合成树 (AE2: Crafting Tree)
client-automatic-doors@client_automatic_doors|客户端自动门*
@@ -16222,7 +16222,7 @@ stop-repeating-command-block-reborn@|停!循环型命令方块:重生*
parry-us@parry!|予以刀振/格挡! (Parry)
frycooks-delight@|油炸乐事 (Frycook's Delight)
-advancedae@
+advancedae@|高级AE (AdvancedAE)
pretty-pipes-fluids|更好的管道:流体 (Pretty Pipes: Fluids)
webstone@
@@ -16285,7 +16285,7 @@ crossstitch@
free-camera-api@|自由相机API (FreeCameraAPI)
@flashback
bloodmagic-meteors-jei-support|血魔法陨星显示 (BloodMagic Meteors JEI Support)
-limited-damage-indicator-particle|伤害粒子限制*
+limited-damage-indicator-particle@|伤害粒子限制*
netherite-extension@netheriteextension|下界合金拓展续作*
immersive-farming@|沉浸农业*
@vpb-too-many-guns
@@ -16306,7 +16306,7 @@ sign-edit-lite@signeditlite|告示牌编辑轻量版*
@book-formatting
@morewaterlogging
-
+@customspyglassmagnificationoverlay|自定义望远镜覆盖层 (CustomSpyglassMagnificationOverlay)
@chatting
@crosshair
@@ -16350,14 +16350,14 @@ stackmob-fabric@
itemindicator@
kenshiro-mod|北斗神拳*
roamers
-
+@legacycaneditsign
title-screen-mobs@
antiblocksrechiseled@
end-ores
tnt-expanded
-
+glimmering-tales|微光纪事*
fluid-interaction-tweaker|自定义流体交互*
kubejs-extended-crafting
kubejs-addon-blood-magic
@@ -16399,7 +16399,7 @@ revenge-mark|遗怨待战/复仇标记*
@create-coggle
-create-enchantable-machinery@
+create-enchantable-machinery@¨create-enchantable-machinery-fabric
bad-eyes|近视眼*
bad-dreams@|噩梦*
turtlebois-rpg-classes
@@ -16444,7 +16444,7 @@ damage-incorporated@
exmodifier@|极墨重锻-革故鼎新 (ExModifier)
-
+@creepersburn-reborn
lazy-dfu-forge|DFU载入优化 [FORGE] (Lazy DataFixerUpper(LazyDFU) [FORGE])
@creaturechat
@@ -16477,7 +16477,7 @@ upgraded-additional-trims
create-let-the-adventure-begin@
bountiful-critters
flashlight-mcowners
-smartpot@
+smartpot@|智能锅 (SmartPot)
merciful-void|渊佑堕者/慈悲的虚空*
@@ -16498,8 +16498,8 @@ tfc-hammer-time@
tfc-better-stone-age
battery-status-info@batterystatusinfo
mineteam@mine-team
-
-
+@nocreativemode|禁用创造模式 (NoCreativeMode)
+@darktitlebar-forge
croaklib@
nbt-copy
@@ -16510,7 +16510,7 @@ modern-debugify
recipe-essentials-forge-fabric
zombie-villager-control|催起死尸/僵尸村民控制*
@1.8-pvp-mod
-
+@blackscreenquiet|黑屏安静 (BlackScreenQuiet)
@too-many-shortcuts
better-horse-movement
@@ -16545,7 +16545,7 @@ tfc-woodworking@
tfc-auroras@auroras
greg-paintings@
gtmthings
-create-trading-floor@
+create-trading-floor@|机械动力:交易站点 (Create: Trading Floor)
@fishy-create-stuffs|Yu的机械动力附属 (Fishy Create Stuffs)
biolith@
hungrier@
@@ -16586,7 +16586,7 @@ sodium-extra-information@
@bulk-villager-trading
omniscience
illusion-onslaught
-rar-compat
+rar-compat@
wards
moremcmeta-emissive-forge@moremcmeta-emissive¨moremcmeta-emissive-fabric
@fantazia
@@ -16681,7 +16681,7 @@ jadens-nether-expansion@
configurable@
-
+@chatpositiondisplay|聊天框坐标显示 (ChatPositionDisplay)
@forge-early-loading-screen-fabric|Forge 加载屏幕移植 ((Neo)Forge Early Loading Screen for Fabric)
@@ -16691,16 +16691,16 @@ patpat
@modernlifepatch|摩登生活补丁 (Modern Life Patch)
@ivr
rusticdelight-refabricated@|乡村乐事:重织 (RusticDelight: Refabricated)
-scape-and-run-meshi@
+scape-and-run-meshi@|逃逸:寄生饭 (Scape and Run: Meshi)
@bad-apple-world-preset
gastronomy-works@
@create-air-fabric
colorfulwater
yungs-cave-biomes@|YUNG的洞穴生物群系 (YUNG's Cave Biomes)¨yungs-cave-biomes-fabric|YUNG的洞穴生物群系 (YUNG's Cave Biomes)
-
-
-
+@legacy-command-registry|旧式命令注册 (Legacy Command Registry)
+@screensaver|屏保 (Screensaver)
+@startuptone|启动提示音 (StartupTone)
@@ -16729,13 +16729,13 @@ mc-mods-arrows-infinity-fix
bundles-backport
its-magispelling-time
-
+@windows-starting-overlay|Windows启动界面 (WindowsStartingOverlay)
@dontsneakprogress|偷跑禁止 (DontSneakProgress)
@unkindled
recipe-generator@
@carbonfltr
argonmod@|氩 (Argon)
-foggy-pale-garden@
+foggypalegarden@
@items-stack
arkane-domains
@@ -16763,7 +16763,7 @@ cc-wasm
legacy-patch
@glowup
-
+@customsavedirs
just-enough-magiculture
ae2-jei-integration
entangled-fix
@@ -16778,8 +16778,8 @@ wandrous@
cnpc-dbc-addon@customnpc-plus-dbc-addon
salmons-genesis
the-afterdark
-
-
+endlesssword|无尽拔刀剑 (Endless Sword)
+bh3|崩坏3
loaded-mods-checker@loadedmodschecker|存档模组核对*
i-have-slept@|朕已安寝 (I have slept)
@@ -16797,10 +16797,10 @@ resettleable-traders-forge-fabric@resettleable-traders|流浪商人安家 (Reset
ihearttfc@
itemphysic-1-7-10-unofficial@itemphysic-1.7.10-unofficial|物品物理掉落非官方版 (ItemPhysic Legacy Unofficial)
jei-area-fixer
-ars-elixirum¨ars-elixirum-neoforge¨ars-elixirum-forge
+ars-elixirum-forge@ars-elixirum¨ars-elixirum-neoforge¨ars-elixirum
gold-nugget-recycling
sodium-options-api@
-
+@totom-register
crazyae|疯狂的AE (CrazyAE)
ae2-crafting-tree-legacy|AE2合成树-传统版 (AE2CT-Legacy)
@@ -16808,14 +16808,14 @@ ae2-alchemistry-addon
novaultlimit@
game-discs@|游戏光碟*
-
+@luncheon-meat-s-delight|午餐肉乐事 (Luncheon Meat 'S Delight)
vanilla-plus-resourceful-tools
create-horse-power@
ars-technica
plustic-reforged@
-spawners-plus@spawners_plus
-dracolotl
+spawners-plus@spawners_plus|刷怪笼拓展 (Spawners+)
+dracolotl@
sacks-n-such@
@@ -16828,7 +16828,7 @@ corpsecomplex-unofficial@
@starlish
languagereloadunofficial@
mekanism-covers@
-enchanted-wilderness-elegant-countryside@enchanted-wilderness-elegant-countryside.|优雅乡下 (Elegant Countryside)
+enchanted-wilderness-elegant-countryside@enchanted-wilderness-elegant-countryside.|雅致乡下 (Elegant Countryside)
better-durability-reforged|更好的耐久度:重铸*
sos|我来助你! (SOS)
cherry-on-1-12-2@cherry_on_1.12.2|1.12.2的樱花 (Cherry_on_1.12.2)
@@ -16892,7 +16892,7 @@ creepercollateral
findmyitemsandfluids
@numeric-experience-info
aquapatch
-aviator-dreams
+aviator-dreams@
macaws-stairs@
cross-stitch-colours@
gaming-deco@
@@ -16928,12 +16928,12 @@ watersource-reborn@|水源:重生 (WaterSource: Reborn)
locks-unofficial@|锁:非官方版 (Locks-Unofficial)
just-in-nether@
-jinghui|镜回 (jinghui)
+jinghui|镜回 (JingHui)
@ripples-of-the-past-mandom-addon|RotP:男人领域附属 (Ripples of the Past: Mandom Addon)
@auto-gg
better-brightness-slider-respawn@
-
+@playergyro
animatica-foxified@
mcchatgpt@
@aimobs
@@ -16965,10 +16965,10 @@ sciophobia@|全局自定义文本阴影渲染*
lithic-addon-mod
light-blocks-from-1-17@
light-and-shadow@
-@excavein
+excavein@
spanish-delight-a-farmers-delight-add-on@spanish-delight|西班牙乐事 (Spanish Delight)
ars-nouveaus-flavors-delight@arsdelight|新生乐事 (Ars Nouveau's Flavors & Delight / Ars Delight)
-oceanic-delight|海之乐事*
+oceanic-delight@|海之乐事*
the-player
kelka-craftable-animals@craftableanimals
super-hyper
@@ -16991,7 +16991,7 @@ trailier-tales@
mixinjs-coremod
mcwen
rank-system|评分系统*
-
+@creative-one-punch-forge
bundle-inventory|Bundle Inventory - 问题反馈¨@bundle-inventory
scroll-tweaks@scrolltweaks|鼠标滚轮调整*
neko-server@
@@ -17010,7 +17010,7 @@ backup-manager@
the-root-of-fear@
the-modifiger@
@keypreset|键位预设 (KeyPreset)
-
+@tweakeroo-features-menu
sanity-prequel|理智:前传 (Sanity: Prequel)
ayame-paperdoll@|Ayame 纸娃娃 (Ayame PaperDoll)
@registry-helper-lib
@@ -17023,7 +17023,7 @@ roost-2-flying-higher
@lvlz-better-nether
@lvlz-better-end
cc-shops@
-modular-machinery-reborn
+modular-machinery-reborn@
flare@
rs-crafting-monitor-in-grid@
@photonics
@@ -17040,7 +17040,7 @@ moestweaks
prickle@
farming-3x3@
trickery@
-kebab
+kebab@bogismokkebab
farmers-croptopia@
mc-mods-trees-drop-apples
@infinite-dimensions
@@ -17048,7 +17048,7 @@ mc-mods-trees-drop-apples
mofus-broken-constellations
starlance@
beneath@
-@simpleclientbase|简易客户端:基础 (SimpleClientBase)
+@simple-client-base|简易客户端:基础 (SimpleClientBase)
ultimate-enchantment@|终极附魔*
@@ -17078,7 +17078,7 @@ scalablelux@
weather-changer@
iron-nugget-recycling
@picohud
-
+re-exposer@|Exposer 重置版 (Re-Exposer)
gtmoreoreprocessing@
computercraftedu
bibliocraft-legacy
@@ -17129,7 +17129,7 @@ enviromine-continuation@
@toffys-hooks
mutants-buff
-
+@blockplayer|玩家方块模型 (BlockPlayer)
terrablenderfix@
craterlib@
@caramel-chat
@@ -17142,7 +17142,7 @@ unilib@
modchu_playerformlittlemaidfml
quiet|迁噪碍音/玩家禁言*
-
+galacticraft-compatibility@|星系兼容*
@bettertab
modular-machinery-reborn-mekanism@
@@ -17157,22 +17157,22 @@ create-logistics@
create-pneumatic-equipment@
collisiondamage|碰撞伤害 (CollisionDamage)
@absolutely-proprietary
-tensura-reincarnated@
+tensura-reincarnated@|关于我转生变成史莱姆这档事:转生 (Tensura: Reincarnated)
transformer|奥特化身:无限 (Becomes Ultraman:Infinity)
henshin|变身*
ridiculous-world
quartz_craft@|石英工艺 (Quartz_Craft)
cursed-stare@
-legendblade|传世之刃 (legendblade)
+legendblade|传世之刃 (LegendBlade)
@hypothermic-reforged
-scape-and-run-parasites-extra@
+scape-and-run-parasites-extra@|逃逸:寄生体扩展 (Scape and Run: Parasites Extra)
lauchs-shutters@shutters
golden-foods@golden-foods!
@alexscaves-delight
@spmreborn|烤地瓜:重生 (Sweet Potato Reborn)
dragon-fight-config-endergetic-addon
-
+hour-clock-24@|24小时制 (24Hourclock)
celestial-forge|星月再锻*
@@ -17196,13 +17196,13 @@ more-loom-variants@
more-feeding-trough-variants@
more-smoker-variants@
manascore@
-ames-mobs-configuration|AME生物配置
+ames-mobs-configuration|AME生物配置 (Ame's Mobs' Configuration)
display-delight@
realistic-torches-roa-edition
@slime
-@x-backup
+x-backup@
iron-barrels-forge@iron-barrels
spectral-decorations@
@@ -17221,10 +17221,10 @@ visualjs
lights-out
a-night-stalker
the-faceless-duo
-scape-and-run-parasites-meteorite@
+scape-and-run-parasites-meteorite@|逃逸:祸从天降 (Scape and Run: Parasites Meteorite)
cravencrafts-bloody-bits@
-
+@carcrash|撞车 (CarCrash)
fastlang
min-3-hall-a@
@@ -17232,10 +17232,10 @@ modular-machinery-reborn-energistics@
tweaks-for-galactic-science
java-version-checker
chatcopyrite
-
+chat-boost|聊天增强*
terracart
noteblock-backport@
-scape-and-run-parasite-dimension@srdimension
+scape-and-run-parasite-dimension@srdimension|逃逸:寄染之世 (Scape and Run: Dimension)
hexdebug@|咒术调试 (HexDebug)
paths-of-sin@
@mapperplugin
@@ -17280,7 +17280,7 @@ tumbleweed-wasteland
irons-jewelry
atlas-api
loading-screen-messages
-
+@pipesounds
nemos-invertory-sorting@nemos-inventory-sorting
@better-ux
@hudmini
@@ -17288,7 +17288,7 @@ ribs-panning
hotkettles@
gift-drop@
dragns-livestock-overhaul@
-storage-drawers-unofficial|储物抽屉非官方版*
+storage-drawers-unofficial@|储物抽屉非官方版*
centrifuge-tiers-reproduced@
dense-mekanism@
@@ -17301,31 +17301,31 @@ ender-wyrmlings
new-classic-battle-towers
-chitin-equipment@
-mowzies-cataclysm
+chitin-equipment@|甲壳装备*
+mowzies-cataclysm|Mowzie的灾变 (Mowzie's Cataclysm)
stardew-fishing-fabric@
piglin-slayer@
cataclysm-tools@
witherite-gear
faunus@
oo-ee-a-e-a@
-
+@translator|翻译器 (Translator)
sodium-embeddium-options-mod-compat@sodium-options-mod-compat
secureseed-reborn
creative-eats
-mazerooms
-
+mazerooms@
+friendly-chests
@chitinous_ties
brewable-dragons-breath@|龙息可酿造 (Brewable Dragon's Breath)
-flerovium
+flerovium|鈇*
gathering-torches-become-sunlight|薪火,造炬成阳*
-
-no-arrows-in-you-respawn¨@no-arrows-in-yourespawn|No Arrows In You: Respawn - 审核中
+@morechathistory-neoforge|更多聊天记录-NeoForge (MoreChatHistoryNeoForge)
+no-arrows-in-you-respawn@no-arrows-in-yourespawn
glowcase@
hitbox-api@
@@ -17333,11 +17333,11 @@ hitbox-api@
broken-nametags@
@compostable-flesh
a-cute-little-crock-pot@
-@bountifulharvest
+@bountifulharvest|感恩节乐事 (Bountiful Harvest)
wasteland-grocers
arcane-tablet
@unflavoured-pipes
-curvy-pipes
+curvy-pipes@
classic-bar-legacy|经典状态条 延续版*
@@ -17345,7 +17345,7 @@ glowing-eyeblossom@
openlink@|开放式联机 (OpenLink)
@vs-grapples
@mydriasis
-no-mob-spawning-on-plants
+no-mob-spawning-on-plants@
matmos2
durability-rework@
more-villagers-re-employed@
@@ -17354,7 +17354,821 @@ tcw-bedrock-breaker
apothic-reforge-tokens
advanced-skills-remastered@|进阶技能:重制版 (Advanced Skills: Remastered)
monsters-and-dungeons
-cultivate-the-eternal-realm@IwYRM3LA
+cultivate-the-eternal-realm@
too-much-bosses
valoria@
-glowroot@
\ No newline at end of file
+glowroot@
+@instaminable-deepslate
+
+greedy-bag
+naughthirium@
+chunkpurge|区块清除 (ChunkPurge)
+kubejs-tfmg@
+gauges-and-switches-ported
+mifa@
+tis-vs@
+pistronics-2
+
+
+gladius-combat-evolved
+hungry-chests
+better-withered-mobs@
+pkgbadges@
+@carbasa
+countereds-settlement-roads@
+countereds-terrain-slabs@
+
+explosive-enhancement-reforged@explosive-enhancement-forge
+tmtravlrs-arrow-trails|Tmtravlr的箭矢轨迹 (Tmtravlr's Arrow Trails)
+biome-replacer@|群系替换器*¨biome-replacer-neoforge@|群系替换器 (Biome Replacer)
+custom-suspicious-stew|自定义谜之炖菜*
+
+
+@create-stress-debug
+create-kart
+@barometry
+
+menufpsunlocker
+bogged-spawn@
+unicode-fix@
+insanity
+@sepals
+frustum-culling-advanced
+
+
+star-meow-craft|星喵工艺*
+@defile
+vanishmod@
+@eat-it
+f1-y-f5-mod@perspective-lock
+meddlebootstrap
+mc-modstheleaks
+walk-overhaul
+no-strip@
+mcplus-remastered|MCPlus:重置版 (MCPlus Remastered)
+instant-classic-houses
+hexparse@
+betterraid|更好的劫掠 (BetterRaid)
+villagermetafix
+create-better-villager@create-better-villagers
+mc-modsjs@filesjs
+robotic-parts-first-aid-compat
+craftingpad-fabric@craftingpad
+
+exlines-sushi-mod@sushimod|寿司 (Sushi Mod)
+demanding-saplings@
+coastal-waves@
+@no-swimming
+
+stone-zone@|泛用兼容:石材 (Every Compat*)
+
+a-man-with-plushies@
+enchantmentransfer@|附魔转移 (Enchantment Transfer)
+
+
+
+@async
+
+@touchcontroller
+
+let-me-click-and-send@
+
+careful-cast-corrector-ccc
+4691812@lagbgon-revived
+project-steam
+extended-crafting-terminals-for-applied
+sweety-archeo@sweety-archaeology
+
+chamber-clarity-tac-z@tacz-chamber-clarity
+@techguns
+
+@jdinpack
+selfexpression-slim@
+nothing-dimension@
+@glassitemframes
+portable-stations
+new-world-mod@
+
+
+
+
+book-scroll@
+@journalmod|日志模组 (Journal Mod)
+@autotool-ks|自动工具 (AutoTool)
+@fakevanilla
+
+
+
+
+@qca
+mouse-tweaks-unofficial|鼠标手势非官方版*
+baubles-lts
+elysium-api@
+noisethreader
+@no-bats
+antibat@
+illage-and-spell-age
+origincore|起源核心*
+
+
+heaven-destiny-moment|天命时刻*
+industrial-foregoing-extra-upgrades|工业先锋:更多升级 (Industrial Foregoing: Extra Upgrades)
+growableores-extension@|甘蔗转换机 (GrowableOres Extension)
+oneiricconcept@|梦华构想 (OneiricConcept)
+simelectricity
+somnia-refreshed@
+terramoment|泰拉时刻 (TerraMoment)
+miners-diary-beyond-village
+biggerchathistory
+unifier
+compass-ribbon@
+
+@elytra-hud
+creeper-drops-tnt
+visual-novel-framework@
+@experience-progress
+yapping-tooltips@
+bird-nests@birdnest
+tfc-purified-water
+@exparticle|粒子扩展 (ExParticle)
+@entity-render-disabler
+quick-right-click@
+alchimiae-magicae@|炼金魔法师 (Alchimicae Magicae)
+thaumic-forever
+gregfluxology|GTCEu/GTM能源拓展*
+create-crowns@
+@create-modular-tools
+ae2-wireless-terminal-lts
+@cme-is-bad|并发修改什么的不要鸭 (CME is Bad)
+enchantment-harmony@|和谐相处的附魔书 (EnchantmentHarmony)
+quick-refine|快捷锻刀*
+rubinated-nether@|耀红下界*
+cornucopia-bypizza573@cornucopiabypizza573|丰饶之角 (Cornucopia)
+@epic-fight-bow-tweak
+
+jjkutilities
+crystal-chronicles@
+
+water-particles-render-fix
+ray-trace-library
+delayed-thunder@
+book-and-quick-save@
+keybaordjs@keyboardjs
+better-eating-mod@better-eating
+
+@heart-effects
+@global-wind
+botaniavisualizer
+mana-visualizer
+manavisualizer
+easierworldcreator@ewc
+kubejs-diesel-generators@
+create-slugma@
+cobblemonrider@cobblemonrider1.5
+cobbledex@cobblemon-pokedex
+
+item-descriptions@
+item-production-lib
+
+connectedtexturesmod-backport
+@litematica-printer-easyplace-extension|投影打印机 (Litematica-Printer-Easyplace-Extension)
+elegant-architecture@|雅致画栋*
+extended-workbench-reborn|扩展工作台重置版*
+
+@stans-deathshot
+pathunderfencegates@
+
+latiao-craft-2@|辣条工艺 2*
+canned@
+mushroooms@
+tacz-compatibility
+@baablu-natural-disasters
+@tacz-tweaks
+un-saddle
+@item-landing-sound
+rehooked@
+
+
+
+better-night-vision@betternightvision|更好的夜视*
+
+manors-bounty|庄园馀事 (Manor's Bounty)
+
+
+ftb-money-legacy|FTB交易 延续版 (FTB Money Legacy)
+
+
+vanilla-anvil-repair
+tiered-depths
+tooltip-tweaker
+editableedibles@
+
+@mcdr-command-fabric
+
+pigzilla@
+pale-garden-backport@
+seasons-greetings@
+ex-nihilo-coloratus|无中生有:多彩 (Ex Nihilo: Coloratus)
+granular-mob-griefing
+@dashloader-portinglib-compat
+fermiumbooter2mixinbooter
+immersive-optimization@
+
+tab-tweaks@tabtweaks
+fermiumasm@
+mc-mods-mobs-out-of-the-pool
+clipboardplus@clipboard
+misty-world-unveiled
+balkons-weaponmod-legacy@|更多武器:传承 (Balkon's WeaponMod: Legacy)
+betterendforge-backport
+epic-fight-mod-rapier-moveset-addon
+legendary-warriors
+scape-and-run-holiday@|逃逸:节日 (Scape and Run: Holiday)
+crimson-steves-mutant-mobs@
+
+tactical-imbuements
+puppy-paws@
+ae2-mega-things
+solar-flux-reboot
+solar-flux-additions
+anvil-cell-workbench
+@ultimate-furnace
+create-sifting-fabric@
+create-magics@
+@crypto
+@polyfactory
+buildcraft-legacy@legacy-buildcraft
+
+wither-storm-delight|风暴乐事*
+
+x1000-botania@1000x-botania
+angel-ring-classic|天使指环经典版*
+
+better-animations-collection-revived-2
+scape-and-run-parasites-tweaker
+scape-and-run-parasites-medical-addon
+durability-overhaul@
+quick-hotkeys@quick_hotkeys
+@alwayschristmasday|永远的圣诞节 (AlwaysChristmasDay)
+@strongluck|强运 (StrongLuck)
+
+uncrafted@
+mcjtylib-refilmed@
+nanny@
+resource-library@
+resource-config-api@
+@threatengl
+hybridfix|混合修复 (HybridFix)
+@disable-portal-checks
+eat-eat-eat|吃吃吃*
+@recipe-stages-js|配方阶段 (Recipe Stages JS)
+create-morerecipes
+portable-craft-bench
+portable-tables@ptables
+@flux-network-fork-neoforge
+resource-backpacks@
+etherology@
+snackpirates-aeromancy-additions@aeromancy-additions
+apothic-enchanting
+maidsoul-kitchen@|女仆厨房*
+refinedtools@
+programmed-circuit-card@
+ae2-toogleable-view-cell@ae2-toggleable-viewcell|AE2可切换显示元件 (AE2 Toggleable ViewCell)
+@cobblemon-armors
+letmorefishlove@
+l_ender-s-delight
+ofaw-of-faith-and-war-1237|信仰与战争1237 (Of Faith and War 1237)
+lotmoresteves
+
+legendary-armory-zelda-mod@legendary-armory
+hot-update-1-23-by-gt@hot-update-1.23-by-gt
+management-wanted@
+perception@
+fancy-vfx@
+
+pretty-rain@
+alchemancy
+flavorful
+heavy-fallings@
+erudite-tweaks
+keymap-presets@
+mekanism-no-thanks-nbt@
+@loot-beams-up
+@tekst
+ledger-databases@
+@enderman-death-silence
+infectious-zombie-apocalypse
+xiaoanfc@|小安的农业核心 (Xiaoan's Farming Core)
+@enchant-golden-apple-reborn|附魔金苹果重生 (Enchant Golden Apple Reborn)
+cursor-mod-reload@|自定义光标:重生 (Custom Cursor: Reload)
+
+celestial-overhaul|星月改革*
+simple-faqi@|简单法器 (Simple FaQi)
+redes-coins|Rede的硬币 (Rede's Coins)
+@teleport-commands|传送指令 (Teleport Commands)
+@generic-code-library
+cosmeticarmours@
+ooo-jar@000.jar
+spawner-tweaks
+mob-brawlers
+@transformhandlers
+player-totem@
+@screenshot-uploader
+cute-companions-ducks
+loot-n-explore@
+epic-knights-ice-and-fire
+medieval-origins-revival@
+construction-sticks@
+sophisticated-storage-in-motion@|精妙存储:矿车物流 (Sophisticated Storage in Motion)
+imgur-display@
+extra-sponges@extrasponges|更多海绵*
+more-wolf-armors@|更多狼铠*
+not-enough-rocks@
+pedestal@
+@snowball-and-egg-knockback
+forestry-worktable-display
+accessorify
+high-quality-crops|高品质作物*
+immortal-enchantment@|不死附魔*
+awesome-storage|魔法存储*
+@wallpaper
+
+@modify-player-data
+idcv@|ID冲突查看器 (ID Conflicts Viewer)
+eaglemixins
+kubejs-thermal-augments
+@minopp
+
+
+
+
+camera-anim@|相机大师*
+mekanism-upgrades-reborn|通用机械升级突破:重生 (Mekanism Upgrades: Reborn)
+
+epic-fight-x-irons-spells-animation-fix@
+epic-fight-x-wings-reborn-animation-fix
+ef-bs@battle_arts
+alshanexs-familiars
+feur-skyland@
+balkons-expansion
+
+ascendant-mobs@
+duarm|羽化盔甲 (DuArm)
+bomd-addon
+create-the-air-war@
+bew76@
+block-swapper@
+@simplemap|简易地图 (SimpleMap)
+@oneconfig
+iron-chest-unofficial@|更多箱子非官方版*
+notenoughids-unofficial-1-7-10@notenoughids-unofficial|增加ID上限非官方版 (NotEnoughIDs Unofficial)
+@shutdown
+
+tornado-mod-classic|局部气候&风暴经典版 (Weather and Tornadoes Classic)
+strange-berries@
+jeffs-cursed-walking-structures
+terra-entity|泰拉生物*
+not-interested@
+@new-soviet
+
+
+ragexs-nuclear-tech|Ragex的核科技 (Ragex's Nuclear Tech)
+civcraft
+create-springs-loaded
+mekanism-the-factory-must-grow-compatibility@mekanism-tfmg-compat
+cfm-refurbished-circuit-breaker
+diving-bell-for-tfc
+
+piglin-safety-reburn|猪灵 多喝岩浆:重生 (Piglin Safety: Reburn)
+papers-please|请出示证件 (Papers, Please)
+the-twilight-forest-unofficial@|暮色森林非官方版*
+somnia-awoken-gui-beautify|梦醒时分 - GUI美化 (Somnia Awoken - GUI Beautify)
+redstonecraft@redstonecrafts|红石工艺 (RedStoneCraft)
+mowzies-mobs-baubles|Mowzie 的生物饰品 (Mowzie's Mobs' Baubles)
+
+
+dungeons-and-combat
+@blossomlib
+mod-erate-loading-screen-forge@
+fastquit-forge@
+ingame-info-reborn|游戏信息显示复兴版 (InGame Info Reborn)
+@overlay-mod
+bedisnottoofarawayrefoxed|就要睡觉 (Bed Is Not Too Far Away Refoxed)
+goopreforged@
+
+@crimes-against-the-jvm
+better-split-stack
+sdm-engine-core@sdm-core
+sdm-gamestages@
+
+
+huh-fabric@huh|哼? (Huh?)
+@acme-admin-tools
+custom-nausea@¨custom-nausea-fabric
+accesstransformerjs
+
+checklist
+
+
+
+
+puncher@
+sensible-stackables
+rei-addons-ic2classic
+@blossomtpa
+@lithium-raycast-fix
+@yet-another-minecraft-bingo
+tools-attack|工具也横扫!*
+darkster-epic-guns-tacz-epicfight
+epic-tweaks@
+epic-fight-x-gliders
+
+
+familiar-faces@
+eyes-in-the-dark
+
+jellyfishing-delight@|抓水母乐事*
+
+carrot-rarity@
+more-husbandry
+chest-with-legs
+biodiverse
+railway-wires
+bullseye|靶心*
+
+
+create-mob-spawners@
+salvation-archives-lobotomy
+rlcraft-parasited
+dregora-rl
+underneath
+@leaf-me-alone
+rocket-mons
+farsighted-mobs@
+@h.e.v-suit-voice-system
+astral-sorcery-anti-anti-fake-player-asaafp@astral-sorcery-anti-anti-fake-player
+apotheosis-ascended@
+@better-than-wolves-legacy-unofficial
+warium
+@ait_plus
+fabulous-fletching
+universal-wrench@
+@bottle_ship|瓶中船 (Bottle Ship)
+smallblueslimes-ladders
+doomsday-decoration|末日装饰*
+overclocked-watches@
+portable-wardrobes-easy-armor-swap
+
+
+
+
+
+platform@
+wandering-cocoa@|流浪的可可豆*
+
+no-more-potion-particles@
+larion-world-generation@larion-worldgen
+blueprint-texture-issue-fix
+transparentcapespatch@|透明披风修复 (TransparentCapesPatch)
+
+
+saltyfish-vehicle@
+
+tacz-leawinds-third-person-compat@
+
+
+gtmaid|血汗工厂 (GTMaid)
+
+@damage-tilt-fix
+illager-raid-music@
+@apron
+@coordinated-compass
+
+feur-extension-desert@
+scape-and-run-parasites-quark@
+infested-swarms-and-spiders@
+integrated-cataclysm
+
+
+
+
+@searchworldenhance
+colonies-maid-citizen@|殖民地女仆市民*
+relictium@
+royal-city-gladiolus-sweep|王城剑兰-清理*
+@inventory-blur
+personal-cloud-storage@|个人云存储*
+
+@omni-mobs
+@him
+steve-anomaly@the-anomaly
+integrated-simply-swords
+obsidian-equipment@
+
+
+
+the-deep-void@(mortius)-the-deep-void
+
+cataclysm-incinerators-try-hard|灾变:武器技参数配置/冒火的剑 (Cataclysm: Weapon Skill Config/ Incinerator's Try Hard)
+scp-apollyon-scp-apollyon
+trickster@
+pale-garden
+shulker-enchantments@
+xenotech@
+sewing-kit
+zephyr@zephyr-mod
+create-radars@|机械动力:雷达 (Create: Radars)
+create-more-additions@
+
+
+minecart-turning-reforged@¨@minecart-turning
+offlimits@
+apotheosis-threat
+@string-duper-fix-remover
+
+special-model-loader@
+vs-ship-handler@
+
+
+@dice-rebuild
+lets-do-apple-wood@lets-do-addon-apple-wood
+ascended-quark@
+enchanted-vertical-slabs@
+@better-ladders
+would@
+
+gendustry-community-edition@|基因工业:社区版 (Gendustry: Community Edition)
+ae2fix@|AE2修复 (AE2Fix)
+
+
+betterenddelight@|更好的末地乐事 (BetterEndDelight)
+
+@spanish-delight-refabricated
+crate-delight-croptopia@
+foodspoiling@
+natura-legacy
+
+
+
+
+
+
+riding-partners-reforged@riding-partnersreforged|双人骑行:重铸 (Riding Partners:Reforged)
+
+cat-healing
+plumbob
+@variants-cit
+
+@more-enchantments-soybeani-more-enchantments|豆浆更多附魔 (More Enchantments by Soybeani)
+
+@clearviews
+@immersive_weapons_sounds
+epic-fight-invincible|无坚不摧 (Epic Fight - Invincible)
+ore-dict-re-order
+electrostatics
+
+evil-captcha@
+
+
+
+
+mythic-charms@|神话护符*
+
+
+
+minecraft-add@|原版补充*
+stacked-blocks@
+stacked-blocks-farmers-delight@
+@fowlplay
+
+ender-totem
+@faewufs-diversity
+tonsofenchants
+
+
+cave-root
+
+whos-there-ghost-players@
+the-cuckoo-clock
+final-samurai
+classic-battle-towers|经典战斗高塔*
+sihywtcamd-extensions@difficulty-extensions
+
+@heart-crystals
+@partialhearts
+@the-abyss-runtime
+cosy-critters@
+omnilib@
+astages
+@aloria-loading
+@parentalcontrols
+jason-bot|Jason机器人*
+
+fly-in-the-sky|高空飞行 (Fly in the sky)
+@smartelytra|智能鞘翅 (SmartElytra)
+
+electrostaticsre|每日任务 (ElectrostaticsRE)
+
+brooms-unofficial|扫帚:非官方版*
+fishing-dangers
+distinctpaintings@
+search-on-mcmod|MC百科搜索 (Search on MCMOD)
+infinite-lava-renewed
+@better-fabric-console
+
+fastchest-reforged
+
+
+alexis-64-food
+cosmetic-armor-revitalized-fabric@
+@ambience-v2
+umapyoi-addon-pack@umapyoi-addon|马儿蹦跳:拓展包 (Umapyoi:Addon Pack)
+the-god-of-roadkill
+@visual-snowy-leaves
+verdant@verdant-mod
+grounded@
+gallery@mod
+
+lost-cities-survive-the-dead-edition
+
+
+power-of-the-void|虚空之力 (Power of the Void)
+aerlune-rpg
+
+hitboxbackport
+fsmm@
+create-pantographs-and-wires@
+footwork-api@footwork
+@halplibe
+
+
+crash-assistant@
+
+
+inventorytweak@
+
+legendary-tabs|传说选项卡*
+sky-breaker-blue-skies-limit-remover-1-18-2|天穹突破_1.18.2 (Sky Breaker_1.18.2)
+
+
+
+
+@mxa-tools|MX的工具A (MXA Tools)
+
+xiyus-lucky-block|饩雨的幸运方块 (Xiyu's Lucky Block)
+@epicfight_oldsolar|史诗战斗:旧版solar (EpicFight_oldsolar)
+reskillable-reimagined
+renaissance-core
+@startup-chime|启动音效 (Startup Chime)
+carpet-takeneko-addition@tnca|竹猫的地毯扩展*
+chain-mod
+speedrunmod
+insta-boom
+infinite-night
+nether-lake
+@create-cards
+create-powerlines
+emerald-nugget
+what-dah-dog-doin-tetra-compat
+endbiome
+jasons-fly-mod
+conium@
+yellowtext@
+
+healight@
+@body-health-system
+savemykeybinds@
+nomenublur@
+
+more-loot-tables@
+noteblocks
+@crunchy-crunchy-advancements
+chainvein@
+mobs-on-rails@
+@no-ones-view-model
+gravestone-x-curios-api-compat
+nolijium@
+eventslib
+@etstlib|乙硫醇的匠魂词条库 (EtST Lib)
+orbs-of-crafting
+@leonmtrmod|Leon轨道交通Mod
+
+
+
+
+
+@whatimpressing|正击何键 (What I'm Pressing)
+
+
+stackcraft@
+@extremeview
+@materialexcavator
+@startinthenether
+mekanismmixinhelp
+simple-warp-commands|简单传送指令 (Simple warp commands)
+@mojangfix
+tatercart@
+irons-spell-book-mob-attribute-addon
+epic-fightexsilium-gladius|EpicFight—放逐之刃 (Epic Fight — Exsilium Gladius)
+
+
+
+@blood-blade
+@mineimpact|方神 (MineImpact)
+jadens-nether-expansion-delight@
+
+archery-expansion@
+jjsk
+
+@so-crazy-try|频增趣事 (So Crazy Try)
+ash-of-sin-overlord|罪业余烬:Overlord (Ash Of Sin: Overlord)
+
+@ripples-of-the-past-bad-company-addon|RotP:极恶中队附属 (Ripples of the Past: Bad Company Addon)
+true-prime-piece
+maxims-core|箴言与本源 (Maxims&Core)
+arphex@
+cubic-combat-simulator
+
+webmapview@|网页地图浏览 (webmapview)
+f3-is-my-shawty
+ollamachat¨@ollamachat|ollamachat - 正在审核
+potion-stacker
+@betterweather
+better-gamemode
+better-command
+fishermans-haven@
+bakeries|烘焙坊*
+
+female-plastic-surgery-bust-limit-tweaker
+
+feur-pet-hat-forge@feur-pet-hat
+feur-dimensional-skyland@
+feur-colors@
+feur-statue@
+feur-builder@
+feur-elytra-wings@
+feur-magic-more-enchantments-xp-bottle-forge@feur-magic
+veggies-delight@|蔬菜乐事*
+
+ramadan-delight
+
+
+potion-core-reborn|药水核心:重生*
+
+rolling-gate@|卷帘门*
+bettertab@better-tab|更好的Tab (BetterTab)
+
+put-mana-in|手充魔力*
+ars-botania
+linweiyun-mod|Linweiyun的附魔
+baublesex
+@custom-block-highlight|自定义方块高亮 (Custom Block Highlight)
+tinkers-simple
+threedmusket
+rtmmetro|真实火车:地铁扩展 (RTMMetro)
+
+
+
+lobotomy
+parrot-power
+fortune-ores-2
+
+
+feur-dungeon-spawner@
+feur-extension-fossil@
+feur-extension-jungle@
+cybernetic-system
+
+
+liquid-dirt
+liquid-blocks@
+apothic-combat@
+toms-trading-network@
+somanyapples
+static-block
+cherryvillage
+ductwork@|管道*
+cooldown-coordinator@
+librarianlib-continuous@
+luna@
+loot-integrations
+realtimescale
+
+crafting-recipe-exporter
+
+extra-apoth-compat
+pathtracker@
+elytra-protection
+skip-sleep
+stony-cliffs-are-cool
+simple-ender-things
+mimecraft-mod
+warden-sword
+arch-bows@
+fancy-trinkets@|精美的小饰品*
+block-n-zapping@
+cgs
+dynamic-trees-jadens
+dynamic-trees-croptopia-new
+stacksonstacks
+more-of-all@
\ No newline at end of file
diff --git a/Plain Craft Launcher 2/app.manifest b/Plain Craft Launcher 2/app.manifest
index 498fc70..b4ae5d8 100644
--- a/Plain Craft Launcher 2/app.manifest
+++ b/Plain Craft Launcher 2/app.manifest
@@ -51,11 +51,12 @@
-
- PerMonitorV2
- true
+ true
+
+ PerMonitorV2
+ true
diff --git a/最新正式版.zip b/最新正式版.zip
index 838e85b..fbf4300 100644
Binary files a/最新正式版.zip and b/最新正式版.zip differ