This commit is contained in:
龙腾猫跃
2024-02-22 00:04:00 +08:00
parent 63d48b09e3
commit 83ebc61d8a
37 changed files with 3234 additions and 3401 deletions
+1 -1
View File
@@ -277,7 +277,7 @@
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/> <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/> <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="UndoLimit" Value="15" /> <Setter Property="UndoLimit" Value="15" />
<Setter Property="MaxLength" Value="10000" /> <Setter Property="MaxLength" Value="1000" />
<Setter Property="BorderBrush" Value="{DynamicResource ColorBrushBg0}" /> <Setter Property="BorderBrush" Value="{DynamicResource ColorBrushBg0}" />
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
+6 -5
View File
@@ -47,9 +47,12 @@ Public Class Application
Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K) Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
End If End If
#If DEBUG Then #If DEBUG Then
ElseIf e.Args(0) = "--make" Then
'制作更新包 '制作更新包
UpdateMake(e.Args(1)) ElseIf e.Args(0) = "--edit1" Then
ExeEdit(e.Args(1), True)
Environment.Exit(Result.Cancel)
ElseIf e.Args(0) = "--edit2" Then
ExeEdit(e.Args(1), False)
Environment.Exit(Result.Cancel) Environment.Exit(Result.Cancel)
#End If #End If
End If End If
@@ -112,13 +115,11 @@ Public Class Application
Setup.Load("SystemDebugMode") Setup.Load("SystemDebugMode")
Setup.Load("SystemDebugAnim") Setup.Load("SystemDebugAnim")
Setup.Load("ToolDownloadThread") Setup.Load("ToolDownloadThread")
Setup.Load("ToolDownloadCert")
'网络配置初始化 '网络配置初始化
ServicePointManager.Expect100Continue = True ServicePointManager.Expect100Continue = True
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
ServicePointManager.DefaultConnectionLimit = 1024 ServicePointManager.DefaultConnectionLimit = 1024
'ServicePointManager.ServerCertificateValidationCallback = New Security.RemoteCertificateValidationCallback(Function() As Boolean
' Return True
' End Function)
'计时 '计时
Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms") Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
ApplicationStartTick = GetTimeTick() ApplicationStartTick = GetTimeTick()
+1 -1
View File
@@ -128,7 +128,7 @@ Public Class MyResizer
Private Declare Function GetMonitorInfo Lib "user32" (hMonitor As IntPtr, lpmi As MONITORINFO) As Boolean Private Declare Function GetMonitorInfo Lib "user32" (hMonitor As IntPtr, lpmi As MONITORINFO) As Boolean
Friend Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr Private Declare Function MonitorFromWindow Lib "User32" (handle As IntPtr, flags As Integer) As IntPtr
Private Sub connectMouseHandlers(element As UIElement) Private Sub connectMouseHandlers(element As UIElement)
AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown
+1 -1
View File
@@ -134,7 +134,7 @@
<Grid x:Name="PanTitleInner" Visibility="Collapsed" Margin="-16,0,0,0" Opacity="0" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=PanTitle, Mode=OneWay}"> <Grid x:Name="PanTitleInner" Visibility="Collapsed" Margin="-16,0,0,0" Opacity="0" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=PanTitle, Mode=OneWay}">
<local:MyIconButton HorizontalAlignment="Left" Theme="White" x:Name="BtnTitleInner" Margin="12,0,0,0" Height="28" Width="28" <local:MyIconButton HorizontalAlignment="Left" Theme="White" x:Name="BtnTitleInner" Margin="12,0,0,0" Height="28" Width="28"
LogoScale="0.87" Logo="M1097.162313 584.707482 250.272424 584.707482 562.833701 896.95123C591.749274 925.859979 591.749274 972.659975 562.833701 1001.568724 533.91812 1030.477473 487.058315 1030.477473 458.142742 1001.568724L21.580946 565.451235C6.757449 550.631234-0.361728 531.131238 0.028357 511.679985L0.028357 511.631236 0.028357 511.582486C-0.361728 492.13124 6.757449 472.582488 21.580946 457.762487L458.142742 21.644997C487.058315-7.215002 533.91812-7.215002 562.833701 21.644997 591.749274 50.553746 591.749274 97.4025 562.833701 126.262499L250.272424 438.55499 1097.162313 438.55499C1137.585605 438.55499 1170.304578 471.266238 1170.304578 511.631236 1170.304578 551.947483 1137.585605 584.707482 1097.162313 584.707482L1097.162313 584.707482Z" /> LogoScale="0.87" Logo="M1097.162313 584.707482 250.272424 584.707482 562.833701 896.95123C591.749274 925.859979 591.749274 972.659975 562.833701 1001.568724 533.91812 1030.477473 487.058315 1030.477473 458.142742 1001.568724L21.580946 565.451235C6.757449 550.631234-0.361728 531.131238 0.028357 511.679985L0.028357 511.631236 0.028357 511.582486C-0.361728 492.13124 6.757449 472.582488 21.580946 457.762487L458.142742 21.644997C487.058315-7.215002 533.91812-7.215002 562.833701 21.644997 591.749274 50.553746 591.749274 97.4025 562.833701 126.262499L250.272424 438.55499 1097.162313 438.55499C1137.585605 438.55499 1170.304578 471.266238 1170.304578 511.631236 1170.304578 551.947483 1137.585605 584.707482 1097.162313 584.707482L1097.162313 584.707482Z" />
<TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,0,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" /> <TextBlock SnapsToDevicePixels="False" IsHitTestVisible="False" Name="LabTitleInner" VerticalAlignment="Center" Margin="47,1,60,0" HorizontalAlignment="Left" Foreground="{DynamicResource ColorBrush8}" FontSize="15" />
</Grid> </Grid>
</Grid> </Grid>
<Grid x:Name="PanLeft" Grid.Row="1" HorizontalAlignment="Left" SnapsToDevicePixels="True"> <Grid x:Name="PanLeft" Grid.Row="1" HorizontalAlignment="Left" SnapsToDevicePixels="True">
+12 -12
View File
@@ -129,6 +129,12 @@ Public Class FormMain
'3:BUG+ IMP* FEAT- '3:BUG+ IMP* FEAT-
'2:BUG* IMP- '2:BUG* IMP-
'1:BUG- '1:BUG-
If LastVersion < 314 Then 'Snapshot 2.6.14
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "优化 MC 下载,尝试解决各种导致 MC 下载失败的问题"))
FeatureList.Add(New KeyValuePair(Of Integer, String)(3, "由于 MCBBS 关站,移除其相关内容"))
FeatureCount += 17
BugCount += 16
End If
If LastVersion < 312 Then 'Snapshot 2.6.13 If LastVersion < 312 Then 'Snapshot 2.6.13
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug")) FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
FeatureCount += 6 FeatureCount += 6
@@ -906,7 +912,7 @@ Public Class FormMain
Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!") Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
ElseIf Not (PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod) Then ElseIf Not (PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod) Then
'未处于 Mod 管理页面 '未处于 Mod 管理页面
If MyMsgBox($"是否要将这些文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install If MyMsgBox($"是否要将这{If(FilePathList.Count = 1, "个", "些")}文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install
Else Else
'处于 Mod 管理页面 '处于 Mod 管理页面
Install: Install:
@@ -994,7 +1000,6 @@ Install:
Left -= 10000 Left -= 10000
ShowInTaskbar = False ShowInTaskbar = False
Visibility = Visibility.Hidden Visibility = Visibility.Hidden
'SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) Or WS_EX_TOOLWINDOW)
Log("[System] 窗口已隐藏,位置:(" & Left & "," & Top & ")") Log("[System] 窗口已隐藏,位置:(" & Left & "," & Top & ")")
Else Else
'取消隐藏 '取消隐藏
@@ -1017,16 +1022,9 @@ Install:
Topmost = False Topmost = False
SetForegroundWindow(Handle) SetForegroundWindow(Handle)
Focus() Focus()
'ShowWindow(Handle, 1) 'SW_RESTORE
'SetWindowPos(Handle, 0, 0, 0, 0, 0, 3) 'InsertAfter: TOP, NOMOVE & NOSIZE
'SetFocus(Handle)
Log("[System] 窗口已置顶,位置:(" & Left & ", " & Top & "), " & Width & " x " & Height) Log("[System] 窗口已置顶,位置:(" & Left & ", " & Top & "), " & Width & " x " & Height)
End Sub) End Sub)
End Sub End Sub
'Private Declare Function SetFocus Lib "user32" (hWnd As IntPtr) As IntPtr
'Private Declare Function SetWindowPos Lib "user32" (hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, flags As UInteger) As Boolean
'Private Declare Function AttachThreadInput Lib "user32" (attachFrom As IntPtr, attachTo As IntPtr, isAttach As Boolean) As Boolean
'Private Declare Function SendMessage Lib "user32" (hWnd As IntPtr, msg As UInteger, wParam As Long, lParam As Long) As IntPtr
#End Region #End Region
@@ -1573,7 +1571,11 @@ Install:
''' </summary> ''' </summary>
Public Sub BackToTop() Handles BtnExtraBack.Click Public Sub BackToTop() Handles BtnExtraBack.Click
Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild() Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
If RealScroll IsNot Nothing Then
RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset) RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset)
Else
Log("[UI] 无法返回顶部,未找到合适的 RealScroll", LogLevel.Hint)
End If
End Sub End Sub
Private Function BtnExtraBack_ShowCheck() As Boolean Private Function BtnExtraBack_ShowCheck() As Boolean
Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild() Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
@@ -1581,9 +1583,7 @@ Install:
End Function End Function
Private Function BtnExtraBack_GetRealChild() As MyScrollViewer Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
Dim Scroll = CType(PanMainRight.Child, MyPageRight).PanScroll Return CType(PanMainRight.Child, MyPageRight).PanScroll
If Scroll Is Nothing OrElse Scroll.Visibility <> Visibility.Visible Then Return Nothing
Return Scroll
End Function End Function
#End Region #End Region
Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 B

+12 -6
View File
@@ -11,12 +11,12 @@ Public Module ModBase
#Region "声明" #Region "声明"
'下列版本信息由更新器自动修改 '下列版本信息由更新器自动修改
Public Const VersionBaseName As String = "2.6.13" '不含分支前缀的显示用版本名 Public Const VersionBaseName As String = "2.6.14" '不含分支前缀的显示用版本名
Public Const VersionStandardCode As String = "2.6.13." & VersionBranchCode '标准格式的四段式版本号 Public Const VersionStandardCode As String = "2.6.14." & VersionBranchCode '标准格式的四段式版本号
#If BETA Then #If BETA Then
Public Const VersionCode As Integer = 313 'Release Public Const VersionCode As Integer = 313 'Release
#Else #Else
Public Const VersionCode As Integer = 312 'Snapshot Public Const VersionCode As Integer = 314 'Snapshot
#End If #End If
'自动生成的版本信息 '自动生成的版本信息
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -158,9 +158,13 @@ Public Module ModBase
''' </summary> ''' </summary>
Public Const IconButtonUser As String = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z" Public Const IconButtonUser As String = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"
''' <summary> ''' <summary>
''' 图标按钮,微软,0.9x ''' 图标按钮,盾牌,1x
''' </summary> ''' </summary>
Public Const IconButtonMs As String = "M0,0h5v5h-5ZM6,0h5v5h-5v-5ZM0,6h5v5h-5v-5ZM6,6h5v5h-5v-5Z" Public Const IconButtonShield As String = "M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z"
''' <summary>
''' 图标按钮,离线,0.85x
''' </summary>
Public Const IconButtonOffline As String = "M533.293176 788.841412a60.235294 60.235294 0 1 1 85.202824 85.202823l-42.616471 42.586353c-129.355294 129.385412-339.124706 129.385412-468.510117 0-129.385412-129.385412-129.385412-339.124706 0-468.510117l42.586353-42.616471a60.235294 60.235294 0 1 1 85.202823 85.202824l-42.61647 42.586352a210.823529 210.823529 0 1 0 298.164706 298.164706l42.586352-42.61647z m255.548236-255.548236l42.61647-42.586352a210.823529 210.823529 0 1 0-298.164706-298.164706l-42.586352 42.61647a60.235294 60.235294 0 1 1-85.202824-85.202823l42.616471-42.586353c129.355294-129.385412 339.124706-129.385412 468.510117 0 129.385412 129.385412 129.385412 339.124706 0 468.510117l-42.586353 42.616471a60.235294 60.235294 0 1 1-85.202823-85.202824zM192.542118 192.542118a60.235294 60.235294 0 0 1 85.202823 0l553.712941 553.712941a60.235294 60.235294 0 0 1-85.202823 85.202823L192.542118 277.744941a60.235294 60.235294 0 0 1 0-85.202823z"
''' <summary> ''' <summary>
''' 图标,音符,1x ''' 图标,音符,1x
''' </summary> ''' </summary>
@@ -1980,9 +1984,10 @@ NextElement:
Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments) Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
Using Program As New Process() With {.StartInfo = Info} Using Program As New Process() With {.StartInfo = Info}
Program.Start() Program.Start()
Dim Result As String = Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
Program.WaitForExit(Timeout) Program.WaitForExit(Timeout)
If Not Program.HasExited Then Program.Kill() If Not Program.HasExited Then Program.Kill()
Return Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd Return Result
End Using End Using
End Function End Function
@@ -2111,6 +2116,7 @@ NextElement:
''' </summary> ''' </summary>
Public Sub OpenWebsite(Url As String) Public Sub OpenWebsite(Url As String)
Try Try
If Not Url.ToLower.StartsWith("http") Then Throw New Exception(Url & " 不是一个有效的网址,它必须以 http 开头!")
Log("[System] 正在打开网页:" & Url) Log("[System] 正在打开网页:" & Url)
Process.Start(Url) Process.Start(Url)
Catch ex As Exception Catch ex As Exception
+13 -13
View File
@@ -192,7 +192,6 @@ RequestFinished:
If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Request.ProtocolVersion = HttpVersion.Version11 If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Request.ProtocolVersion = HttpVersion.Version11
Request.Timeout = Timeout Request.Timeout = Timeout
Request.Accept = Accept Request.Accept = Accept
Request.KeepAlive = False
SecretHeadersSign(Url, Request) SecretHeadersSign(Url, Request)
Using res As HttpWebResponse = Request.GetResponse() Using res As HttpWebResponse = Request.GetResponse()
Using HttpStream As Stream = res.GetResponseStream() Using HttpStream As Stream = res.GetResponseStream()
@@ -393,7 +392,6 @@ RequestFinished:
End If End If
Req.ContentType = ContentType Req.ContentType = ContentType
Req.Timeout = Timeout Req.Timeout = Timeout
Req.KeepAlive = False
SecretHeadersSign(Url, Req) SecretHeadersSign(Url, Req)
If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Req.ProtocolVersion = HttpVersion.Version11 If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Req.ProtocolVersion = HttpVersion.Version11
If Method = "POST" OrElse Method = "PUT" Then If Method = "POST" OrElse Method = "PUT" Then
@@ -965,13 +963,12 @@ Capture:
'是否禁用多线程,以及规定碎片大小 '是否禁用多线程,以及规定碎片大小
Dim TargetUrl As String = GetSource().Url Dim TargetUrl As String = GetSource().Url
If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("github.com") Then Return False If TargetUrl.Contains("pcl2-server") OrElse TargetUrl.Contains("gitcode.net") OrElse TargetUrl.Contains("github.com") Then Return False
Dim RealFilePieceLimit = If(TargetUrl.Contains("download.mcbbs.net"), FilePieceLimit * 5, FilePieceLimit)
'寻找最大碎片 '寻找最大碎片
Dim FilePieceMax As NetThread = Threads Dim FilePieceMax As NetThread = Threads
For Each Thread As NetThread In Threads For Each Thread As NetThread In Threads
If Thread.DownloadUndone > FilePieceMax.DownloadUndone Then FilePieceMax = Thread If Thread.DownloadUndone > FilePieceMax.DownloadUndone Then FilePieceMax = Thread
Next Next
If FilePieceMax Is Nothing OrElse FilePieceMax.DownloadUndone < RealFilePieceLimit Then Return False If FilePieceMax Is Nothing OrElse FilePieceMax.DownloadUndone < FilePieceLimit Then Return False
StartPosition = FilePieceMax.DownloadEnd - FilePieceMax.DownloadUndone * 0.4 StartPosition = FilePieceMax.DownloadEnd - FilePieceMax.DownloadUndone * 0.4
StartSource = GetSource() StartSource = GetSource()
@@ -1021,7 +1018,7 @@ StartThread:
Dim HttpRequest As HttpWebRequest Dim HttpRequest As HttpWebRequest
Dim ResultStream As Stream = Nothing Dim ResultStream As Stream = Nothing
'部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟 '部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
'延迟过长会导致某些特别慢的链接迟迟不被掐死,即使 MCBBS 源在 10s 下也会出现类似情况 '延迟过长会导致某些特别慢的链接迟迟不被掐死
Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000) Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 6000) * (1 + Info.Source.FailCount), 30000)
Info.State = NetState.Connect Info.State = NetState.Connect
Try Try
@@ -1206,7 +1203,9 @@ SourceBreak:
'End SyncLock 'End SyncLock
Info.Source.Ex = ex Info.Source.Ex = ex
'根据情况判断,是否在多线程下禁用下载源(连续错误过多,或不支持断点续传) '根据情况判断,是否在多线程下禁用下载源(连续错误过多,或不支持断点续传)
If ex.Message.Contains("该下载源不支持") OrElse ex.Message.Contains("未能解析") OrElse ex.Message.Contains("(404)") OrElse ex.Message.Contains("(403)") OrElse ex.Message.Contains("(502)") OrElse ex.Message.Contains("无返回数据") OrElse ex.Message.Contains("空间不足") OrElse 'BMCLAPI 的部分源在高频率请求下会返回 403,所以不应因此禁用下载源
If ex.Message.Contains("该下载源不支持") OrElse ex.Message.Contains("未能解析") OrElse ex.Message.Contains("(404)") OrElse
ex.Message.Contains("(502)") OrElse ex.Message.Contains("无返回数据") OrElse ex.Message.Contains("空间不足") OrElse ex.Message.Contains("获取到的片大小不一致") OrElse
(Info.Source.FailCount >= MathClamp(NetTaskThreadLimit, 5, 40) AndAlso DownloadDone < 1) OrElse (Info.Source.FailCount >= MathClamp(NetTaskThreadLimit, 5, 40) AndAlso DownloadDone < 1) OrElse
Info.Source.FailCount > NetTaskThreadLimit Then Info.Source.FailCount > NetTaskThreadLimit Then
Dim IsThisFail As Boolean = False Dim IsThisFail As Boolean = False
@@ -1225,7 +1224,7 @@ Wrong:
'本线程引发下载源被禁用 '本线程引发下载源被禁用
If IsThisFail Then If IsThisFail Then
Log("[Download] " & LocalName & " " & Uuid & "#:下载源被禁用(" & Info.Source.Id & "):" & Info.Source.Url) Log("[Download] " & LocalName & " " & Uuid & "#:下载源被禁用(" & Info.Source.Id & "):" & Info.Source.Url)
Log(ex, "下载源 " & Info.Source.Id & " 已被禁用", If(ex.Message.Contains("不支持断点续传") OrElse ex.Message.Contains("404") OrElse ex.Message.Contains("416"), LogLevel.Developer, LogLevel.Debug)) Log(ex, "下载源 " & Info.Source.Id & " 已被禁用", If(ex.Message.Contains("不支持断点续传") OrElse ex.Message.Contains("(404)") OrElse ex.Message.Contains("(416)"), LogLevel.Developer, LogLevel.Debug))
If IsSourceFailed() Then If IsSourceFailed() Then
'没有可用源 '没有可用源
Log("[Download] 文件 " & LocalName & " 已无可用下载源") Log("[Download] 文件 " & LocalName & " 已无可用下载源")
@@ -1475,7 +1474,7 @@ Retry:
End Get End Get
Set(value As Integer) Set(value As Integer)
_FailCount = value _FailCount = value
If State = LoadState.Loading AndAlso value >= Math.Min(2000, Math.Max(FileRemain * 5.5, NetTaskThreadLimit * 5.5 + 3)) Then If State = LoadState.Loading AndAlso value >= Math.Min(10000, Math.Max(FileRemain * 5.5, NetTaskThreadLimit * 5.5 + 3)) Then
Log("[Download] 由于同加载器中失败次数过多引发强制失败:连续失败了 " & value & " 次", LogLevel.Debug) Log("[Download] 由于同加载器中失败次数过多引发强制失败:连续失败了 " & value & " 次", LogLevel.Debug)
On Error Resume Next On Error Resume Next
Dim ExList As New List(Of Exception) Dim ExList As New List(Of Exception)
@@ -1846,7 +1845,8 @@ Retry:
Private Sub StartManager() Private Sub StartManager()
If IsManagerStarted Then Exit Sub If IsManagerStarted Then Exit Sub
IsManagerStarted = True IsManagerStarted = True
RunInNewThread(Sub() RunInNewThread(
Sub()
Try Try
Dim LastLoopTime As Long Dim LastLoopTime As Long
While True While True
@@ -1898,12 +1898,12 @@ Retry:
NewThreadCount -= 1 NewThreadCount -= 1
End If End If
Next Next
'等待 40 ms:BMCLAPI 部分时候有 20QPS 的限制
Thread.Sleep(40)
Loop While NewThreadCount > 0 AndAlso IsSuccess Loop While NewThreadCount > 0 AndAlso IsSuccess
End If End If
'等待直至 120 ms '等待 40 ms
Do While GetTimeTick() - LastLoopTime < 120 Thread.Sleep(40)
Thread.Sleep(10)
Loop
End While End While
Catch ex As Exception Catch ex As Exception
Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert) Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert)
@@ -59,7 +59,6 @@
Entry.WikiId = i Entry.WikiId = i
If SplitedLine.Count >= 2 Then If SplitedLine.Count >= 2 Then
Entry.ChineseName = SplitedLine(1) Entry.ChineseName = SplitedLine(1)
Entry.MCBBS = If(SplitedLine.Count >= 3, SplitedLine(2), Nothing)
If Entry.ChineseName.Contains("*") Then '处理 * If Entry.ChineseName.Contains("*") Then '处理 *
Entry.ChineseName = Entry.ChineseName.Replace("*", " (" & Entry.ChineseName = Entry.ChineseName.Replace("*", " (" &
String.Join(" ", If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Split("-").Select(Function(w) w.Substring(0, 1).ToUpper & w.Substring(1, w.Length - 1))) & ")") String.Join(" ", If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Split("-").Select(Function(w) w.Substring(0, 1).ToUpper & w.Substring(1, w.Length - 1))) & ")")
@@ -82,10 +81,6 @@
''' </summary> ''' </summary>
Public ChineseName As String = "" Public ChineseName As String = ""
''' <summary> ''' <summary>
''' MCBBS 发布帖编号(例如 195107)。若为 Nothing 则没有发布帖。
''' </summary>
Public MCBBS As Integer?
''' <summary>
''' CurseForge Slug(例如 advanced-solar-panels)。 ''' CurseForge Slug(例如 advanced-solar-panels)。
''' </summary> ''' </summary>
Public CurseForgeSlug As String = Nothing Public CurseForgeSlug As String = Nothing
@@ -95,7 +90,7 @@
Public ModrinthSlug As String = Nothing Public ModrinthSlug As String = Nothing
Public Overrides Function ToString() As String Public Overrides Function ToString() As String
Return If(CurseForgeSlug, "") & "&" & If(ModrinthSlug, "") & "|" & WikiId & "|" & ChineseName & If(MCBBS Is Nothing, "", "|" & MCBBS) Return If(CurseForgeSlug, "") & "&" & If(ModrinthSlug, "") & "|" & WikiId & "|" & ChineseName
End Function End Function
End Class End Class
@@ -200,14 +195,6 @@
End Get End Get
End Property End Property
''' <summary> ''' <summary>
''' MCBBS 的帖子 ID。若为 0 则没有。
''' </summary>
Public ReadOnly Property MCBBS As Integer
Get
Return If(DatabaseEntry Is Nothing, 0, If(DatabaseEntry.MCBBS, 0))
End Get
End Property
''' <summary>
''' 翻译后的中文名。若数据库没有则等同于 RawName。 ''' 翻译后的中文名。若数据库没有则等同于 RawName。
''' </summary> ''' </summary>
Public ReadOnly Property TranslatedName As String Public ReadOnly Property TranslatedName As String
@@ -884,10 +871,10 @@ NoSubtitle:
Dim SearchResult As String = "" Dim SearchResult As String = ""
For i = 0 To Math.Min(4, SearchResults.Count - 1) '就算全是准确的,也最多只要 5 个 For i = 0 To Math.Min(4, SearchResults.Count - 1) '就算全是准确的,也最多只要 5 个
If Not SearchResults(i).AbsoluteRight AndAlso i >= Math.Min(2, SearchResults.Count - 1) Then Exit For '把 3 个结果拼合以提高准确度 If Not SearchResults(i).AbsoluteRight AndAlso i >= Math.Min(2, SearchResults.Count - 1) Then Exit For '把 3 个结果拼合以提高准确度
If SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ") & " " If SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ").Replace("/", " ") & " "
If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.Replace("-", " ") & " " If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.Replace("-", " ").Replace("/", " ") & " "
SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First. SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First.
Replace(":", "").Replace("(", "").Replace(")", "").ToLower & " " Replace(":", "").Replace("(", "").Replace(")", "").ToLower.Replace("/", " ") & " "
Next Next
Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer) Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer)
'去除常见连接词 '去除常见连接词
@@ -911,9 +898,8 @@ NoSubtitle:
For Each Keyword In AllPossibleKeywords.Split(" ") For Each Keyword In AllPossibleKeywords.Split(" ")
Keyword = Keyword.Trim("["c, "]"c) Keyword = Keyword.Trim("["c, "]"c)
If Keyword = "" Then Continue For If Keyword = "" Then Continue For
If {"forge", "fabric", "for", "mod", If {"forge", "fabric", "for", "mod", "quilt"}.Contains(Keyword) Then '#208
"forge/fabric", "fabric/forge", "fabric/quilt", "quilt/fabric"}.Contains(Keyword) Then '#208 Log("[Comp] 已跳过搜索关键词:" & Keyword, LogLevel.Developer)
Log("[Comp] 已跳过搜索关键词:" & Keyword)
Continue For Continue For
End If End If
RightKeywords.Add(Keyword) RightKeywords.Add(Keyword)
@@ -927,7 +913,7 @@ NoSubtitle:
'例外项:OptiForge、OptiFabric(拆词后因为包含 Forge/Fabric 导致无法搜到实际的 Mod) '例外项:OptiForge、OptiFabric(拆词后因为包含 Forge/Fabric 导致无法搜到实际的 Mod)
If RawFilter.Replace(" ", "").ToLower.Contains("optiforge") Then Task.Input.SearchText = "optiforge" If RawFilter.Replace(" ", "").ToLower.Contains("optiforge") Then Task.Input.SearchText = "optiforge"
If RawFilter.Replace(" ", "").ToLower.Contains("optifabric") Then Task.Input.SearchText = "optifabric" If RawFilter.Replace(" ", "").ToLower.Contains("optifabric") Then Task.Input.SearchText = "optifabric"
Log("[Comp] 工程列表搜索最终文本:" & Task.Input.SearchText, LogLevel.Developer) Log("[Comp] 工程列表搜索最终文本:" & Task.Input.SearchText, LogLevel.Debug)
Task.Progress = 0.1 Task.Progress = 0.1
#End Region #End Region
@@ -583,7 +583,8 @@ Done:
If LogMc IsNot Nothing Then If LogMc IsNot Nothing Then
'常规信息 '常规信息
If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf)) If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
If LogMc.Contains("A potential solution has been determined:") OrElse LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf)) If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
If LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)((\t)+ - [^\n]+\n)+"), ""), "(?<=(\t)+)[^\n]+"), vbLf))
End If End If
End Sub End Sub
@@ -221,9 +221,7 @@
''' </summary> ''' </summary>
Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain) Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult)) Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
BackupUrl:="https://download.mcbbs.net/mc/game/version_manifest.json",
IsJson:=True)
Try Try
Dim Versions As JArray = Json("versions") Dim Versions As JArray = Json("versions")
If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")") If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
@@ -384,8 +382,7 @@
''' </summary> ''' </summary>
Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain) Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult)) Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
BackupUrl:="https://download.mcbbs.net/optifine/versionList", IsJson:=True)
Try Try
Dim Versions As New List(Of DlOptiFineListEntry) Dim Versions As New List(Of DlOptiFineListEntry)
For Each Token As JObject In Json For Each Token As JObject In Json
@@ -469,8 +466,7 @@
''' </summary> ''' </summary>
Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain) Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult)) Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default, Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
BackupUrl:="https://download.mcbbs.net/forge/minecraft")
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")") If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
'获取所有版本信息 '获取所有版本信息
Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?") Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
@@ -664,8 +660,7 @@
''' Forge 版本列表,BMCLAPI。 ''' Forge 版本列表,BMCLAPI。
''' </summary> ''' </summary>
Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry))) Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, IsJson:=True)
BackupUrl:="https://download.mcbbs.net/forge/minecraft/" & Loader.Input, IsJson:=True)
Dim Versions As New List(Of DlForgeVersionEntry) Dim Versions As New List(Of DlForgeVersionEntry)
Try Try
Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input) Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
@@ -827,8 +822,7 @@
''' </summary> ''' </summary>
Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain) Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult)) Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
BackupUrl:="https://download.mcbbs.net/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
Try Try
Dim Json As JObject = Result("versions") Dim Json As JObject = Result("versions")
Dim Versions As New List(Of DlLiteLoaderListEntry) Dim Versions As New List(Of DlLiteLoaderListEntry)
@@ -914,8 +908,7 @@
''' </summary> ''' </summary>
Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain) Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult)) Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
BackupUrl:="https://download.mcbbs.net/fabric-meta/v2/versions", IsJson:=True)
Try Try
Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result} Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项") If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
@@ -943,26 +936,25 @@
Public Function DlSourceResourceGet(MojangBase As String) As String() Public Function DlSourceResourceGet(MojangBase As String) As String()
MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net") MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/assets").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/assets").Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"), Return {
MojangBase, MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets"),
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets") MojangBase
} }
End Function End Function
Public Function DlSourceLibraryGet(MojangBase As String) As String() Public Function DlSourceLibraryGet(MojangBase As String) As String()
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/maven").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/maven").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"), Return {
MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/libraries").Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
MojangBase,
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"), MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries") MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries"),
MojangBase
} }
End Function End Function
Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String() Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址") If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net").Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"), Return {
MojangBase, MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com"),
MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com") MojangBase
} }
End Function End Function
@@ -727,8 +727,7 @@ Wait:
Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile))) Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
Log("[Java] 开始获取 Java 下载信息") Log("[Java] 开始获取 Java 下载信息")
Dim IndexFileStr As String = NetGetCodeByDownload( Dim IndexFileStr As String = NetGetCodeByDownload(
{"https://download.mcbbs.net/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json", {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json",
"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}, "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
IsJson:=True) IsJson:=True)
'获取下载地址 '获取下载地址
@@ -740,8 +739,7 @@ Wait:
Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}") Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
'获取文件列表 '获取文件列表
Dim ListFileStr As String = NetGetCodeByDownload( Dim ListFileStr As String = NetGetCodeByDownload(
{Address.Replace("piston-meta.mojang.com", "download.mcbbs.net"), {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"),
Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com"),
Address}, Address},
IsJson:=True) IsJson:=True)
Dim BaseDir As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\" Dim BaseDir As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\runtime\" & TargetEntry.Key & "\"
@@ -753,8 +751,7 @@ Wait:
If Checker.Check(BaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件 If Checker.Check(BaseDir & File.Name) Is Nothing Then Continue For '跳过已存在的文件
Dim Url As String = Info("url") Dim Url As String = Info("url")
Results.Add(New NetFile( Results.Add(New NetFile(
{Url.Replace("piston-data.mojang.com", "download.mcbbs.net"), {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"),
Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com"),
Url}, Url},
BaseDir & File.Name, Checker)) BaseDir & File.Name, Checker))
Next Next
@@ -1911,9 +1911,9 @@ IgnoreCustomSkin:
'获取自定义命令 '获取自定义命令
Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun") Dim CustomCommandGlobal As String = Setup.Get("LaunchAdvanceRun")
If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal) If CustomCommandGlobal <> "" Then CustomCommandGlobal = ArgumentReplace(CustomCommandGlobal, True)
Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent) Dim CustomCommandVersion As String = Setup.Get("VersionAdvanceRun", Version:=McVersionCurrent)
If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion) If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion, True)
'输出 bat '输出 bat
Try Try
@@ -2072,7 +2072,7 @@ IgnoreCustomSkin:
'获取窗口标题 '获取窗口标题
Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent) Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent)
If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle") If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle")
WindowTitle = ArgumentReplace(WindowTitle) WindowTitle = ArgumentReplace(WindowTitle, False)
'初始化等待 '初始化等待
Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle) Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle)
@@ -2124,7 +2124,7 @@ IgnoreCustomSkin:
''' <summary> ''' <summary>
''' 在启动结束时,对 PCL 约定的替换标记进行处理。 ''' 在启动结束时,对 PCL 约定的替换标记进行处理。
''' </summary> ''' </summary>
Private Function ArgumentReplace(Raw As String) As String Private Function ArgumentReplace(Raw As String, ReplaceTimeAndDate As Boolean) As String
If Raw Is Nothing Then Return Nothing If Raw Is Nothing Then Return Nothing
'路径替换 '路径替换
Raw = Raw.Replace("{minecraft}", PathMcFolder) Raw = Raw.Replace("{minecraft}", PathMcFolder)
@@ -2134,6 +2134,10 @@ IgnoreCustomSkin:
'普通替换 '普通替换
Raw = Raw.Replace("{user}", McLoginLoader.Output.Name) Raw = Raw.Replace("{user}", McLoginLoader.Output.Name)
Raw = Raw.Replace("{uuid}", McLoginLoader.Output.Uuid) Raw = Raw.Replace("{uuid}", McLoginLoader.Output.Uuid)
If ReplaceTimeAndDate Then '设置窗口标题时需要动态替换日期和时间
Raw = Raw.Replace("{date}", Date.Now.ToString("yyyy/M/d"))
Raw = Raw.Replace("{time}", Date.Now.ToString("HH:mm:ss"))
End If
Select Case McLoginLoader.Input.Type Select Case McLoginLoader.Input.Type
Case McLoginType.Legacy Case McLoginType.Legacy
If PageLinkHiper.HiperState = LoadState.Finished Then If PageLinkHiper.HiperState = LoadState.Finished Then
@@ -42,15 +42,19 @@
#Region "读取默认(Original)文件夹,即当前、官启文件夹,可能没有结果" #Region "读取默认(Original)文件夹,即当前、官启文件夹,可能没有结果"
'扫描当前文件夹 '扫描当前文件夹
If CheckPermission(Path) AndAlso Directory.Exists(Path & "versions\") Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Path, .Type = McFolderType.Original}) Try
If Directory.Exists(Path & "versions\") Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Path, .Type = McFolderType.Original})
For Each Folder As DirectoryInfo In New DirectoryInfo(Path).GetDirectories For Each Folder As DirectoryInfo In New DirectoryInfo(Path).GetDirectories
If CheckPermission(Folder.FullName) AndAlso Directory.Exists(Folder.FullName & "versions\") OrElse Folder.Name = ".minecraft" Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Folder.FullName & "\", .Type = McFolderType.Original}) If Directory.Exists(Folder.FullName & "versions\") OrElse Folder.Name = ".minecraft" Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Folder.FullName & "\", .Type = McFolderType.Original})
Next Next
Catch ex As Exception
Log(ex, "扫描 PCL 所在文件夹中是否有 MC 文件夹失败")
End Try
'扫描官启文件夹 '扫描官启文件夹
Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\" Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\"
If (CacheMcFolderList.Count = 0 OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹 If (Not CacheMcFolderList.Any OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹
CheckPermission(MojangPath) AndAlso Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹 Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹
CacheMcFolderList.Add(New McFolder With {.Name = "官方启动器文件夹", .Path = MojangPath, .Type = McFolderType.Original}) CacheMcFolderList.Add(New McFolder With {.Name = "官方启动器文件夹", .Path = MojangPath, .Type = McFolderType.Original})
End If End If
@@ -406,7 +410,7 @@
End If End If
'无法获取 '无法获取
_Version.McName = "Unknown" _Version.McName = "Unknown"
Info = "PCL 无法识别该版本,请向作者反馈此问题" Info = "PCL 无法识别该版本的 MC 版本号"
Catch ex As Exception Catch ex As Exception
Log(ex, "识别 Minecraft 版本时出错") Log(ex, "识别 Minecraft 版本时出错")
_Version.McName = "Unknown" _Version.McName = "Unknown"
@@ -1915,7 +1919,7 @@ NextVersion:
'获取下载信息 '获取下载信息
Try Try
Log("[Minecraft] 开始获取 Authlib-Injector 下载信息") Log("[Minecraft] 开始获取 Authlib-Injector 下载信息")
DownloadInfo = GetJson(NetGetCodeByDownload({"https://download.mcbbs.net/mirrors/authlib-injector/artifact/latest.json", "https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"}, IsJson:=True)) DownloadInfo = GetJson(NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/mirrors/authlib-injector/artifact/latest.json"}, IsJson:=True))
Catch ex As Exception Catch ex As Exception
Log(ex, "获取 Authlib-Injector 下载信息失败") Log(ex, "获取 Authlib-Injector 下载信息失败")
End Try End Try
@@ -1926,9 +1930,7 @@ NextVersion:
'开始下载 '开始下载
Dim DownloadAddress As String = DownloadInfo("download_url") Dim DownloadAddress As String = DownloadInfo("download_url")
Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer) Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
Result.Add(New NetFile({ Result.Add(New NetFile({DownloadAddress}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
DownloadAddress.Replace("bmclapi2.bangbang93.com", "download.mcbbs.net"), DownloadAddress
}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
End If End If
End If End If
End If End If
@@ -1958,7 +1960,7 @@ NextVersion:
'获取 Url 的真实地址 '获取 Url 的真实地址
Urls.Add(Token.Url) Urls.Add(Token.Url)
If Token.Url.Contains("launcher.mojang.com/v1/objects") Then Urls = DlSourceLauncherOrMetaGet(Token.Url).ToList() 'Mappings If Token.Url.Contains("launcher.mojang.com/v1/objects") Then Urls = DlSourceLauncherOrMetaGet(Token.Url).ToList() 'Mappings
If Token.Url.Contains("maven") Then Urls.Insert(0, Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOf("maven")), "https://download.mcbbs.net/").Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven")) If Token.Url.Contains("maven") Then Urls.Insert(0, Token.Url.Replace(Mid(Token.Url, 1, Token.Url.IndexOf("maven")), "https://bmclapi2.bangbang93.com/").Replace("maven.fabricmc.net", "maven").Replace("maven.minecraftforge.net", "maven"))
End If End If
If Token.LocalPath.Contains("transformer-discovery-service") Then If Token.LocalPath.Contains("transformer-discovery-service") Then
'Transformer 文件释放 'Transformer 文件释放
@@ -1970,7 +1972,6 @@ NextVersion:
Dim OptiFineBase As String = Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_") Dim OptiFineBase As String = Token.LocalPath.Replace(If(Token.IsJumpLoader, JumpLoaderFolder, CustomMcFolder) & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(Token.LocalPath).Replace("-", "_")
OptiFineBase = "/maven/com/optifine/" & OptiFineBase OptiFineBase = "/maven/com/optifine/" & OptiFineBase
If OptiFineBase.Contains("_pre") Then OptiFineBase = OptiFineBase.Replace("com/optifine/", "com/optifine/preview_") If OptiFineBase.Contains("_pre") Then OptiFineBase = OptiFineBase.Replace("com/optifine/", "com/optifine/preview_")
Urls.Add("https://download.mcbbs.net" & OptiFineBase)
Urls.Add("https://bmclapi2.bangbang93.com" & OptiFineBase) Urls.Add("https://bmclapi2.bangbang93.com" & OptiFineBase)
ElseIf Urls.Count <= 2 Then ElseIf Urls.Count <= 2 Then
'普通文件 '普通文件
@@ -13,6 +13,14 @@ Public Module ModMod
Public Sub New(Path As String) Public Sub New(Path As String)
Me.Path = If(Path, "") Me.Path = If(Path, "")
End Sub End Sub
''' <summary>
''' Mod 的完整路径,去除最后的 .disabled 和 .old。
''' </summary>
Public ReadOnly Property RawPath As String
Get
Return GetPathFromFullPath(Path) & RawFileName
End Get
End Property
''' <summary> ''' <summary>
''' Mod 的完整文件名。 ''' Mod 的完整文件名。
@@ -768,6 +776,18 @@ VersionFindFail:
End Property End Property
Private _ModrinthHash As String Private _ModrinthHash As String
#End Region
#Region "API"
Public Overrides Function ToString() As String
Return $"{State} - {Path}"
End Function
Public Overrides Function Equals(obj As Object) As Boolean
Dim target = TryCast(obj, McMod)
Return target IsNot Nothing AndAlso Path = target.Path
End Function
#End Region #End Region
''' <summary> ''' <summary>
@@ -844,6 +864,17 @@ VersionFindFail:
'加载 McMod 对象 '加载 McMod 对象
Dim ModEntry As New McMod(ModFile.FullName) Dim ModEntry As New McMod(ModFile.FullName)
ModEntry.Load() ModEntry.Load()
Dim DumpMod As McMod = ModList.FirstOrDefault(Function(m) m.RawFileName = ModEntry.RawFileName)
If DumpMod IsNot Nothing Then
Dim DisabledMod As McMod = If(DumpMod.State = McMod.McModState.Disabled, DumpMod, ModEntry)
Log($"[Mod] 重复的 Mod 文件:{DumpMod.FileName} 与 {ModEntry.FileName},已忽略 {DisabledMod.FileName}", LogLevel.Debug)
If DisabledMod Is ModEntry Then
Continue For
Else
ModList.Remove(DisabledMod)
ModUpdateList.Remove(DisabledMod)
End If
End If
ModList.Add(ModEntry) ModList.Add(ModEntry)
'读取 Comp 缓存 '读取 Comp 缓存
If ModEntry.State = McMod.McModState.Unavaliable Then Continue For If ModEntry.State = McMod.McModState.Unavaliable Then Continue For
@@ -860,7 +891,8 @@ VersionFindFail:
Log($"[Mod] 共有 {ModList.Count} 个 Mod,其中 {ModUpdateList.Where(Function(m) m.Comp Is Nothing).Count} 个需要联网获取信息,{ModUpdateList.Where(Function(m) m.Comp IsNot Nothing).Count} 个需要更新信息") Log($"[Mod] 共有 {ModList.Count} 个 Mod,其中 {ModUpdateList.Where(Function(m) m.Comp Is Nothing).Count} 个需要联网获取信息,{ModUpdateList.Where(Function(m) m.Comp IsNot Nothing).Count} 个需要更新信息")
'排序 '排序
ModList = Sort(ModList, Function(Left As McMod, Right As McMod) As Boolean ModList = Sort(ModList,
Function(Left As McMod, Right As McMod) As Boolean
If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
Return Left.State = McMod.McModState.Unavaliable Return Left.State = McMod.McModState.Unavaliable
Else Else
@@ -96,7 +96,7 @@
'设置窗口标题 '设置窗口标题
For i = 1 To 3 For i = 1 To 3
If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then If IsWindowFinished AndAlso IsWindowAppeared AndAlso WindowTitle <> "" AndAlso State = MinecraftState.Running AndAlso Not GameProcess.HasExited Then
Dim RealTitle As String = WindowTitle.Replace("{time}", Date.Now.ToShortTimeString) Dim RealTitle As String = WindowTitle.Replace("{date}", Date.Now.ToString("yyyy/M/d")).Replace("{time}", Date.Now.ToString("HH:mm:ss"))
SetWindowText(WindowHandle, RealTitle.ToCharArray) SetWindowText(WindowHandle, RealTitle.ToCharArray)
End If End If
Thread.Sleep(64) Thread.Sleep(64)
+1 -1
View File
@@ -25,7 +25,7 @@
For Each File In EnumerateFiles(Path & "PCL\Musics\") For Each File In EnumerateFiles(Path & "PCL\Musics\")
'文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的 '文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
Dim Ext As String = File.Extension.ToLower Dim Ext As String = File.Extension.ToLower
If {"ini", "jpg", "txt", "cfg", "lrc", "db", "png"}.Contains(Ext) Then Continue For If {".ini", ".jpg", ".txt", ".cfg", ".lrc", ".db", ".png"}.Contains(Ext) Then Continue For
MusicAllList.Add(File.FullName) MusicAllList.Add(File.FullName)
Next Next
End If End If
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*” ' 方法是按如下所示使用“*”
<Assembly: AssemblyVersion("2.6.13.0")> <Assembly: AssemblyVersion("2.6.14.0")>
<Assembly: AssemblyFileVersion("2.6.13.0")> <Assembly: AssemblyFileVersion("2.6.14.0")>
<Assembly: NeutralResourcesLanguage("")> <Assembly: NeutralResourcesLanguage("")>
@@ -519,10 +519,8 @@ Public Module ModDownloadLib
Dim Sources As New List(Of String) Dim Sources As New List(Of String)
'BMCLAPI 源 'BMCLAPI 源
If DownloadInfo.IsPreview Then If DownloadInfo.IsPreview Then
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Else Else
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
End If End If
'官方源 '官方源
@@ -686,10 +684,8 @@ Retry:
Dim Sources As New List(Of String) Dim Sources As New List(Of String)
'BMCLAPI 源 'BMCLAPI 源
If DownloadInfo.IsPreview Then If DownloadInfo.IsPreview Then
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Else Else
Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "")) Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
End If End If
'官方源 '官方源
@@ -852,22 +848,11 @@ Retry:
Case "1.6.2" Case "1.6.2"
Address.Add("https://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar") Address.Add("https://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar")
Case "1.5.2" Case "1.5.2"
'Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar",
' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar"})
Address.Add("https://dl.liteloader.com/redist/1.5.2/liteloader-installer-1.5.2-01.jar") Address.Add("https://dl.liteloader.com/redist/1.5.2/liteloader-installer-1.5.2-01.jar")
Case Else Case Else
Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")") Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")")
End Select End Select
Else Else
'BMCLAPI 源下载的是安装后的 Jar,而不是 Installer,故无法使用
''BMCLAPI 源
'If DownloadInfo.IsPreview Then
' Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar",
' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar"})
'Else
' Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar",
' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar"})
'End If
'官方源 '官方源
Address.Add("http://jenkins.liteloader.com/job/LiteLoaderInstaller%20" & DownloadInfo.Inherit & "/lastSuccessfulBuild/artifact/" & If(DownloadInfo.Inherit = "1.8", "ant/dist/", "build/libs/") & DownloadInfo.FileName) Address.Add("http://jenkins.liteloader.com/job/LiteLoaderInstaller%20" & DownloadInfo.Inherit & "/lastSuccessfulBuild/artifact/" & If(DownloadInfo.Inherit = "1.8", "ant/dist/", "build/libs/") & DownloadInfo.FileName)
End If End If
@@ -1100,7 +1085,6 @@ Retry:
Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile))) Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
Dim Files As New List(Of NetFile) Dim Files As New List(Of NetFile)
Files.Add(New NetFile({ Files.Add(New NetFile({
"https://download.mcbbs.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
"https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
"https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
}, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))) }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
@@ -1123,7 +1107,7 @@ Retry:
Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean) Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
'选择 Java '选择 Java
Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
If Java Is Nothing Then If Java Is Nothing Then
If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。") If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
'开始自动下载 '开始自动下载
@@ -1133,7 +1117,7 @@ Retry:
Thread.Sleep(10) Thread.Sleep(10)
Loop Loop
'检查下载结果 '检查下载结果
Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0)) Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
If Task.IsAborted Then Exit Sub If Task.IsAborted Then Exit Sub
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。") If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
End If End If
@@ -1160,7 +1144,8 @@ Retry:
Dim LastResults As New Queue(Of String) Dim LastResults As New Queue(Of String)
Using outputWaitHandle As New AutoResetEvent(False) Using outputWaitHandle As New AutoResetEvent(False)
Using errorWaitHandle As New AutoResetEvent(False) Using errorWaitHandle As New AutoResetEvent(False)
AddHandler process.OutputDataReceived, Function(sender, e) AddHandler process.OutputDataReceived,
Function(sender, e)
Try Try
If e.Data Is Nothing Then If e.Data Is Nothing Then
outputWaitHandle.[Set]() outputWaitHandle.[Set]()
@@ -1182,7 +1167,8 @@ Retry:
End Try End Try
Return Nothing Return Nothing
End Function End Function
AddHandler process.ErrorDataReceived, Function(sender, e) AddHandler process.ErrorDataReceived,
Function(sender, e)
Try Try
If e.Data Is Nothing Then If e.Data Is Nothing Then
errorWaitHandle.[Set]() errorWaitHandle.[Set]()
@@ -1321,8 +1307,7 @@ Retry:
'添加主文件 '添加主文件
Dim Files As New List(Of NetFile) From {New NetFile({ Dim Files As New List(Of NetFile) From {New NetFile({
"https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
"https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName, "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
"https://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
}, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))} }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))}
Task.Output = Files Task.Output = Files
End Sub) With {.ProgressWeight = 0.5, .Show = False}) End Sub) With {.ProgressWeight = 0.5, .Show = False})
@@ -1650,7 +1635,7 @@ Retry:
RunInNewThread(Sub() RunInNewThread(Sub()
Try Try
Log("[Download] 刷新 Forge 推荐版本缓存开始") Log("[Download] 刷新 Forge 推荐版本缓存开始")
Dim Result As String = NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/forge/promos", "https://download.mcbbs.net/forge/promos"}) Dim Result As String = NetGetCodeByDownload("https://bmclapi2.bangbang93.com/forge/promos")
If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")") If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
Dim ResultJson As JContainer = GetJson(Result) Dim ResultJson As JContainer = GetJson(Result)
'获取所有推荐版本列表 '获取所有推荐版本列表
@@ -1721,7 +1706,6 @@ Retry:
'下载 '下载
'BMCLAPI 不支持 Fabric Installer 下载 'BMCLAPI 不支持 Fabric Installer 下载
Dim Address As New List(Of String) Dim Address As New List(Of String)
'Address.Add(Url.Replace("maven.fabricmc.net", "download.mcbbs.net/maven"))
Address.Add(Url) 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}) Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
'启动 '启动
@@ -1759,7 +1743,7 @@ Retry:
Task.Progress = 0.5 Task.Progress = 0.5
'构造文件请求 '构造文件请求
Task.Output = New List(Of NetFile) From {New NetFile({ Task.Output = New List(Of NetFile) From {New NetFile({
"https://download.mcbbs.net/fabric-meta/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json", "https://bmclapi2.bangbang93.com/fabric-meta/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json",
"https://meta.fabricmc.net/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json" "https://meta.fabricmc.net/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json"
}, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))} }, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))}
'新建 mods 文件夹 '新建 mods 文件夹
@@ -16,7 +16,6 @@
<StackPanel Orientation="Horizontal" Height="35"> <StackPanel Orientation="Horizontal" Height="35">
<local:MyButton x:Name="BtnIntroWeb" MinWidth="140" Text="转到 CurseForge" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" /> <local:MyButton x:Name="BtnIntroWeb" MinWidth="140" Text="转到 CurseForge" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
<local:MyButton x:Name="BtnIntroWiki" MinWidth="140" Text="转到 MC 百科" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" /> <local:MyButton x:Name="BtnIntroWiki" MinWidth="140" Text="转到 MC 百科" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnIntroMCBBS" MinWidth="140" Text="转到 MCBBS" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnIntroCopy" MinWidth="140" Text="复制名称" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" /> <local:MyButton x:Name="BtnIntroCopy" MinWidth="140" Text="复制名称" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@@ -145,7 +145,6 @@
'决定按钮显示 '决定按钮显示
BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth") BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible) BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
BtnIntroMCBBS.Visibility = If(Project.MCBBS = 0, Visibility.Collapsed, Visibility.Visible)
AniControlEnabled -= 1 AniControlEnabled -= 1
End Sub End Sub
@@ -323,9 +322,6 @@
Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html") OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
End Sub End Sub
Private Sub BtnIntroMCBBS_Click(sender As Object, e As EventArgs) Handles BtnIntroMCBBS.Click
OpenWebsite("https://www.mcbbs.net/thread-" & Project.MCBBS & "-1-1.html")
End Sub
Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
ClipboardSet(CompItem.LabTitle.Text) ClipboardSet(CompItem.LabTitle.Text)
End Sub End Sub
@@ -33,7 +33,7 @@
<Path Name="PathTypeOne" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center" <Path Name="PathTypeOne" HorizontalAlignment="Left" RenderTransformOrigin="0.5,0.5" Margin="12,0,0,0" Stretch="Uniform" Fill="{DynamicResource ColorBrush3}" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
Data=""> Data="">
<Path.RenderTransform> <Path.RenderTransform>
<ScaleTransform ScaleX="1.2" ScaleY="1.2" /> <ScaleTransform ScaleX="1.05" ScaleY="1.05" />
</Path.RenderTransform> </Path.RenderTransform>
</Path> </Path>
<TextBlock Name="LabTypeOne" Text="" FontSize="14" VerticalAlignment="Center" Grid.Column="1" Foreground="{DynamicResource ColorBrush3}" Margin="15,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" /> <TextBlock Name="LabTypeOne" Text="" FontSize="14" VerticalAlignment="Center" Grid.Column="1" Foreground="{DynamicResource ColorBrush3}" Margin="15,0,12,0" Grid.ColumnSpan="2" SnapsToDevicePixels="False" UseLayoutRounding="False" />
@@ -286,7 +286,7 @@ UnknownType:
Setup.Set("LoginType", McLoginType.Ms) Setup.Set("LoginType", McLoginType.Ms)
PanType.Visibility = Visibility.Collapsed PanType.Visibility = Visibility.Collapsed
PanTypeOne.Visibility = Visibility.Visible PanTypeOne.Visibility = Visibility.Visible
PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonMs) PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield)
LabTypeOne.Text = "正版登录" LabTypeOne.Text = "正版登录"
RadioLoginType5.Visibility = Visibility.Visible RadioLoginType5.Visibility = Visibility.Visible
RadioLoginType0.Visibility = Visibility.Collapsed RadioLoginType0.Visibility = Visibility.Collapsed
@@ -295,7 +295,7 @@ UnknownType:
Setup.Set("LoginType", McLoginType.Legacy) Setup.Set("LoginType", McLoginType.Legacy)
PanType.Visibility = Visibility.Collapsed PanType.Visibility = Visibility.Collapsed
PanTypeOne.Visibility = Visibility.Visible PanTypeOne.Visibility = Visibility.Visible
PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonUser) PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline)
LabTypeOne.Text = "离线登录" LabTypeOne.Text = "离线登录"
Case 3 '统一通行证 Case 3 '统一通行证
If Setup.Get("CacheNideAccess") = "" Then If Setup.Get("CacheNideAccess") = "" Then
@@ -58,307 +58,21 @@
'检查网络状态 '检查网络状态
Private Shared Sub InitPingCheck(Task As LoaderTask(Of Integer, Integer)) Private Shared Sub InitPingCheck(Task As LoaderTask(Of Integer, Integer))
PingTime = 0
Try
Log("[HiPer] 网络检测:连通检测开始")
Dim StartTime = Date.Now.Ticks
Dim Result As String = NetGetCodeByClient("https://www.baidu.com/duty/", Encoding.UTF8, 20000, "")
Dim DuringTime As Integer = (Date.Now.Ticks - StartTime) / 10000
If Result.Contains("百度") Then
Log("[HiPer] 网络检测:连通检测成功(" & DuringTime & "ms)")
PingTime = DuringTime
Else
Log("[HiPer] 网络检测:连通检测失败(获取的内容有误)")
PingTime = -1
End If
Catch ex As ThreadInterruptedException
Catch ex As Exception
Log(ex, "连通检测失败")
PingTime = -1
End Try
End Sub End Sub
Private Shared Sub InitIpCheck() Private Shared Sub InitIpCheck()
IpCheckStatus = LoadState.Loading
Try
Log("[HiPer] 网络检测:IP 地址检测开始")
Dim IpCheckResult As String = NetRequestOnce("https://ipinfo.io/json", "GET", "", "application/json", 10000)
Dim Country As String = GetJson(IpCheckResult)("country")
IpIsInChina = Country = "CN"
IpCheckStatus = LoadState.Finished
Log("[HiPer] 网络检测:IP 地址检测结果:" & Country)
Catch ex As ThreadInterruptedException
IpCheckStatus = LoadState.Aborted
Catch ex As Exception
Log(ex, "IP 地址检测失败")
IpCheckStatus = LoadState.Failed
IpIsInChina = True
End Try
End Sub End Sub
Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean
Private Shared Sub InitCheck(Task As LoaderTask(Of Integer, Integer)) Private Shared Sub InitCheck(Task As LoaderTask(Of Integer, Integer))
'检查协议
If Not Setup.Get("LinkEula") Then
Reopen:
Select Case MyMsgBox("PCL 的联机服务由速聚授权提供。" & vbCrLf & "在使用前,你需要同意速聚的用户服务协议和隐私政策。", "协议授权", "同意", "拒绝", "查看用户服务协议和隐私政策")
Case 1
Setup.Set("LinkEula", True)
Case 2
Throw New Exception("$你拒绝了用户服务协议……")
Case 3
OpenWebsite("https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxMTMyODk3Mg==&action=getalbum&album_id=2585385685407514625&scene=173&from_msgid=2247483720&from_itemidx=1&count=3&nolastread=1#wechat_redirect")
GoTo Reopen
End Select
End If
'等待网络环境检查加载器结束
SetLoadDesc("正在检查网络环境……", "检查网络环境")
Do While Task.State = LoadState.Loading AndAlso Not (PingTime <> 0 AndAlso IpCheckStatus <> LoadState.Loading)
Thread.Sleep(50)
Loop
'获取网络环境检查结果
If IpCheckStatus = LoadState.Finished AndAlso Not IpIsInChina Then
If MyMsgBox("检测到你的 IP 不在中国,这会导致联机变得非常不稳定。" & vbCrLf &
"如果你正开着加速器或者 VPN,请先关闭它们,然后再继续……", "警告", "继续", "取消", IsWarn:=True, ForceWait:=True) = 2 Then
Throw New Exception("$请在关闭加速器或者 VPN 后点击重试。")
End If
Task.Progress = 0.5
InitIpCheck() '重新进行检查
End If
'没有联网
If IpCheckStatus = LoadState.Failed AndAlso PingTime = -1 Then
Throw New Exception("$PCL 没法连上网……" & vbCrLf & "如果你改变了网络环境,请重启 PCL。")
End If
End Sub End Sub
'获取所需文件 '获取所需文件
Private Shared Sub InitGetFile(Task As LoaderTask(Of Integer, List(Of NetFile))) Private Shared Sub InitGetFile(Task As LoaderTask(Of Integer, List(Of NetFile)))
'初始化
SetLoadDesc("正在初始化……", "初始化")
Directory.CreateDirectory(PathHiper)
WriteFile(PathHiper & "HiPer 联机模块.vbs", "createobject(""wscript.shell"").run """"""" & PathHiper & "HiPer 联机模块.exe"""" v"", 0", Encoding:=Encoding.GetEncoding("GB18030")) '准备启动脚本
'获取索引码文件
SetLoadDesc("正在获取索引码文件……", "获取索引码文件")
Dim Cert As String = RunInUiWait(Function() FrmLinkHiper.TextCert.Text)
Log("[Hiper] 联机索引码:" & Cert)
If Cert = Setup.Get("LinkHiperCertLast") AndAlso File.Exists(PathHiper & "cert.yml") Then
Log("[Hiper] 索引码与上次输入的一致,跳过获取步骤")
Else
Dim CertRaw As String
Try
CertRaw = NetRequestOnce("https://cert.mcer.cn/" & Cert & ".yml", "GET", "", "")
Catch ex As Exception
If GetExceptionSummary(ex).Contains("(404)") Then
Throw New CertOutdatedException '索引码无效或已过期
ElseIf GetExceptionSummary(ex).Contains("too many requests") Then
Throw New Exception("你的尝试太频繁了,请暂时啥都别点,等两分钟后再试……")
Else
Throw
End If
End Try
If Not CertRaw.Contains("WARNING <<< AUTO SYNC AREA") Then Throw New Exception("获取到的索引码文件内容有误!")
WriteFile(PathHiper & "cert.yml", CertRaw)
Setup.Set("LinkHiperCertLast", Cert)
Setup.Set("LinkHiperCertTime", "")
End If
Task.Progress = 0.25
'获取 CPU 架构
SetLoadDesc("正在获取 CPU 架构……", "获取 CPU 架构")
Dim Architecture As String
Select Case GetType(String).Assembly.GetName().ProcessorArchitecture
Case Reflection.ProcessorArchitecture.X86
Architecture = "386"
Case Reflection.ProcessorArchitecture.Amd64
Architecture = "amd64"
Case Reflection.ProcessorArchitecture.Arm
Architecture = "arm64"
Case Else
Architecture = "arm64"
Log("[Hiper] 当前 CPU 架构为 " & GetStringFromEnum(GetType(String).Assembly.GetName().ProcessorArchitecture) & ",没有最适合的项,这可能会导致联机模块无法启动!", LogLevel.Debug)
End Select
Log("[Hiper] CPU 架构:" & Architecture)
'检查更新:hiper.exe
SetLoadDesc("正在检查联机模块本体更新……", "检查联机模块本体更新")
Dim RequiredFiles As New List(Of NetFile)
Dim Checksums As String = Nothing, ChecksumHiper As String = Nothing
If File.Exists(PathHiper & "HiPer 联机模块.exe") Then
Try
Checksums = NetGetCodeByDownload({
"http://mirror.hiper.cn.s2.the.bb/packages.sha1",
"http://mirror.hiper.cn.s3.the.bb:175/packages.sha1",
"https://gitcode.net/to/hiper/-/raw/master/packages.sha1",
"https://cert.mcer.cn/mirror/packages.sha1"
})
Catch ex As Exception
Log(ex, "获取联机模块更新信息失败,将会强制启动联机模块", LogLevel.Hint)
GoTo FinishHiperFileCheck
End Try
ChecksumHiper = RegexSeek(Checksums, "[0-9a-f]{40}(?= windows-" & Architecture & "/hiper.exe)")
If ChecksumHiper Is Nothing Then
Log("[Hiper] 未找到联机模块更新信息,将会强制启动联机模块", LogLevel.Hint)
GoTo FinishHiperFileCheck
End If
Log("[Hiper] hiper.exe 的所需 SHA1:" & ChecksumHiper)
If ChecksumHiper = GetFileSHA1(PathHiper & "HiPer 联机模块.exe") Then
Log("[Hiper] hiper.exe 文件校验通过,无需重新下载")
GoTo FinishHiperFileCheck
End If
End If
Log("[Hiper] 需要重新下载 hiper.exe")
RequiredFiles.Add(New NetFile({"http://mirror.hiper.cn.s2.the.bb/windows-" & Architecture & "/hiper.exe",
"http://mirror.hiper.cn.s3.the.bb:175/windows-" & Architecture & "/hiper.exe",
"https://gitcode.net/to/hiper/-/raw/master/windows-" & Architecture & "/hiper.exe?inline=false",
"https://cert.mcer.cn/mirror/windows-" & Architecture & "/hiper.exe"},
PathHiper & "HiPer 联机模块.exe",
New FileChecker(Hash:=ChecksumHiper, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
FinishHiperFileCheck:
Task.Progress = 0.5
'检查更新:wintun.dll
Dim ChecksumWintun As String = Nothing
If File.Exists(PathHiper & "wintun.dll") Then
Try
If Checksums Is Nothing Then
Checksums = NetGetCodeByDownload({
"http://mirror.hiper.cn.s2.the.bb/packages.sha1",
"http://mirror.hiper.cn.s3.the.bb:175/packages.sha1",
"https://gitcode.net/to/hiper/-/raw/master/packages.sha1",
"https://cert.mcer.cn/mirror/packages.sha1"
})
End If
Catch ex As Exception
Log(ex, "获取联机模块更新信息失败,将会强制启动联机模块", LogLevel.Hint)
GoTo FinishWintunFileCheck
End Try
ChecksumWintun = RegexSeek(Checksums, "[0-9a-f]{40}(?= windows-" & Architecture & "/wintun.dll)")
If ChecksumWintun Is Nothing Then
Log("[Hiper] 未找到联机模块更新信息,将会强制启动联机模块", LogLevel.Hint)
GoTo FinishWintunFileCheck
End If
Log("[Hiper] wintun.dll 的所需 SHA1:" & ChecksumWintun)
If ChecksumWintun = GetFileSHA1(PathHiper & "wintun.dll") Then
Log("[Hiper] wintun.dll 文件校验通过,无需重新下载")
GoTo FinishWintunFileCheck
End If
End If
Log("[Hiper] 需要重新下载 wintun.dll")
RequiredFiles.Add(New NetFile({"http://mirror.hiper.cn.s2.the.bb/windows-" & Architecture & "/wintun.dll",
"http://mirror.hiper.cn.s3.the.bb:175/windows-" & Architecture & "/wintun.dll",
"https://gitcode.net/to/hiper/-/raw/master/windows-" & Architecture & "/wintun.dll?inline=false",
"https://cert.mcer.cn/mirror/windows-" & Architecture & "/wintun.dll"},
PathHiper & "wintun.dll",
New FileChecker(Hash:=ChecksumWintun, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
FinishWintunFileCheck:
Task.Progress = 0.75
'检查更新:MCB
If Not IsServerSide Then
SetLoadDesc("正在检查联机模块组件更新……", "检查联机模块组件更新")
Dim ChecksumMcb As String = Nothing
If File.Exists(PathHiper & "MCB 联机模块.exe") Then
Try
Checksums = NetGetCodeByDownload({
"http://mirror.hiper.cn.s2.the.bb/utils/minecraft-broadcast/packages.sha1",
"http://mirror.hiper.cn.s3.the.bb:175/utils/minecraft-broadcast/packages.sha1",
"https://gitcode.net/to/hiper/-/raw/master/utils/minecraft-broadcast/packages.sha1",
"https://cert.mcer.cn/mirror/utils/minecraft-broadcast/packages.sha1"
})
Catch ex As Exception
Log(ex, "获取联机模块组件更新信息失败,将会强制启动联机模块组件", LogLevel.Hint)
GoTo FinishMcbFileCheck
End Try
ChecksumMcb = RegexSeek(Checksums, "[0-9a-f]{40}(?= mcb-windows-" & Architecture & ".exe)")
If ChecksumMcb Is Nothing Then
Log("[Hiper] 未找到联机模块组件更新信息,将会强制启动联机模块组件", LogLevel.Hint)
GoTo FinishMcbFileCheck
End If
Log("[Hiper] mcb.exe 的所需 SHA1:" & ChecksumMcb)
If ChecksumMcb = GetFileSHA1(PathHiper & "MCB 联机模块.exe") Then
Log("[Hiper] mcb.exe 文件校验通过,无需重新下载")
GoTo FinishMcbFileCheck
End If
End If
Log("[Hiper] 需要重新下载 mcb.exe")
RequiredFiles.Add(New NetFile({
"http://mirror.hiper.cn.s2.the.bb/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe",
"http://mirror.hiper.cn.s3.the.bb:175/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe",
"https://gitcode.net/to/hiper/-/raw/master/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe?inline=false",
"https://cert.mcer.cn/mirror/utils/minecraft-broadcast/mcb-windows-" & Architecture & ".exe"},
PathHiper & "MCB 联机模块.exe",
New FileChecker(Hash:=ChecksumMcb, MinSize:=1024 * 200))) 'Hash 的默认值即为 Nothing,所以 Checksum 可以为 Nothing
FinishMcbFileCheck:
End If
'添加 point.yml
RequiredFiles.Add(New NetFile({"https://cert.mcer.cn/point.yml"}, PathHiper & "point.yml", New FileChecker(MinSize:=200)))
'开始下载
SetLoadDesc("正在下载联机模块……", "下载联机模块")
Task.Output = RequiredFiles
End Sub End Sub
Public Class CertOutdatedException Public Class CertOutdatedException
Inherits Exception Inherits Exception
End Class End Class
'启动联机模块 '启动联机模块
Private Shared Sub InitLaunch(Task As LoaderTask(Of Integer, Integer)) Private Shared Sub InitLaunch(Task As LoaderTask(Of Integer, Integer))
'关闭运行中的 HiPer,然后再刷新凭证文件
SetLoadDesc("正在关闭运行中的联机模块……", "关闭运行中的联机模块")
HiperStop(True)
'准备凭证文件
SetLoadDesc("正在准备索引码文件……", "准备索引码文件")
Dim CertFile As String = ReadFile(PathHiper & "cert.yml")
'添加防火墙配置
If IsServerSide Then
CertFile += ("\n\ninbound:\n port: " & HostPort & "\n proto: tcp\n host: any").Replace("\n", vbCrLf)
Else
CertFile += ("\n\noutbound:\n port: " & HostPort & "\n proto: tcp\n host: " & HostIp).Replace("\n", vbCrLf)
End If
CertFile += "\nlogging:\n format: json".Replace("\n", vbCrLf)
'更新 point.yml 段
Const SyncAreaRegex As String = "(?<=#[ ]*WARNING >>> AUTO SYNC AREA)[\s\S]+?(?=#[ ]*WARNING <<< AUTO SYNC AREA)"
CertFile = CertFile.Replace(RegexSeek(CertFile, SyncAreaRegex), RegexSeek(ReadFile(PathHiper & "point.yml"), SyncAreaRegex))
WriteFile(PathHiper & "config.yml", CertFile)
AllNodes = RegexSearch(CertFile.Replace(vbLf, "cr"), "(?<="")[0-9]+.[0-9]+.[0-9]+.[0-9]+(?=""( )?:( )?cr - "")").Distinct.
Where(Function(l) Not (l = "6.6.1.1" OrElse l = "6.6.2.2" OrElse l = "6.6.3.3")).ToList
If AllNodes.Count < 2 Then Throw New Exception("$索引码文件格式有误,未找到节点 IP!")
Task.Progress = 0.05
'尝试启动
HiperStart(Task) '启动失败会直接抛出异常
Task.Progress = 0.2
'检查连接情况
SetLoadDesc("正在连接到联机节点 (1/2)……" & If(IpIsInChina, "", vbCrLf & "由于网络不在中国,可能要花费很长时间。"), "检查联机节点")
If Not IsServerSide AndAlso HostIp = HiperIp Then Throw New Exception("$还搁这自己连自己?!不是吧?!")
Dim PingNodeResults As New List(Of Integer)
Dim UnpingedNodes As New List(Of String)(AllNodes)
Do Until PingNodeResults.Count >= 2 OrElse Task.IsAborted
Dim SelectedNode = RandomOne(UnpingedNodes)
Dim PingNode = Ping(SelectedNode, 4000)
If PingNode = -1 Then
Task.Progress += (If(PingNodeResults.Count = 0, 0.58, 0.88) - Task.Progress) * 0.3
Else
SetLoadDesc("正在连接到联机节点 (2/2)……" & If(IpIsInChina, "", vbCrLf & "由于网络不在中国,可能要花费很长时间。"), "检查联机节点")
Task.Progress = 0.6
PingNodeResults.Add(PingNode)
UnpingedNodes.Remove(SelectedNode)
End If
Loop
PingNodes = PingNodeResults.Min
Task.Progress = 0.9
'与房主 Ping 一下看看
If Not IsServerSide Then
SetLoadDesc("正在连接到房主……", "检查房主")
HostPing = -1 '实际上不使用本次 Ping 的结果,因为它真的偏大……
Do
Dim PingHost = Ping(HostIp, 4000, False)
If PingHost = -1 Then
Thread.Sleep(500)
Else
Exit Do
End If
Loop Until Task.IsAborted
'在等待 2 秒后再次触发 Ping,以刷新显示的 Ping
RunInNewThread(Sub()
Thread.Sleep(2000)
TaskPingHost.Start(True, IsForceRestart:=True)
End Sub, "HiPer Delayed Ping")
End If
'刷新完成页面
SetLoadDesc("正在加载完成页面……", "加载完成页面")
RunInUiWait(AddressOf FrmLinkHiper.WatcherTimer1)
End Sub End Sub
Private Shared PingNodes As Integer, AllNodes As List(Of String) Private Shared PingNodes As Integer, AllNodes As List(Of String)
@@ -389,40 +103,6 @@ FinishMcbFileCheck:
''' </summary> ''' </summary>
Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean
Return False Return False
HiperStop = False
'修改凭证
Dim ConfigContent As String = ReadFile(PathHiper & "config.yml")
If Not ConfigContent.Contains("enable: false") Then
WriteFile(PathHiper & "config.yml", ConfigContent & vbCrLf & "enable: false")
End If
'关闭所有进程
For Each ProcessObject In Process.GetProcesses
Dim IsHiper As Boolean = ProcessObject.ProcessName = "HiPer 联机模块"
Dim IsMcb As Boolean = ProcessObject.ProcessName = "MCB 联机模块"
If Not IsHiper AndAlso Not IsMcb Then Continue For
HiperStop = True
Try
If IsMcb Then
ProcessObject.Kill()
Log("[HiPer] 已结束进程 PID " & ProcessObject.Id & ":" & ProcessObject.ProcessName)
If HiperStop AndAlso SleepWhenKilled Then Thread.Sleep(1000) '等待 1 秒确认进程已退出
Else 'IsHiper
If SleepWhenKilled Then
Thread.Sleep(4000) '等待 4 秒确认进程已退出
If Not ProcessObject.HasExited Then
ProcessObject.Kill()
Log("[HiPer] 已结束进程 PID " & ProcessObject.Id & ":" & ProcessObject.ProcessName)
End If
End If
End If
Catch ex As Exception
Log(ex, "结束进程失败(" & ProcessObject.Id & "," & ProcessObject.ProcessName & ")")
End Try
Next
'初始化
HiperState = LoadState.Waiting
HiperProcessId = -1 : McbProcessId = -1
HiperCertTime = Nothing
End Function End Function
''' <summary> ''' <summary>
''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。 ''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。
@@ -430,125 +110,10 @@ FinishMcbFileCheck:
''' 若程序正在运行,则会先停止其运行。 ''' 若程序正在运行,则会先停止其运行。
''' </summary> ''' </summary>
Public Shared Sub HiperStart(Task As LoaderTask(Of Integer, Integer)) Public Shared Sub HiperStart(Task As LoaderTask(Of Integer, Integer))
Try
SetLoadDesc("正在启动联机模块……", "启动联机模块")
PossibleFailReason = Nothing
'启动 Hiper
Log("[Hiper] 启动 Hiper 进程")
DeleteDirectory(PathHiper & "logs") '清理日志
Dim HiperInfo = New ProcessStartInfo With {
.FileName = "wscript",
.Arguments = """" & PathHiper & "HiPer 联机模块.vbs""",
.Verb = "runas" '需要管理员权限
}
Dim HiperVbsProcess As New Process() With {.StartInfo = HiperInfo}
HiperVbsProcess.Start()
'查找真正的 Hiper 进程
SetLoadDesc("联机模块正在启动……", "联机模块加载")
Do
Dim GotProcesses = Process.GetProcesses.
Where(Function(l) l.ProcessName = "HiPer 联机模块" AndAlso Math.Abs((l.StartTime - Date.Now).TotalMinutes) < 1)
If GotProcesses.Count > 0 Then
HiperProcessId = GotProcesses.First.Id
Log("[Hiper] 已发现 Hiper 进程,PID:" & HiperProcessId)
Exit Do
End If
Thread.Sleep(50)
Loop While Task.State = LoadState.Loading AndAlso Not Task.IsAborted
If HiperProcessId = -1 Then Throw New Exception("联机模块未能成功启动!")
HiperState = LoadState.Loading
Task.Progress = 0.15
'抓取日志
Dim LogLine As Integer = 0
Do
Thread.Sleep(100)
If Not File.Exists(PathHiper & "logs\hiper.log") Then Continue Do
Dim LogLines As String() = ReadFile(PathHiper & "logs\hiper.log").TrimEnd(vbLf).Split(vbLf)
For i = LogLine To LogLines.Count - 1
HiperLogLine(LogLines(i), Task)
Next
LogLine = LogLines.Count
Loop While HiperState = LoadState.Loading AndAlso Not Task.IsAborted AndAlso Process.GetProcesses.Any(Function(l) l.Id = HiperProcessId)
'输出
If HiperState = LoadState.Finished Then
Log("[Hiper] Hiper 启动完成")
ElseIf PossibleFailReason IsNot Nothing AndAlso PossibleFailReason.Contains("索引码已过期") Then
Throw New CertOutdatedException()
Else
Throw New Exception(If(PossibleFailReason, "联机模块因未知原因启动失败!"))
End If
'启动 MCB
Task.Progress = 0.25
If Not IsServerSide Then
Try
Log("[Hiper] 启动 MCB 进程")
Dim McbInfo = New ProcessStartInfo With {
.FileName = PathHiper & "MCB 联机模块.exe", .WorkingDirectory = PathHiper,
.UseShellExecute = False, .CreateNoWindow = True,
.RedirectStandardError = True, .RedirectStandardOutput = True,
.Arguments = "-addr " & HostIp & ":" & HostPort & " -motd ""PCL 联机房间"""
}
Dim McbProcess As New Process() With {.StartInfo = McbInfo}
McbProcess.Start()
McbProcessId = McbProcess.Id
Catch ex As Exception
Throw New Exception("联机模块组件启动失败", ex)
End Try
End If
Catch ex As Exception
Try
HiperStop(True) '由于启动失败停止进程
Catch
End Try
HiperState = LoadState.Failed
Throw
End Try
End Sub End Sub
'Hiper 日志 'Hiper 日志
Private Shared Sub HiperLogLine(Content As String, Task As LoaderTask(Of Integer, Integer)) Private Shared Sub HiperLogLine(Content As String, Task As LoaderTask(Of Integer, Integer))
'检查报错
Dim ContentTest As String = Content.ToLower
Dim ErrorMessage As String = Nothing
Dim ContentJson As JObject = Nothing
If Content.StartsWith("{""") AndAlso Content.EndsWith("}") Then
ContentJson = GetJson(Content)
ContentTest = If(ContentJson("error"), ContentJson("msg")).ToString.ToLower
ErrorMessage = ContentJson("error")
End If
If ContentTest.Contains("hiper certificate for this host is expired") Then
PossibleFailReason = "$你的索引码已过期,请更换新的索引码!"
ElseIf ContentTest.Contains("error creating interface: access is denied") Then
PossibleFailReason = "$没有获得管理员权限,无法创建网络通道!"
ElseIf ContentTest.Contains("cannot create a file when that file already exists") Then
PossibleFailReason = "$请不要重复开启多个联机模块!"
ElseIf Content.Contains("failed to load config") Then
PossibleFailReason = "$索引码文件内容存在错误!"
ElseIf Content.Contains("system cannot find the file specified") Then
PossibleFailReason = "$创建网络通道失败!" & vbCrLf & "如果你曾启动过 VPN 软件,请先打开那个软件,然后关掉它,最后再重试。虽然不知道为啥,但这样大概管用……"
End If
If PossibleFailReason Is Nothing AndAlso ErrorMessage IsNot Nothing Then PossibleFailReason = ErrorMessage
'检查结束
If ContentJson IsNot Nothing Then
Dim Message As String = ContentJson("msg")
If Message = "HiPer interface is active" Then
HiperIp = ContentJson("network").ToString.Split("/").First
If IsServerSide Then HostIp = HiperIp
Log("[Hiper] Hiper 启动完成,IP:" & HiperIp)
HiperState = LoadState.Finished
ElseIf Message = "Validity of client certificate" Then
Dim VaildTime As String = ContentJson("valid")
Date.TryParseExact(VaildTime, "yyyy-MM-dd HH:mm:ss", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.None, HiperCertTime)
Log("[Hiper] 索引码到期时间:" & HiperCertTime.ToString)
Setup.Set("LinkHiperCertTime", HiperCertTime.ToString)
End If
End If
'写入日志
If ModeDebug OrElse Content.Contains("""error""") Then Log("[Hiper] " & Content)
End Sub End Sub
Private Shared PossibleFailReason As String = Nothing Private Shared PossibleFailReason As String = Nothing
@@ -643,42 +208,6 @@ FinishMcbFileCheck:
'每 15 秒执行的 Timer '每 15 秒执行的 Timer
Private Shared HostPing As Integer = -1 Private Shared HostPing As Integer = -1
Private Sub WatcherTimer15() Private Sub WatcherTimer15()
If Not (HiperState = LoadState.Finished OrElse HiperState = LoadState.Loading) Then Exit Sub
'检查 HiPer 崩溃
Try
Process.GetProcessById(HiperProcessId)
Catch
Dim LogLines = ReadFile(PathHiper & "logs\hiper.log").TrimEnd(vbLf).Split(vbLf)
HiperExit(False)
MyMsgBox("由于联机模块异常退出,已退出联机房间。" & vbCrLf & vbCrLf &
"联机模块最后的日志:" & vbCrLf & Join(LogLines.Skip(LogLines.Count - 3).ToList, vbCrLf), "联机断开")
End Try
'下面的部分需要完全完成加载
If HiperState <> LoadState.Finished Then Exit Sub
'检查 MCB 崩溃
Try
If Not IsServerSide Then Process.GetProcessById(McbProcessId)
Catch
HiperExit(False)
MyMsgBox("由于 MCB 联机模块异常退出,已退出联机房间。", "联机断开")
End Try
'重新检查 Ping
If Not IsServerSide Then
Dim PingHostFailedCount As Integer = 0
Do
TaskPingHost.WaitForExit(True, IsForceRestart:=True)
If TaskPingHost.Output = -1 Then
PingHostFailedCount += 1
Log("[HiPer] Ping 房主失败(第 " & PingHostFailedCount & " 次)")
If PingHostFailedCount >= 3 Then
HiperExit(False) : MyMsgBox("与房主断开连接,已退出联机房间。", "联机断开") : Exit Sub
End If
Else
If PingHostFailedCount > 0 Then Log("[HiPer] Ping 房主已恢复成功(" & HostPing & "ms)")
Exit Do
End If
Loop While True
End If
End Sub End Sub
#End Region #End Region
@@ -711,14 +240,6 @@ FinishMcbFileCheck:
'创建房间 '创建房间
Private Sub BtnSelectCreate_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectCreate.MouseLeftButtonUp Private Sub BtnSelectCreate_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectCreate.MouseLeftButtonUp
''获取端口号
'Dim PortInput As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {
' New ValidateInteger(2, 65535),
' New ValidateExceptSame({"55555", "55557"}, "端口不能为 %!")
' }, "在 MC 的暂停画面选择【对局域网开放】", "输入端口号", "确定", "取消")
'If PortInput Is Nothing Then Exit Sub
''开始
'RoomCreate(Val(PortInput))
End Sub End Sub
Private Sub RoomCreate(Port As Integer) Private Sub RoomCreate(Port As Integer)
'记录信息 '记录信息
@@ -730,34 +251,6 @@ FinishMcbFileCheck:
'加入房间 '加入房间
Private Sub BtnSelectJoin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectJoin.MouseLeftButtonUp Private Sub BtnSelectJoin_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles BtnSelectJoin.MouseLeftButtonUp
' '获取信息
' Dim Code As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {New ValidateLength(8, 99)}, "",
' "输入联机码", "确定", "取消")
' If Code Is Nothing Then Exit Sub
' '记录信息
' If Not Code.EndsWith(RequestVersion) OrElse Not Code.StartsWith("P") Then GoTo WrongCode
' Dim Ip As String, Port As Integer
' Try
' Dim IpAndPort As Long = RadixConvert(Code.Substring(1, Code.Length - 2).ToUpper, 36, 10)
' 'IpAndPort = HostPort + IpParts(0) * 65536 + IpParts(1) * 65536 * 256 + IpParts(2) * 65536 * 256 * 256 + IpParts(3) * 65536 * 256 * 256 * 256
' Ip = Math.Floor(IpAndPort / (65536L * 256 * 256 * 256))
' IpAndPort = IpAndPort Mod (65536L * 256 * 256 * 256)
' Ip = Math.Floor(IpAndPort / (65536L * 256 * 256)) & "." & Ip
' IpAndPort = IpAndPort Mod (65536L * 256 * 256)
' Ip = Math.Floor(IpAndPort / (65536L * 256)) & "." & Ip
' IpAndPort = IpAndPort Mod (65536L * 256)
' Ip = Math.Floor(IpAndPort / (65536L)) & "." & Ip
' Port = IpAndPort Mod 65536
' Catch
' GoTo WrongCode
' End Try
' '启动
' RoomJoin(Ip, Port)
' Exit Sub
'WrongCode:
' If Not Code.StartsWith("P") AndAlso Code.Length >= 49 Then Hint("你输入的可能是 IOI 的联机码,请在左侧的联机方式中选择 IOI!", HintType.Critical) : Exit Sub
' If Code.StartsWith("P") AndAlso Not Code.EndsWith(RequestVersion) Then Hint("你的 PCL 版本与房主的 PCL 版本不一致!", HintType.Critical) : Exit Sub
' Hint("你输入的联机码无效!", HintType.Critical)
End Sub End Sub
Private Sub RoomJoin(Ip As String, Port As Integer) Private Sub RoomJoin(Ip As String, Port As Integer)
'记录信息 '记录信息
@@ -773,54 +266,6 @@ FinishMcbFileCheck:
'承接状态切换的 UI 改变 '承接状态切换的 UI 改变
Private Sub OnLoadStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState) Private Sub OnLoadStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState)
Select Case NewState
Case LoadState.Loading
UpdateProgress(0)
If IsServerSide Then
LabLoadTitle.Text = "正在创建联机房间"
Log("[Hiper] 正在创建联机房间,端口 " & HostPort)
Else
LabLoadTitle.Text = "正在加入联机房间"
Log("[Hiper] 正在加入联机房间,目标 IP " & HostIp & ":" & HostPort)
End If
LabLoadDesc.Text = "正在初始化……"
LoadStep = "准备初始化"
Case LoadState.Failed
UpdateProgress(1)
If IsServerSide Then
LabLoadTitle.Text = "创建联机房间失败"
Else
LabLoadTitle.Text = "加入联机房间失败"
End If
Dim RealException As Exception = If(Loader.Error.InnerException, Loader.Error)
If TypeOf RealException Is CertOutdatedException Then
LabCertTitle.Text = "索引码无效"
LabCertDesc.Text = "你的 HiPer 索引码无效或者已经过期!" & vbCrLf & "请重新输入索引码。"
HiperExit(True)
ElseIf RealException.Message.StartsWith("$") Then
LabLoadDesc.Text = RealException.Message.TrimStart("$") & vbCrLf &
"点击镐子重试,或者点击灰色的 × 取消。"
Else
LabLoadDesc.Text = LoadStep & "失败:" & GetExceptionSummary(RealException) & vbCrLf &
"点击镐子重试,或者点击灰色的 × 取消。"
End If
Log(Loader.Error, "HiPer 联机尝试失败")
Case LoadState.Finished
UpdateProgress(1)
CurrentSubpage = Subpages.PanFinish
BtnFinishPing.Visibility = If(IsServerSide, Visibility.Collapsed, Visibility.Visible)
LineFinishPing.Visibility = If(IsServerSide, Visibility.Collapsed, Visibility.Visible)
BtnFinishCopy.Visibility = If(IsServerSide, Visibility.Visible, Visibility.Collapsed)
If IsServerSide Then
LabFinishTitle.Text = "已创建联机房间"
LabFinishDesc.Text = "已在端口 " & HostPort & " 创建了联机房间。" & vbCrLf & "点击下方的复制联机码按钮,然后把联机码发给朋友吧!"
Else
LabFinishTitle.Text = "已加入联机房间"
LabFinishDesc.Text = "启动游戏,进入多人游戏页面后稍等片刻,房间就会出现在服务器列表的最下方。" & vbCrLf & "如果提示无效会话,不要退出联机房间,重新启动游戏即可!"
End If
LabFinishIp.Text = HostIp & ":" & HostPort
Log("[Hiper] 已完成连接")
End Select
End Sub End Sub
Private Shared LoadStep As String = "准备初始化" Private Shared LoadStep As String = "准备初始化"
Private Shared Sub SetLoadDesc(Intro As String, [Step] As String) Private Shared Sub SetLoadDesc(Intro As String, [Step] As String)
@@ -888,10 +333,6 @@ FinishMcbFileCheck:
'复制联机码 '复制联机码
Private Sub BtnFinishCopy_Click(sender As Object, e As EventArgs) Handles BtnFinishCopy.Click Private Sub BtnFinishCopy_Click(sender As Object, e As EventArgs) Handles BtnFinishCopy.Click
Dim IpParts = HostIp.Split(".").Select(Function(l) Val(l)).ToList
Dim IpAndPort As Long = HostPort + IpParts(0) * 65536 + IpParts(1) * 65536 * 256 + IpParts(2) * 65536 * 256 * 256 + IpParts(3) * 65536 * 256 * 256 * 256
ClipboardSet("P" & RadixConvert(IpAndPort, 10, 36) & RequestVersion, False)
Hint("已复制联机码!", HintType.Finish)
End Sub End Sub
'Ping 房主 'Ping 房主
@@ -1,204 +1,10 @@
Imports System.Net Imports System.Net.Sockets
Imports System.Net.Sockets
Public Class PageLinkIoi Public Class PageLinkIoi
Public Const RequestVersion As Integer = 4 Public Const RequestVersion As Integer = 4
Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion
Public Shared PathIoi As String = PathAppdata & "联机模块\IOI 联机模块.exe" Public Shared PathIoi As String = PathAppdata & "联机模块\IOI 联机模块.exe"
#Region "初始化"
'页面初始化
Private IsLoad As Boolean = False
Private Sub MeLoaded() Handles Me.Loaded
FormMain.EndProgramForce(Result.Aborted)
'重复加载部分
PanBack.ScrollToHome()
RefreshUi()
'非重复加载部分
If IsLoad Then Exit Sub
IsLoad = True
'更新线程
RunInNewThread(Sub()
Do While True
Thread.Sleep(200)
If FrmMain.PageCurrent.Page = FormMain.PageType.Link AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.LinkIoi Then RunInUiWait(Sub() FrmLinkIoi.RefreshUi())
RefreshWorker()
Loop
End Sub, "Link Timer")
End Sub
'加载器初始化与左边栏处理
Private Sub LoaderInit() Handles Me.Initialized
PageLoaderInit(Load, PanLoad, PanBack, PanAlways, InitLoader, Sub()
End Sub)
End Sub
'初始化加载器与步骤
Public Shared WithEvents InitLoader As New LoaderCombo(Of Integer)("联机模块初始化", {
New LoaderTask(Of Integer, Integer)("端口检查", AddressOf InitPortCheck) With {.ProgressWeight = 1},
New LoaderTask(Of Integer, Integer)("启动请求核心", AddressOf StartSocketListener) With {.ProgressWeight = 1},
New LoaderTask(Of Integer, List(Of NetFile))("初次启动尝试", AddressOf InitFirst) With {.ProgressWeight = 4},
New LoaderDownload("下载更新文件", New List(Of NetFile)) With {.ProgressWeight = 6},
New LoaderTask(Of List(Of NetFile), Boolean)("二次启动尝试", AddressOf InitSecond) With {.ProgressWeight = 3},
New LoaderTask(Of Boolean, Boolean)("创建请求核心房间", AddressOf InitRequest) With {.ProgressWeight = 2}
})
'判断端口是否被占用
Private Shared Sub InitPortCheck()
'检查协议
If Not Setup.Get("LinkEula") Then
Reopen:
Select Case MyMsgBox("PCL 的联机服务由速聚授权提供。" & vbCrLf & "在使用前,你需要同意速聚的用户服务协议和隐私政策。", "协议授权", "同意", "拒绝", "查看用户服务协议和隐私政策")
Case 1
Setup.Set("LinkEula", True)
Case 2
Throw New Exception("你拒绝了用户服务协议……")
Case 3
OpenWebsite("https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxMTMyODk3Mg==&action=getalbum&album_id=2585385685407514625&scene=173&from_msgid=2247483720&from_itemidx=1&count=3&nolastread=1#wechat_redirect")
GoTo Reopen
End Select
End If
'先掐死
IoiStop(True)
'检查端口
Dim HasConflict As Boolean = False
For Each Port In Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
If Port.Port <> 55555 AndAlso Port.Port <> 55557 Then Continue For
Log("[IOI] 发现端口 " & Port.Port & " 被占用")
HasConflict = True
Next
If Not HasConflict Then Exit Sub
'对应的报错
For Each Line As String In ShellAndGetOutput("netstat", "-ano", 30000).Split({vbCrLf}, StringSplitOptions.RemoveEmptyEntries)
If Not (Line.Contains("127.0.0.1:55555") OrElse Line.Contains("127.0.0.1:55557")) Then Continue For
Dim ProcessName As String
Try
ProcessName = Process.GetProcessById(Line.Split(" ").Last).ProcessName
Catch ex As Exception
Log(ex, "获取占用端口的进程信息失败,假定进程已结束")
Continue For
End Try
If ProcessName = "联机模块" Then
Throw New Exception("由于一个已知问题,请在重启电脑后再尝试使用联机功能")
ElseIf ProcessName = "Idle" Then
'不知道为啥反正又没占用了
Log("[IOI] 未发现占用此端口的程序,继续执行")
Else
Throw New Exception("端口被程序 " & ProcessName & " 占用,无法启动联机模块,请在任务管理器关闭此程序后再试")
End If
Next
End Sub
'下载更新前尝试启动
Private Shared Sub InitFirst(Task As LoaderTask(Of Integer, List(Of NetFile)))
'初次启动尝试
If IoiVersion <> Setup.Get("LinkIoiVersion") Then
Log("[IOI] 设置版本强制要求联机模块更新")
Setup.Set("LinkIoiVersion", IoiVersion)
ElseIf File.Exists(PathIoi) Then
Task.Progress = 0.2
CheckFirewall()
If IoiStart() Then
'已完成启动与初始化
Task.Output = New List(Of NetFile)
Exit Sub
Else
'需要更新
IoiStop(True)
File.Delete(PathIoi)
GoTo StartDownload
End If
'如果抛出异常则直接使加载器失败
End If
'开始下载
StartDownload:
Task.Progress = 0.8
Log("[IOI] 需要下载联机模块")
If File.Exists(PathTemp & "联机模块.zip") Then File.Delete(PathTemp & "联机模块.zip")
Task.Output = New List(Of NetFile) From {New NetFile(
{"https://gitcode.net/to/cato_bin/-/raw/master/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
"http://mirror.hiper.cn.s2.the.bb/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
"http://mirror.hiper.cn.s3.the.bb:175/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip",
"https://pcl2-server-1253424809.file.myqcloud.com/link/ioi_v2_x" & If(Is32BitSystem, 32, 64) & ".zip{CDN}"},
PathTemp & "联机模块.zip")}
End Sub
'下载更新后尝试启动
Private Shared Sub InitSecond(Task As LoaderTask(Of List(Of NetFile), Boolean))
'若首次尝试已经成功,则直接跳过
If IoiState = LoadState.Finished Then Exit Sub
'解压更新包
Log("[IOI] 解压联机模块以完成下载")
If File.Exists(PathTemp & "ioi.exe") Then File.Delete(PathTemp & "ioi.exe")
If File.Exists(PathIoi) Then File.Delete(PathIoi)
ExtractFile(PathTemp & "联机模块.zip", PathTemp)
File.Delete(PathTemp & "联机模块.zip")
CopyFile(PathTemp & "ioi.exe", PathIoi)
File.Delete(PathTemp & "ioi.exe")
Task.Progress = 0.4
'再次尝试启动
CheckFirewall()
If IoiStart() Then Exit Sub
IoiStop(True)
Throw New Exception("联机模块初始化失败")
End Sub
'在 IOI 启动后建立 55557 房间
Private Shared Sub InitRequest()
Try
Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=55557" & "&password=" & IoiPassword, "PUT", "", "", 100000))
If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
Catch ex As InvalidOperationException
'API 返回的错误
Log("创建请求核心房间失败:" & ex.Message, LogLevel.Msgbox)
Catch ex As Exception
'常规错误
Log(ex, "创建请求核心房间失败", LogLevel.Msgbox)
End Try
End Sub
'检查防火墙权限,并添加对应的权限
Private Shared Sub CheckFirewall()
Try
Log("[IOI] Windows 防火墙:检测开始")
If Not PageLinkLeft.FirewallIsBlock(PathIoi) Then Exit Try
If PageLinkLeft.FirewallPolicy.CurrentProfile.ExceptionsNotAllowed Then
'禁止白名单
MyMsgBox("由于 Windows 防火墙阻止了所有传入连接,PCL 无法获取防火墙通行权限。" & vbCrLf &
"联机会有很大概率失败,就算连上了,延迟也会变高……" & vbCrLf & vbCrLf &
"请先关闭 Windows 防火墙中的 " & vbLQ & "阻止所有传入连接" & vbRQ & " 选项,然后重启 PCL。", "遭到防火墙拦截")
ElseIf IsAdmin() Then
'有管理员权限
Log("[IOI] Windows 防火墙:尝试添加防火墙通行权限")
Try
PageLinkLeft.FirewallAddAuthorized("Plain Craft Launcher 启动器(IOI 联机模块)", PathIoi)
Catch ex As Exception
Log(ex, "无法将联机模块添加至防火墙白名单,可能导致联机失败", LogLevel.Msgbox)
End Try
Else
'无管理员权限
If MyMsgBox("由于你开启了 Windows 防火墙,PCL 需要获取防火墙通行权限。" & vbCrLf & vbCrLf &
"若继续,PCL 将尝试以管理员权限重新启动。" & vbCrLf &
"若拒绝,联机模块可能会被防火墙拦截,联机会有很大概率失败。", "需要管理员权限", "继续", "拒绝") = 1 Then
Log("[IOI] Windows 防火墙:尝试提升权限")
If RunAsAdmin("--link ioi") Then
FrmMain.EndProgram(False) '已重新运行
Else
Hint("获取管理员权限失败,请尝试右键 PCL,选择 " & vbLQ & "以管理员身份运行" & vbRQ & ",然后再进入联机页面!", HintType.Critical)
End If
Else
Hint("在没有防火墙权限的情况下尝试联机,很可能会导致联机失败!", HintType.Critical)
End If
End If
Log("[IOI] Windows 防火墙:无防火墙通行权限")
Catch ex As Exception
Log(ex, "无法检测防火墙状态,可能导致联机失败", LogLevel.Msgbox)
End Try
End Sub
#End Region
#Region "进程管理" #Region "进程管理"
Private Shared IoiId As String, IoiPassword As String Private Shared IoiId As String, IoiPassword As String
@@ -209,38 +15,7 @@ StartDownload:
''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。 ''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。
''' </summary> ''' </summary>
Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean
IoiStop = False Return False
'发送断开信息
Try
If IoiProcess IsNot Nothing AndAlso Not IoiProcess.HasExited Then
For i = 0 To UserList.Count - 1
If i > UserList.Count - 1 Then Exit For
Dim User = UserList.Values(i)
SendDisconnectRequest(User)
Next
End If
Catch ex As Exception
Log(ex, "结束 IOI 进程时发送 Disconnect 信息失败")
End Try
'关闭所有 Ioi 进程
For Each ProcessObject In Process.GetProcesses
If ProcessObject.ProcessName <> "IOI 联机模块" Then Continue For
IoiStop = True
Try
ProcessObject.Kill()
Log("[IOI] 已关闭联机模块:" & ProcessObject.Id)
If SleepWhenKilled Then Thread.Sleep(3000) '等待 3 秒确认进程已退出
Catch ex As Exception
Log(ex, "关闭联机模块失败(" & ProcessObject.Id & ")")
End Try
Next
'初始化
IoiProcess = Nothing
IoiId = Nothing
IoiPassword = Nothing
IoiState = LoadState.Waiting
UserList = New Dictionary(Of String, LinkUserIoi)
RoomListForMe = New List(Of RoomEntry)
End Function End Function
''' <summary> ''' <summary>
''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。 ''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。
@@ -248,130 +23,11 @@ StartDownload:
''' 若 Ioi 正在运行,则会先停止其运行。 ''' 若 Ioi 正在运行,则会先停止其运行。
''' </summary> ''' </summary>
Public Shared Function IoiStart() As Boolean Public Shared Function IoiStart() As Boolean
IoiStop(True) Return False
Log("[IOI] 启动联机模块进程")
Dim Info = New ProcessStartInfo With {
.FileName = PathIoi,
.UseShellExecute = False,
.CreateNoWindow = True,
.RedirectStandardError = True,
.RedirectStandardOutput = True,
.WorkingDirectory = PathTemp
}
IoiProcess = New Process() With {.StartInfo = Info}
Using outputWaitHandle As New AutoResetEvent(False)
Using errorWaitHandle As New AutoResetEvent(False)
AddHandler IoiProcess.OutputDataReceived, Function(sender, e)
Try
If e.Data Is Nothing Then
outputWaitHandle.[Set]()
Else
IoiLogLine(e.Data)
End If
Catch ex As ObjectDisposedException
Catch ex As Exception
Log(ex, "读取联机模块信息失败")
IoiState = LoadState.Failed
End Try
Return Nothing
End Function
AddHandler IoiProcess.ErrorDataReceived, Function(sender, e)
Try
If e.Data Is Nothing Then
errorWaitHandle.[Set]()
Else
IoiLogLine(e.Data)
End If
Catch ex As ObjectDisposedException
Catch ex As Exception
Log(ex, "读取联机模块错误信息失败")
IoiState = LoadState.Failed
End Try
Return Nothing
End Function
IoiProcess.Start()
IoiState = LoadState.Loading
IoiProcess.BeginOutputReadLine()
IoiProcess.BeginErrorReadLine()
'等待
Do Until IoiProcess.HasExited OrElse IoiState <> LoadState.Loading
Thread.Sleep(10)
Loop
'输出
If IoiState = LoadState.Finished Then
Log("[IOI] 联机模块启动成功")
Return True
Else
Throw New Exception("联机模块启动失败")
End If
'Select Case IoiState
' Case LoadState.Finished
' Log("[IOI] 联机模块启动成功")
' Return True
' Case LoadState.Aborted
' Log("[IOI] 联机模块要求更新")
' Return False
' Case LoadState.Failed
' Log("[IOI] 联机模块启动出现异常")
' Return False
' Case Else 'LoadState.Loading
' Throw New Exception("联机模块启动失败,请检查你的网络连接")
'End Select
End Using
End Using
End Function End Function
'Ioi 日志 'Ioi 日志
Private Shared Sub IoiLogLine(Content As String) Private Shared Sub IoiLogLine(Content As String)
'初始化
If Content.Contains(" > http://127.0.0.1:55555/") Then Exit Sub
If Content.Contains("All done") Then
If IoiId IsNot Nothing AndAlso IoiPassword IsNot Nothing Then
IoiState = LoadState.Finished
Else
Log("[IOI] 联机模块汇报初始化完成,但未提供账户信息")
IoiState = LoadState.Failed
End If
Exit Sub
End If
If Content.Contains("Password :: ") Then
IoiPassword = Content.Split({"Password :: "}, StringSplitOptions.None)(1)
Exit Sub '为保证安全不记录密码到 Log
End If
'初始化
If Content.Contains("ID :: ") Then IoiId = Content.Split({"ID :: "}, StringSplitOptions.None)(1)
If Content.Contains("Initialization failed") OrElse Content.Contains("The version is ") Then IoiState = LoadState.Aborted
'系统回应
If Content.Contains("'portssub' from ") Then LastPortsId = Content.Split(" ").Last
If Content.Contains("Listening tcp ") Then
If UserList.ContainsKey(LastPortsId) Then
UserList(LastPortsId).Ports(Content.Split(" ").Last) = RegexSeek(Content, "(?<=Listening tcp )[^:]+")
Else
Log("[IOI] 未在列表中的用户出现意料外的连接信息")
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & LastPortsId & "&password=" & IoiPassword, "DELETE", "", "", 500)
End If
End If
'断开连接
If Content.Contains("Closed tcp connection from ") AndAlso Content.Contains(":55557") Then
For i = 0 To UserList.Count - 1
Dim User = UserList.Values(i)
If Not Content.Contains(User.Ports(55557)) OrElse User.IsDisposed Then Continue For
Log("[IOI] 检测到 55557 端口断开(" & User.DisplayName & ")")
UserRemove(User, True)
Exit For
Next
End If
'写入日志
If Not Content.Contains("read /dev/stdin: The handle is invalid.") AndAlso LogLinesCount < If(ModeDebug, 10000, 1000) Then
LogLinesCount += 1
Log("[IOI] " & Content)
End If
End Sub End Sub
Private Shared LogLinesCount As Integer = 0 Private Shared LogLinesCount As Integer = 0
Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口 Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口
@@ -384,94 +40,10 @@ StartDownload:
Private Shared UserListIdentifyCache As String = "" Private Shared UserListIdentifyCache As String = ""
Private Shared RoomListIdentifyCache As String = "" Private Shared RoomListIdentifyCache As String = ""
Public Sub RefreshUi() Public Sub RefreshUi()
Try
'确认用户列表缓存
Dim UserListIdentify As String = Join(UserList, vbCrLf)
If UserListIdentify <> UserListIdentifyCache Then
UserListIdentifyCache = UserListIdentify
'刷新列表项
PanUserList.Children.Clear()
For i = 0 To UserList.Count - 1
If i > UserList.Count - 1 Then Exit For
PanUserList.Children.Add(UserList.Values(i).ToListItem)
Next
'刷新列表标题
CardUser.Title = "已连接的玩家 (" & PanUserList.Children.Count & ")"
End If
'刷新用户列表项
For Each UserItem As MyListItem In PanUserList.Children
CType(UserItem.Tag, LinkUserIoi).RefreshUi(UserItem)
Next
'确认房间列表缓存
Dim RoomListIdentify As String = Join(GetRoomList(), vbCrLf)
If RoomListIdentify <> RoomListIdentifyCache Then
RoomListIdentifyCache = RoomListIdentify
'刷新列表项
PanRoom.Children.Clear()
For Each Room In GetRoomList()
PanRoom.Children.Add(Room.ToListItem)
Next
End If
'刷新房间列表项
For Each RoomItem As MyListItem In PanRoom.Children
CType(RoomItem.Tag, RoomEntry).RefreshUi(RoomItem)
Next
'刷新操作提示
If UserList.Count = 0 Then
If RoomListForMe.Count = 0 Then
LabHint.Text = "若想创建房间,请点击创建房间按钮,并按说明进行操作。" & vbCrLf & "若想加入他人的房间,请点击建立连接,然后输入对方的联机码。"
Else
LabHint.Text = "若想让其他人加入你的房间,请点击复制联机码,然后让你的朋友输入你的联机码以建立连接。"
End If
Else
If GetRoomList.Count = 0 Then
LabHint.Text = "若想创建房间,请点击创建房间按钮,然后输入对局域网开放后 MC 显示的端口号,或本地服务端的端口号。"
ElseIf RoomListForMe.Count = 0 Then
'已有连接,本人没有房间,对方有房间
LabHint.Text = "若想加入某个房间,直接点击该房间即可获取说明。"
Else
'已有连接,且本人有房间
LabHint.Text = "指向你所创建的房间,能在右侧找到修改房间名称、关闭房间等选项。" &
If(RoomListForMe.Count <> GetRoomList.Count, vbCrLf & "若想加入其他人的房间,直接点击该房间即可获取说明。", "")
End If
End If
Catch ex As Exception
Log(ex, "联机模块 UI 时钟运行失败", LogLevel.Feedback)
End Try
End Sub End Sub
'工作线程刷新 '工作线程刷新
Public Sub RefreshWorker() Public Sub RefreshWorker()
Try
'检测退出
If InitLoader.State = LoadState.Finished AndAlso IoiProcess.HasExited Then
Log("[IOI] 联机模块出现异常!", LogLevel.Hint)
ModuleStopManually()
End If
'检测用户
For i = 0 To UserList.Values.Count - 1
If i > UserList.Values.Count - 1 Then Exit For
Dim User = UserList.Values(i)
If User.Progress < 1 Then Continue For '跳过连接中的用户
'发送心跳包
If Date.Now - User.LastSend > New TimeSpan(0, 0, RandomInteger(50, 30)) Then
RunInNewThread(Sub()
Try
SendUpdateRequest(User, 1)
Catch ex As Exception
Log(ex, "心跳包发送失败(" & User.DisplayName & ")", LogLevel.Normal)
End Try
End Sub, "Link Heartbeat " & User.Id)
End If
'检测被动离线
If Date.Now - User.LastReceive > New TimeSpan(0, 2, 0) Then
SendDisconnectRequest(User)
Hint("与 " & User.DisplayName & " 的连接已中断!", HintType.Critical)
End If
Next
Catch ex As Exception
Log(ex, "联机模块工作时钟运行失败", LogLevel.Feedback)
End Try
End Sub End Sub
#End Region #End Region
@@ -482,65 +54,6 @@ StartDownload:
''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。 ''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。
''' </summary> ''' </summary>
Private Shared Sub SendPortsubRequest(User As LinkUserIoi) Private Shared Sub SendPortsubRequest(User As LinkUserIoi)
Log("[IOI] 尝试建立连接:" & User.Id)
'向对方发送 portsub 请求
Dim Retry As Integer = 0
RetryLink:
Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "PUT", "", "", 100000))
'失败与重试
If Result("msg") IsNot Nothing Then
Dim ErrorMessage As String = Result("msg").ToString
Select Case ErrorMessage
Case "failed to find any peer in table"
Retry += 1
ErrorMessage = "我方网络环境不佳,连接失败。" '初始化对等机列表超时
Log("[IOI] 尝试建立连接结果:未找到对等机,第 " & Retry & " 级重试")
Case "routing: not found"
Retry += 4
ErrorMessage = "我方或对方网络环境不佳,或对方已关闭联机模块,未找到路由。" '未知情况
Log("[IOI] 尝试建立连接结果:无法连接到路由,第 " & Retry & " 级重试")
Case "you are already connected to specified host"
If User.IsDisposed Then Throw New ThreadInterruptedException("用户对象已被释放")
Log("[IOI] 尝试建立连接结果:已与对方连接")
GoTo Done
'Retry += 1
'NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 100000)
'ErrorMessage = "已与对方连接。" '双向连接偶尔导致
'Log("[IOI] 已与对方连接,尝试中断现有连接,第 " & Retry & " 级重试")
Case "dial backoff"
Retry += 20
ErrorMessage = "对方网络环境不佳,或对方已关闭联机模块。请尝试让对方主动连接你,而不是你去连接对方。" 'NAT2 连 NAT4 导致
Log("[IOI] 尝试建立连接结果:NAT 异常,第 " & Retry & " 级重试")
Case Else
If ErrorMessage.Contains("all dials failed") Then
Retry += 8
ErrorMessage = "我方或对方网络环境不佳,或对方已关闭联机模块,连接失败。" '网络不佳,或对方已关闭模块
Log("[IOI] 尝试建立连接结果:连接失败,第 " & Retry & " 级重试")
Else
Retry += 8
Log("[IOI] 尝试建立连接结果:未知错误(" & ErrorMessage & "),第 " & Retry & " 级重试")
End If
End Select
If User.IsDisposed Then Throw New ThreadInterruptedException("用户对象已被释放")
If Retry <= 64 Then
User.Progress = Retry * 0.01 + 0.05
Thread.Sleep(3000)
GoTo RetryLink
Else
Throw New InvalidOperationException(ErrorMessage)
End If
End If
Log("[IOI] 尝试建立连接结果:成功")
'等待获取对方端口
Done:
Dim WaitCount As Integer = 0
Do Until User.Ports.ContainsKey(55555) AndAlso User.Ports.ContainsKey(55557)
WaitCount += 1
User.Progress = 0.7 + WaitCount / 200 * 0.1 '70% ~ 80%
If WaitCount = 100 Then Throw New Exception("连接超时,请尝试重新连接(未收到端口回报)!")
Thread.Sleep(150)
Loop
User.Progress = 0.8
End Sub End Sub
''' <summary> ''' <summary>
@@ -584,20 +97,6 @@ Done:
''' 尝试发送断开请求,并将其从用户列表中移除。 ''' 尝试发送断开请求,并将其从用户列表中移除。
''' </summary> ''' </summary>
Private Shared Sub SendDisconnectRequest(User As LinkUserIoi, Optional Message As String = Nothing, Optional IsError As Boolean = False) Private Shared Sub SendDisconnectRequest(User As LinkUserIoi, Optional Message As String = Nothing, Optional IsError As Boolean = False)
Dim RawJson As New JObject()
RawJson("id") = IoiId
RawJson("type") = "disconnect"
If Message IsNot Nothing Then
RawJson("message") = Message
RawJson("isError") = IsError
End If
Try
User.Send(RawJson)
Thread.Sleep(50)
Catch
End Try
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
User.Dispose()
End Sub End Sub
#End Region #End Region
@@ -606,29 +105,9 @@ Done:
'刷新连接 '刷新连接
Private Shared Sub BtnListRefresh_Click(sender As MyIconButton, e As EventArgs) Private Shared Sub BtnListRefresh_Click(sender As MyIconButton, e As EventArgs)
Dim User As LinkUserIoi = sender.Tag
User.PingRecord.Clear()
FrmLinkIoi.RefreshUi()
RunInThread(Sub()
Try
SendUpdateRequest(User, 1)
Catch ex As Exception
If InitLoader.State = LoadState.Finished Then Log(ex, "刷新与 " & User.DisplayName & " 的连接失败", LogLevel.Hint)
End Try
End Sub)
End Sub End Sub
'断开连接 '断开连接
Private Shared Sub BtnListDisconnect_Click(sender As MyIconButton, e As EventArgs) Private Shared Sub BtnListDisconnect_Click(sender As MyIconButton, e As EventArgs)
Dim User As LinkUserIoi = sender.Tag
sender.IsEnabled = False
RunInThread(Sub()
If User.Progress < 1 AndAlso User.RelativeThread IsNot Nothing AndAlso User.RelativeThread.IsAlive Then
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
User.Dispose()
Else
SendDisconnectRequest(User, GetPlayerName() & " 主动断开了连接!")
End If
End Sub)
End Sub End Sub
'复制联机码 '复制联机码
Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click
@@ -680,46 +159,10 @@ Done:
#Region "请求核心" #Region "请求核心"
Private Shared Listener As Socket = Nothing
''' <summary> ''' <summary>
''' 启动 Socket 监听核心。 ''' 启动 Socket 监听核心。
''' </summary> ''' </summary>
Public Shared Sub StartSocketListener() Public Shared Sub StartSocketListener()
If Listener IsNot Nothing Then Exit Sub '已经启动过了
Listener = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) With {.ReceiveTimeout = 1000000000, .SendTimeout = 1000000000}
Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, True)
Try
Listener.Bind(New IPEndPoint(IPAddress.Any, 55557))
Catch ex As Exception
Log(ex, "初次启动 Socket 监听核心失败,开始重试")
Thread.Sleep(1000)
Listener.Bind(New IPEndPoint(IPAddress.Any, 55557))
End Try
Listener.Listen(100)
Log("[IOI] 已启动 Socket 监听核心")
RunInNewThread(Sub()
While True
Try
'获取新的 Socket
Dim bytes(1024) As Byte
Dim ClientSocket As Socket = Listener.Accept()
'获取初始输入信息
Dim RequestInput As String = Encoding.UTF8.GetString(bytes, 0, ClientSocket.Receive(bytes))
If Not RequestInput.StartsWith("PCL - ") Then Exit Sub
Dim RawData As String
Try
RawData = SecretDecrypt(WebUtility.UrlDecode(RequestInput.Substring(6)))
Log("[IOI] 接收到新的 Socket:" & RawData)
PageLinkIoi.ReceiveJson(GetJson(RawData), ClientSocket)
Catch ex As Exception
Log(ex, "新的 Socket 处理出错(内容:" & RequestInput & ")")
End Try
Catch ex As Exception
Log(ex, "Socket 监听出错")
Thread.Sleep(3000) '防止死循环出错
End Try
End While
End Sub, "Socket Listener")
End Sub End Sub
#End Region #End Region
@@ -738,40 +181,9 @@ Done:
'请求管理 '请求管理
Public Socket As Socket = Nothing Public Socket As Socket = Nothing
Public Sub Send(Request As JObject) Public Sub Send(Request As JObject)
If Socket Is Nothing Then Throw New Exception("该用户尚未绑定 Socket")
Dim Data As String = Request.ToString(Newtonsoft.Json.Formatting.None)
If ModeDebug Then Log("[IOI] 发送联机数据包(" & DisplayName & "):" & Data)
Data = "PCL - " & WebUtility.UrlEncode(SecretEncrypt(Data))
Me.LastSend = Date.Now
Socket.Send(Encoding.UTF8.GetBytes(Data))
End Sub End Sub
Public ListenerThread As Thread = Nothing Public ListenerThread As Thread = Nothing
Public Sub StartListener() Public Sub StartListener()
If ListenerThread IsNot Nothing Then Exit Sub
'启动监听
ListenerThread = RunInNewThread(Sub()
Try
Dim bytes(1024) As Byte
While PageLinkIoi.UserList.ContainsValue(Me)
Dim RequestInput As String = Encoding.UTF8.GetString(bytes, 0, Socket.Receive(bytes))
If Not RequestInput.StartsWith("PCL - ") Then Exit Sub
Dim RawData As String = SecretDecrypt(WebUtility.UrlDecode(RequestInput.Substring(6)))
If ModeDebug Then Log("[IOI] 接收联机数据包(" & DisplayName & "):" & RawData)
PageLinkIoi.ReceiveJson(GetJson(RawData)) '调用去实际的联机模块
End While
Catch ex As ThreadInterruptedException
Log("[IOI] 用户监听已中断(" & DisplayName & ")")
Catch ex As Exception
If IsDisposed Then Exit Sub
If ex.GetType.Equals(GetType(SocketException)) AndAlso CType(ex, SocketException).SocketErrorCode = 10053 Then
Log("[IOI] 客户端已关闭(" & DisplayName & ")")
PageLinkIoi.UserRemove(CType(Me, PageLinkIoi.LinkUserIoi), True)
Exit Sub
End If
Log(ex, "用户监听出错(" & DisplayName & ")")
PageLinkIoi.UserRemove(CType(Me, PageLinkIoi.LinkUserIoi), True)
End Try
End Sub, "Link Listener " & DisplayName)
End Sub End Sub
Public Sub BindSocket(Socket As Socket) Public Sub BindSocket(Socket As Socket)
If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket") If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket")
@@ -975,135 +387,12 @@ Done:
'正向与反向连接 '正向与反向连接
Public Shared Sub BtnLeftCreate_Click() Handles BtnLeftCreate.Click Public Shared Sub BtnLeftCreate_Click() Handles BtnLeftCreate.Click
''获取信息
'Dim Code As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {New ValidateLength(9, 99999)}, "", "输入对方的联机码", "确定", "取消")
'If Code Is Nothing Then Exit Sub
''检查
'If Code.StartsWith("P") AndAlso Code.Length < 48 Then
' Hint("你输入的可能是 HiPer 的联机码,请在左侧的联机方式中选择 HiPer!", HintType.Critical) : Exit Sub
'End If
'Dim Id As String, DisplayName As String
'Try '解密失败检查
' Id = "12D3" & Code.Substring(0, 48)
' DisplayName = SecretDecrypt(Code.Substring(48))
'Catch
' Hint("你输入的联机码有误!", HintType.Critical)
' Exit Sub
'End Try
'If Id = IoiId Then '自我连接检查
' Hint("我连我自己?搁这卡 Bug 呢?", HintType.Critical)
' Exit Sub
'End If
''开始
'Dim User As New LinkUserIoi(Id, DisplayName)
'User.RelativeThread = RunInNewThread(Sub()
' Dim WaitCount As Integer = 0
' Try
' '加入列表
' If UserList.ContainsKey(Id) Then
' Hint(UserList(Id).DisplayName & " 已在列表中,无需再次连接!")
' Exit Sub
' Else
' UserList.Add(Id, User)
' End If
' '发送 portsub 请求(0% -> 80%)
' SendPortsubRequest(User)
' '构建 Socket(81% -> 82%)
' Dim ClientSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) With {.ReceiveTimeout = 1000000000, .SendTimeout = 1000000000}
' ClientSocket.Connect(New IPEndPoint(IPAddress.Parse(User.Ports(55557)), 55557))
' User.BindSocket(ClientSocket)
' User.Progress = 0.82
' '发送 connect 请求(83% -> 85%)
' SendConnectRequest(User)
' User.Progress = 0.85
' '等待对方向自己请求
' Log("[IOI] 加入成功,等待反向请求")
' Do While User.Progress < 0.9999
' User.Progress += 0.0002
' If User.Progress > 0.98 AndAlso User.Progress < 0.9999 Then Throw New Exception("对方未回应连接请求!")
' Thread.Sleep(100)
' Loop
' Hint("已连接到 " & User.DisplayName & "!", HintType.Finish)
' Catch ex As ThreadInterruptedException
' Log("[IOI] 已中断主动发起的连接(" & User.DisplayName & ")")
' Catch ex As InvalidOperationException
' 'API 返回的错误
' NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
' User.Dispose()
' If InitLoader.State = LoadState.Finished Then Log("与 " & DisplayName & " 建立连接失败:" & ex.Message, LogLevel.Msgbox)
' Catch ex As Exception
' '常规错误
' NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
' User.Dispose()
' If ex.InnerException IsNot Nothing AndAlso TypeOf ex.InnerException Is ThreadInterruptedException Then Exit Sub
' If InitLoader.State = LoadState.Finished Then Log(ex, "与 " & DisplayName & " 建立连接失败", LogLevel.Msgbox)
' End Try
' End Sub, "Link Create " & DisplayName)
End Sub End Sub
Private Shared Sub SendPortsubBack(User As LinkUserIoi, TargetVersion As Integer) Private Shared Sub SendPortsubBack(User As LinkUserIoi, TargetVersion As Integer)
Try
SendPortsubRequest(User)
User.Progress = 0.9
If TargetVersion > RequestVersion Then
SendDisconnectRequest(User, "无法连接到 " & GetPlayerName() & ":对方的 PCL 版本过低!", True)
Throw New InvalidOperationException("你的 PCL 版本过低!")
ElseIf TargetVersion < RequestVersion Then
SendDisconnectRequest(User, "无法连接到 " & GetPlayerName() & ":你的 PCL 版本过低!", True)
Throw New InvalidOperationException("对方的 PCL 版本过低!")
Else
SendUpdateRequest(User, 1)
User.Progress = 1
Hint(User.DisplayName & " 已与你建立连接!")
End If
Catch ex As ThreadInterruptedException
Log("[IOI] 已中断被动建立的连接(" & User.DisplayName & ")")
Catch ex As InvalidOperationException
'API 返回的错误
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
User.Dispose()
If InitLoader.State = LoadState.Finished Then Log("与 " & User.DisplayName & " 建立连接失败:" & ex.Message, LogLevel.Hint)
Catch ex As Exception
'常规错误
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
User.Dispose()
If InitLoader.State = LoadState.Finished Then Log(ex, "与 " & User.DisplayName & " 建立连接失败", LogLevel.Hint)
End Try
End Sub End Sub
'创建房间 '创建房间
Private Sub LinkCreate() Handles BtnCreate.Click Private Sub LinkCreate() Handles BtnCreate.Click
'If MyMsgBox("请先进入 MC 并暂停游戏,在暂停页面选择对局域网开放,然后在下一个窗口输入 MC 显示的端口号。" & vbCrLf & "若使用服务端开服,则直接在下一个窗口输入服务器配置中的端口号即可。", "提示", "继续", "取消") = 2 Then Exit Sub
''获取端口号
'Dim Port As String = MyMsgBoxInput("", New ObjectModel.Collection(Of Validate) From {
' New ValidateInteger(0, 65535),
' New ValidateExceptSame({"55555", "55557"}, "端口不能为 %!"),
' New ValidateExceptSame(RoomListForMe.Select(AddressOf RoomEntry.SelectPort), "端口 % 已创建过房间,请在删除该房间后继续!")
' }, "", "输入端口号", "确定", "取消")
'If Port Is Nothing Then Exit Sub
''获取显示名称
'Dim DisplayName As String = MyMsgBoxInput(GetPlayerName() & " 的房间 - " & Port, New ObjectModel.Collection(Of Validate) From {
' New ValidateNullOrWhiteSpace(), New ValidateLength(1, 40), New ValidateFilter()
' }, "", "输入房间名(建议包含游戏版本等信息)", "确定", "取消")
'If DisplayName Is Nothing Then Exit Sub
'DisplayName = DisplayName.Trim
''开始
'RunInThread(Sub()
' Try
' '请求
' Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=" & Port & "&password=" & IoiPassword, "PUT", "", "", 100000))
' If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
' '成功
' RoomListForMe.Add(New RoomEntry(Port, DisplayName))
' Hint("房间 " & DisplayName & " 已创建!", HintType.Finish)
' SendUpdateRequestToAllUsers()
' Catch ex As InvalidOperationException
' 'API 返回的错误
' Log("创建房间失败:" & ex.Message, LogLevel.Msgbox)
' Catch ex As Exception
' '常规错误
' Log(ex, "创建房间失败", LogLevel.Msgbox)
' End Try
' End Sub)
End Sub End Sub
Private Shared Sub SendUpdateRequestToAllUsers() Private Shared Sub SendUpdateRequestToAllUsers()
For i = 0 To UserList.Count - 1 For i = 0 To UserList.Count - 1
@@ -1119,18 +408,6 @@ Done:
End Sub End Sub
'修改房间名称 '修改房间名称
Private Shared Sub BtnRoomEdit_Click(sender As MyIconButton, e As EventArgs) Private Shared Sub BtnRoomEdit_Click(sender As MyIconButton, e As EventArgs)
'Dim Room As RoomEntry = sender.Tag
''获取房间名
'Dim DisplayName As String = MyMsgBoxInput(Room.DisplayName, New ObjectModel.Collection(Of Validate) From {
' New ValidateNullOrWhiteSpace(),
' New ValidateLength(1, 40)
' }, "", "输入房间名(建议包含游戏版本等信息)", "确定", "取消")
'If DisplayName Is Nothing Then Exit Sub
'DisplayName = DisplayName.Trim
''修改
'Room.DisplayName = DisplayName
'FrmLinkIoi.RefreshUi()
'SendUpdateRequestToAllUsers()
End Sub End Sub
'加入房间 '加入房间
Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs) Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs)
@@ -1141,25 +418,6 @@ Done:
End Sub End Sub
'关闭房间 '关闭房间
Private Shared Sub BtnRoomClose_Click(sender As MyIconButton, e As EventArgs) Private Shared Sub BtnRoomClose_Click(sender As MyIconButton, e As EventArgs)
Dim Room As RoomEntry = sender.Tag
RunInThread(Sub()
Try
'远程移除
Dim Result As JObject = GetJson(NetRequestOnce("http://127.0.0.1:55555/api/port?proto=tcp&port=" & Room.Port & "&password=" & IoiPassword, "DELETE", "", "", 100000))
If Result("msg") IsNot Nothing Then Throw New InvalidOperationException(Result("msg").ToString)
'本地移除
RoomListForMe.Remove(Room)
'成功
RunInUi(Sub() FrmLinkIoi.RefreshUi())
SendUpdateRequestToAllUsers()
Catch ex As InvalidOperationException
'API 返回的错误
If InitLoader.State = LoadState.Finished Then Log("移除房间失败:" & ex.Message, LogLevel.Msgbox)
Catch ex As Exception
'常规错误
If InitLoader.State = LoadState.Finished Then Log(ex, "移除房间失败", LogLevel.Msgbox)
End Try
End Sub)
End Sub End Sub
'获取数据包 '获取数据包
@@ -1238,21 +496,9 @@ Done:
''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。 ''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。
''' </summary> ''' </summary>
Public Shared Sub UserRemove(User As LinkUserIoi, ShowLeaveMessage As Boolean) Public Shared Sub UserRemove(User As LinkUserIoi, ShowLeaveMessage As Boolean)
If Not UserList.ContainsKey(User.Id) Then Exit Sub
If ShowLeaveMessage Then Hint(User.DisplayName & " 已离开!")
NetRequestOnce("http://127.0.0.1:55555/api/link?id=" & User.Id & "&password=" & IoiPassword, "DELETE", "", "", 500)
User.Dispose()
End Sub End Sub
'关闭联机模块按钮
Private Shared Sub ModuleStateChanged(Loader As LoaderBase, NewState As LoadState, OldState As LoadState) Handles InitLoader.OnStateChangedUi
If FrmLinkLeft IsNot Nothing Then CType(FrmLinkLeft.ItemIoi.Buttons(0), MyIconButton).Visibility = If(NewState = LoadState.Finished, Visibility.Visible, Visibility.Collapsed)
End Sub
Public Shared Sub ModuleStopManually() Public Shared Sub ModuleStopManually()
IoiStop(False)
InitLoader.Error = New Exception("联机模块已关闭,点击以重新启动")
InitLoader.State = LoadState.Failed
IoiState = LoadState.Failed
End Sub End Sub
End Class End Class
@@ -1,6 +1,4 @@
Imports NetFwTypeLib Public Class PageLinkLeft
Public Class PageLinkLeft
Private IsLoad As Boolean = False Private IsLoad As Boolean = False
Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次 Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次
@@ -15,84 +13,6 @@ Public Class PageLinkLeft
IsPageSwitched = False IsPageSwitched = False
End Sub End Sub
#Region "Windows 防火墙"
'为联机模块添加防火墙通行权限的函数库
'不加防火墙权限你要我怎么办,这联机根本连都连不上,球球你们别看到防火墙就说是后门了,真的很恼火……
''' <summary>
''' 获取某个文件是否被 Windows 防火墙阻止。
''' </summary>
Public Shared Function FirewallIsBlock(FilePath As String) As Boolean
'获取配置
If FirewallPolicy Is Nothing Then
Try
FirewallPolicy = FirewallPolicyGet()
Catch ex As Exception
Log(ex, "Windows 防火墙:可能关闭得太死了,根本检测不到,笑死", LogLevel.Normal)
Return False
End Try
End If
'检查是否开启
If Not FirewallPolicy.CurrentProfile.FirewallEnabled Then
Log("[Link] Windows 防火墙:防火墙关闭")
Return False
End If
'检查是否允许例外
If FirewallPolicy.CurrentProfile.ExceptionsNotAllowed Then
Log("[Link] Windows 防火墙:防火墙开启,不允许例外")
Return True
End If
'检查白名单
Dim Target As String = FilePath.Replace(PathTemp.Split("\").First, "").ToLower
For Each App As INetFwAuthorizedApplication In FirewallPolicy.CurrentProfile.AuthorizedApplications
If ModeDebug Then Log("[Link] 防火墙白名单(" & App.Enabled & "):" & App.ProcessImageFileName)
If Not App.Enabled Then Continue For
'信标的奇妙盘符 E:0/xxxxx/xxx 导致误判……
Dim FileNameTrimmed As String = App.ProcessImageFileName.Replace(App.ProcessImageFileName.Split("\").First, "")
If App.ProcessImageFileName.ToLower.Contains(FilePath) Then
Log("[Link] Windows 防火墙:开启,已在白名单中(" & FilePath & ")")
Return False
End If
Next
Log("[Link] Windows 防火墙:开启,未在白名单中(" & FilePath & ")")
Return True
End Function
''' <summary>
''' 添加防火墙通行权限。需要管理员权限。
''' </summary>
Public Shared Sub FirewallAddAuthorized(DisplayName As String, FilePath As String)
Log("[Link] Windows 防火墙:添加通行权限(" & FilePath & ")")
'获取配置
If FirewallPolicy Is Nothing Then FirewallPolicy = FirewallPolicyGet()
'添加项目
FirewallPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_DOMAIN).AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
FirewallPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD).AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
FirewallPolicy.CurrentProfile.AuthorizedApplications.Add(New FirewallApp(DisplayName, FilePath))
End Sub
Private Class FirewallApp
Implements INetFwAuthorizedApplication
Public Property Name As String Implements INetFwAuthorizedApplication.Name
Public Property ProcessImageFileName As String Implements INetFwAuthorizedApplication.ProcessImageFileName
Public Property IpVersion As NET_FW_IP_VERSION_ = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY Implements INetFwAuthorizedApplication.IpVersion
Public Property Scope As NET_FW_SCOPE_ = NET_FW_SCOPE_.NET_FW_SCOPE_ALL Implements INetFwAuthorizedApplication.Scope
Public Property RemoteAddresses As String = "*" Implements INetFwAuthorizedApplication.RemoteAddresses
Public Property Enabled As Boolean = True Implements INetFwAuthorizedApplication.Enabled
Public Sub New(DisplayName As String, FilePath As String)
Name = DisplayName
ProcessImageFileName = FilePath
End Sub
End Class
'获取防火墙配置
Public Shared FirewallPolicy As INetFwPolicy = Nothing
Public Shared Function FirewallPolicyGet() As INetFwPolicy
Const CLSID_FIREWALL_MANAGER As String = "{304CE942-6E39-40D8-943A-B913C40C9CD4}"
Dim objType As Type = Type.GetTypeFromCLSID(New Guid(CLSID_FIREWALL_MANAGER))
Return TryCast(Activator.CreateInstance(objType), INetFwMgr).LocalPolicy
End Function
#End Region
#Region "页面切换" #Region "页面切换"
''' <summary> ''' <summary>
@@ -184,7 +104,6 @@ Public Class PageLinkLeft
PageLinkHiper.ModuleStopManually() PageLinkHiper.ModuleStopManually()
End Sub End Sub
Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs) Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs)
sender.Visibility = If(PageLinkIoi.InitLoader.State = LoadState.Finished, Visibility.Visible, Visibility.Collapsed)
End Sub End Sub
Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs) Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs)
PageLinkIoi.ModuleStopManually() PageLinkIoi.ModuleStopManually()
@@ -79,7 +79,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" /> <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="LaunchArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则不进行更改。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="默认" MaxLength="100" /> <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="LaunchArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则不进行更改。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="默认" MaxLength="100" />
<TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" /> <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchArgumentInfo" Text="Plain Craft Launcher 2" Grid.Column="1" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角"> <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchArgumentInfo" Text="Plain Craft Launcher 2" Grid.Column="1" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角">
<local:MyTextBox.ValidateRules> <local:MyTextBox.ValidateRules>
@@ -235,7 +235,7 @@
<local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。" /> <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="LaunchAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。" />
<TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" /> <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText="" <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="LaunchAdvanceRun" Grid.Column="1" HintText=""
ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" /> ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
<local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="LaunchAdvanceRunWait" Visibility="Collapsed" <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="LaunchAdvanceRunWait" Visibility="Collapsed"
Text="等待命令执行完成后再继续启动" Margin="0,8,0,7" /> Text="等待命令执行完成后再继续启动" Margin="0,8,0,7" />
<StackPanel Margin="0,12,0,4" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2"> <StackPanel Margin="0,12,0,4" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
@@ -26,6 +26,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表获取" Margin="0,0,25,0" /> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Text="版本列表获取" Margin="0,0,25,0" />
<local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1"> <local:MyComboBox x:Name="ComboDownloadVersion" Grid.ColumnSpan="2" Tag="ToolDownloadVersion" Grid.Column="1">
@@ -55,6 +56,9 @@
<local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器显示" Grid.Row="8" Height="22" Grid.ColumnSpan="2" <local:MyCheckBox Margin="0,2,0,4" Text="简化 Mod 加载器显示" Grid.Row="8" Height="22" Grid.ColumnSpan="2"
x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt" x:Name="CheckDownloadIgnoreQuilt" Tag="ToolDownloadIgnoreQuilt"
ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。&#xa;例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" /> ToolTip="下载 Mod 时,若该 Mod 支持 Forge 和 Fabric,则显示为支持任意 Mod 加载器。&#xa;例如,开启时 JEI 显示支持全版本,关闭后 JEI 就会显示仅支持 Forge / Fabric 全版本。" />
<local:MyCheckBox Margin="0,2,0,4" Text="验证 SSL 证书" Grid.Row="9" Height="22" Grid.ColumnSpan="2"
x:Name="CheckDownloadCert" Tag="ToolDownloadCert"
ToolTip="在进行连接时,检查 SSL 证书是否正常。&#xa;进行验证会提高安全性,但疑似会导致少部分玩家的微软登录失败。" />
</Grid> </Grid>
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -58,6 +58,7 @@
ComboDownloadTranslate.SelectedIndex = Setup.Get("ToolDownloadTranslate") ComboDownloadTranslate.SelectedIndex = Setup.Get("ToolDownloadTranslate")
CheckDownloadKeepModpack.Checked = Setup.Get("ToolDownloadKeepModpack") CheckDownloadKeepModpack.Checked = Setup.Get("ToolDownloadKeepModpack")
CheckDownloadIgnoreQuilt.Checked = Setup.Get("ToolDownloadIgnoreQuilt") CheckDownloadIgnoreQuilt.Checked = Setup.Get("ToolDownloadIgnoreQuilt")
CheckDownloadCert.Checked = Setup.Get("ToolDownloadCert")
'Minecraft 更新提示 'Minecraft 更新提示
CheckUpdateRelease.Checked = Setup.Get("ToolUpdateRelease") CheckUpdateRelease.Checked = Setup.Get("ToolUpdateRelease")
@@ -88,6 +89,7 @@
Setup.Reset("ToolDownloadTranslate") Setup.Reset("ToolDownloadTranslate")
Setup.Reset("ToolDownloadKeepModpack") Setup.Reset("ToolDownloadKeepModpack")
Setup.Reset("ToolDownloadIgnoreQuilt") Setup.Reset("ToolDownloadIgnoreQuilt")
Setup.Reset("ToolDownloadCert")
Setup.Reset("ToolUpdateRelease") Setup.Reset("ToolUpdateRelease")
Setup.Reset("ToolUpdateSnapshot") Setup.Reset("ToolUpdateSnapshot")
Setup.Reset("ToolHelpChinese") Setup.Reset("ToolHelpChinese")
@@ -109,7 +111,7 @@
End Sub End Sub
'将控件改变路由到设置改变 '将控件改变路由到设置改变
Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckDebugMode.Change, CheckDebugDelay.Change, CheckDebugSkipCopy.Change, CheckUpdateRelease.Change, CheckUpdateSnapshot.Change, CheckHelpChinese.Change, CheckDownloadKeepModpack.Change, CheckDownloadIgnoreQuilt.Change Private Shared Sub CheckBoxChange(sender As MyCheckBox, e As Object) Handles CheckDebugMode.Change, CheckDebugDelay.Change, CheckDebugSkipCopy.Change, CheckUpdateRelease.Change, CheckUpdateSnapshot.Change, CheckHelpChinese.Change, CheckDownloadKeepModpack.Change, CheckDownloadIgnoreQuilt.Change, CheckDownloadCert.Change
If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked) If AniControlEnabled = 0 Then Setup.Set(sender.Tag, sender.Checked)
End Sub End Sub
Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change
@@ -1,4 +1,6 @@
Public Class PageVersionMod Imports System.Security.Principal
Public Class PageVersionMod
#Region "初始化" #Region "初始化"
@@ -65,12 +67,12 @@
PanBack.Visibility = Visibility.Collapsed PanBack.Visibility = Visibility.Collapsed
Exit Sub Exit Sub
End If End If
SearchBox.Text = ""
'输出结果 '输出结果
ModItems.Clear() ModItems.Clear()
For Each ModEntity As McMod In Mods For Each ModEntity As McMod In Mods
ModItems(ModEntity.RawFileName) = McModListItem(ModEntity) ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
Next Next
SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
RefreshResult(Mods) RefreshResult(Mods)
Catch ex As Exception Catch ex As Exception
Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback) Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
@@ -333,20 +335,33 @@
End Sub End Sub
Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean) Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
Dim IsSuccessful As Boolean = True Dim IsSuccessful As Boolean = True
For Each ModEntity In ModList.ToList For Each ModE In ModList.ToList
Dim ModEntity = ModE '仅用于去除迭代变量无法修改的限制
Dim NewPath As String = Nothing Dim NewPath As String = Nothing
If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
'禁用 '禁用
NewPath = ModEntity.Path & ".disabled" NewPath = ModEntity.Path & If(File.Exists(ModEntity.Path & ".old"), ".old", ".disabled")
ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then
'启用 '启用
NewPath = ModEntity.Path.Replace(".disabled", "").Replace(".old", "") NewPath = ModEntity.RawPath
Else Else
Continue For Continue For
End If End If
'重命名 '重命名
Try Try
If File.Exists(NewPath) AndAlso Not File.Exists(ModEntity.Path) Then Continue For '因为未知原因 Mod 的状态已经切换完了 If File.Exists(NewPath) Then
If File.Exists(ModEntity.Path) Then
'同时存在两个名称的 Mod
If GetFileMD5(ModEntity.Path) <> GetFileMD5(NewPath) Then
MyMsgBox($"目前同时存在启用和禁用的两个 Mod 文件:{vbCrLf} - {NewPath}{vbCrLf} - {ModEntity.Path}{vbCrLf}{vbCrLf}注意,这两个文件的内容并不相同。{vbCrLf}在手动删除或重命名其中一个文件后,才能继续操作。", "存在文件冲突")
Continue For
End If
Else
'已经重命名过了
Log("[Mod] Mod 的状态已被切换", LogLevel.Debug)
Continue For
End If
End If
File.Delete(NewPath) File.Delete(NewPath)
FileSystem.Rename(ModEntity.Path, NewPath) FileSystem.Rename(ModEntity.Path, NewPath)
Catch ex As FileNotFoundException Catch ex As FileNotFoundException
@@ -389,6 +404,16 @@
Try Try
Dim IsSuccessful As Boolean = True Dim IsSuccessful As Boolean = True
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
'确认需要删除的文件
ModList = ModList.SelectMany(
Function(Target As McMod)
If Target.State = McMod.McModState.Fine Then
Return {Target.Path, Target.Path & If(File.Exists(Target.Path & ".old"), ".old", ".disabled")}
Else
Return {Target.Path, Target.RawPath}
End If
End Function).Distinct.Where(Function(m) File.Exists(m)).Select(Function(m) New McMod(m)).ToList()
'实际删除文件
For Each ModEntity In ModList For Each ModEntity In ModList
'删除 '删除
Try Try
@@ -410,8 +435,8 @@
'更改 Loader 和 UI 中的列表 '更改 Loader 和 UI 中的列表
McModLoader.Output.Remove(ModEntity) McModLoader.Output.Remove(ModEntity)
ModItems.Remove(ModEntity.RawFileName) ModItems.Remove(ModEntity.RawFileName)
Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).First(Function(i) i.Entry Is ModEntity)) Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
PanList.Children.RemoveAt(IndexOfUi) If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
Next Next
RefreshTitle() RefreshTitle()
If Not IsSuccessful Then If Not IsSuccessful Then
@@ -26,7 +26,7 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" /> <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏窗口标题" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则使用全局设置的值。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="使用全局设置" MaxLength="100" /> <local:MyTextBox x:Name="TextArgumentTitle" Grid.Row="0" Grid.ColumnSpan="2" Tag="VersionArgumentTitle" Grid.Column="1" ToolTip="自定义游戏窗口的标题,若留空则使用全局设置的值。&#xa;支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号" HintText="使用全局设置" MaxLength="100" />
<TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" /> <TextBlock Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义信息" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionArgumentInfo" Grid.Column="1" HintText="使用全局设置" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角。&#xa;若留空,则使用全局设置的值。"> <local:MyTextBox x:Name="TextArgumentInfo" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionArgumentInfo" Grid.Column="1" HintText="使用全局设置" ToolTip="该信息会显示在游戏主界面的左下角,与 F3 调试页面的左上角。&#xa;若留空,则使用全局设置的值。">
<local:MyTextBox.ValidateRules> <local:MyTextBox.ValidateRules>
@@ -168,10 +168,8 @@
<Grid Height="35" Name="PanServerNide" Margin="0,12,0,0"> <Grid Height="35" Name="PanServerNide" Margin="0,12,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" /> <ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Button" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<local:MyButton x:Name="BtnServerNideWeb" Grid.Column="0" MinWidth="140" Text="服主后台" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" /> <local:MyButton x:Name="BtnServerNideWeb" Grid.Column="0" MinWidth="140" Text="服主后台" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" ColorType="Highlight" />
<local:MyButton x:Name="BtnServerNideBbs" Grid.Column="1" MinWidth="140" Text="查看简介" Padding="13,0" Margin="0,0,20,0" HorizontalAlignment="Left" />
</Grid> </Grid>
<local:MyButton x:Name="BtnServerAuthLittle" Height="35" MinWidth="150" Margin="0,12,0,0" Text="设置为 LittleSkin" HorizontalAlignment="Left" ColorType="Highlight" /> <local:MyButton x:Name="BtnServerAuthLittle" Height="35" MinWidth="150" Margin="0,12,0,0" Text="设置为 LittleSkin" HorizontalAlignment="Left" ColorType="Highlight" />
</StackPanel> </StackPanel>
@@ -199,7 +197,7 @@
<local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。&#xa;若留空,则使用全局设置的值。" HintText="使用全局设置" /> <local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="2" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如输入 --demo 则会以试玩模式启动游戏。&#xa;若留空,则使用全局设置的值。" HintText="使用全局设置" />
<TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" /> <TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3" <local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。" /> ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:版本文件夹(.minecraft\versions\版本名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏版本名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行版本文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行版本文件夹下的 test.jar&#xa; · notepad &quot;{verindie}option.txt&quot; :使用记事本打开该版本的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
<local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed" <local:MyCheckBox Grid.Row="5" Grid.Column="1" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
Text="等待命令执行完成后再继续启动" Margin="0,5,0,0" /> Text="等待命令执行完成后再继续启动" Margin="0,5,0,0" />
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2"> <StackPanel Margin="0,10,0,0" Orientation="Horizontal" Grid.Row="6" Grid.ColumnSpan="2">
@@ -121,9 +121,10 @@
End Sub End Sub
Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerNide.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.ValidatedTextChanged, TextAdvanceRun.ValidatedTextChanged Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerNide.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.ValidatedTextChanged, TextAdvanceRun.ValidatedTextChanged
If AniControlEnabled = 0 Then If AniControlEnabled = 0 Then
Dim HandledText As String = sender.Text '#3194,不能删减 /
If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/") 'Dim HandledText As String = sender.Text
Setup.Set(sender.Tag, HandledText, Version:=PageVersionLeft.Version) 'If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/")
Setup.Set(sender.Tag, sender.Text, Version:=PageVersionLeft.Version)
End If End If
End Sub End Sub
Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderRamCustom.Change Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderRamCustom.Change
@@ -398,14 +399,12 @@ PreFin:
Private Sub BtnServerNideWeb_Click(sender As Object, e As EventArgs) Handles BtnServerNideWeb.Click Private Sub BtnServerNideWeb_Click(sender As Object, e As EventArgs) Handles BtnServerNideWeb.Click
OpenWebsite("https://login.mc-user.com:233/server/intro") OpenWebsite("https://login.mc-user.com:233/server/intro")
End Sub End Sub
Private Sub BtnServerNideBbs_Click(sender As Object, e As EventArgs) Handles BtnServerNideBbs.Click
OpenWebsite("https://www.mcbbs.net/thread-729821-1-1.html")
End Sub
'LittleSkin 'LittleSkin
Private Sub BtnServerAuthLittle_Click(sender As Object, e As EventArgs) Handles BtnServerAuthLittle.Click Private Sub BtnServerAuthLittle_Click(sender As Object, e As EventArgs) Handles BtnServerAuthLittle.Click
If TextServerAuthServer.Text <> "" AndAlso TextServerAuthServer.Text <> "https://littleskin.cn/api/yggdrasil" AndAlso If TextServerAuthServer.Text <> "" AndAlso TextServerAuthServer.Text <> "https://littleskin.cn/api/yggdrasil" AndAlso
MyMsgBox("即将把第三方登录设置覆盖为自动使用 LittleSkin 登录,是否继续?", "覆盖确认", "确定", "取消") = 2 Then Exit Sub MyMsgBox("即将把第三方登录设置覆盖为 LittleSkin 登录。" & vbCrLf & "除非你是服主,或者服主要求你这样做,否则请不要继续。" & vbCrLf & vbCrLf & "是否确实需要覆盖当前设置?",
"设置覆盖确认", "继续", "取消") = 2 Then Exit Sub
TextServerAuthServer.Text = "https://littleskin.cn/api/yggdrasil" TextServerAuthServer.Text = "https://littleskin.cn/api/yggdrasil"
TextServerAuthRegister.Text = "https://littleskin.cn/auth/register" TextServerAuthRegister.Text = "https://littleskin.cn/auth/register"
TextServerAuthName.Text = "LittleSkin 登录" TextServerAuthName.Text = "LittleSkin 登录"
@@ -838,9 +838,6 @@
<ItemGroup> <ItemGroup>
<Resource Include="Images\Blocks\Fabric.png" /> <Resource Include="Images\Blocks\Fabric.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Resource Include="Images\Heads\MCBBS.png" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\forge-installer.jar" /> <None Include="Resources\forge-installer.jar" />
</ItemGroup> </ItemGroup>
+5 -5
View File
@@ -30,7 +30,7 @@
Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" /> Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
<TextBlock TextWrapping="Wrap" Margin="0,0,0,6" <TextBlock TextWrapping="Wrap" Margin="0,0,0,6"
Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" /> Text="Image 代表图片,你需要在它的 Source 属性中填写一个网址或文件路径,它会从该处获取图片并显示。通常需要使用 Height 限制它的高度。" />
<Image Height="50" HorizontalAlignment="Center" Source="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" /> <Image Height="50" HorizontalAlignment="Center" Source="https://zh.minecraft.wiki/images/Grass_Block_JE7_BE6.png?bf67e" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -58,7 +58,7 @@
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" <TextBlock TextWrapping="Wrap" Margin="0,0,0,10"
Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" /> Text="将按钮的 EventType 属性设为 打开网页,然后在 EventData 属性中写入网址,即可通过点击按钮打开网页。" />
<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="打开 MCBBS" EventType="打开网页" EventData="https://www.mcbbs.net/" /> Text="打开 Minecraft Wiki" EventType="打开网页" EventData="https://zh.minecraft.wiki/" />
<TextBlock TextWrapping="Wrap" Margin="0,10,0,10" <TextBlock TextWrapping="Wrap" Margin="0,10,0,10"
Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" /> Text="或者将 EventType 属性改为 弹出窗口,然后在 EventData 属性中写入弹窗的标题与内容……" />
<local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
@@ -119,7 +119,7 @@
<TextBlock Margin="0,0,0,4" <TextBlock Margin="0,0,0,4"
Text="· 下载文件:在点击时要求选择文件保存位置,并且下载 EventData 的链接中的文件" /> Text="· 下载文件:在点击时要求选择文件保存位置,并且下载 EventData 的链接中的文件" />
<local:MyButton Margin="0,5,0,6" Width="250" Height="35" <local:MyButton Margin="0,5,0,6" Width="250" Height="35"
Text="下载 MCBBS 的 Logo" EventType="下载文件" EventData="https://www.mcbbs.net/template/mcbbs/image/logo_sc.png" /> Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
<TextBlock Margin="0,4,0,4" <TextBlock Margin="0,4,0,4"
Text="· 打开帮助:跳转到 EventData 对应的帮助文件(输入相对 Help 文件夹的路径)"/> Text="· 打开帮助:跳转到 EventData 对应的帮助文件(输入相对 Help 文件夹的路径)"/>
<local:MyButton Margin="0,5,0,6" Width="250" Height="35" <local:MyButton Margin="0,5,0,6" Width="250" Height="35"
@@ -153,7 +153,7 @@
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="内存优化" EventType="内存优化" /> Text="内存优化" EventType="内存优化" />
<local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0" <local:MyButton Margin="0,0,10,0" Width="140" Height="35" HorizontalAlignment="Left" Padding="13,0,13,0"
Text="下载作者的头像" EventType="下载文件" EventData="https://www.mcbbs.net/uc_server/data/avatar/000/14/51/06_avatar_small.jpg?ts=1471402592" /> Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
@@ -227,7 +227,7 @@
<TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 7 " Margin="5,0" Background="{DynamicResource ColorBrush7}" />
<TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" /> <TextBlock TextWrapping="Wrap" Text=" 浓度 8 " Margin="5,0" Background="{DynamicResource ColorBrush8}" />
</StackPanel> </StackPanel>
<TextBlock Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}" <TextBlock TextWrapping="Wrap" Margin="0,15,0,8" Foreground="{DynamicResource ColorBrush2}" Background="{DynamicResource ColorBrush6}"
Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" /> Text="除了字体颜色,你也可以将主题色用于背景色、边框颜色等颜色参数。例如这行字的颜色为浓度 2,背景为浓度 6。" />
</StackPanel> </StackPanel>
</local:MyCard> </local:MyCard>
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.