From 83ebc61d8a1f1773c983c70b5fc7ea0bce2e123a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E7=8C=AB=E8=B7=83?= <1043137532@qq.com>
Date: Thu, 22 Feb 2024 00:04:00 +0800
Subject: [PATCH] 2.6.14
---
Plain Craft Launcher 2/Application.xaml | 2 +-
Plain Craft Launcher 2/Application.xaml.vb | 11 +-
Plain Craft Launcher 2/Controls/MyResizer.vb | 2 +-
Plain Craft Launcher 2/FormMain.xaml | 2 +-
Plain Craft Launcher 2/FormMain.xaml.vb | 26 +-
Plain Craft Launcher 2/Images/Heads/MCBBS.png | Bin 715 -> 0 bytes
.../Modules/Base/ModBase.vb | 18 +-
Plain Craft Launcher 2/Modules/Base/ModNet.vb | 142 +-
.../Modules/Minecraft/ModComp.vb | 28 +-
.../Modules/Minecraft/ModCrash.vb | 3 +-
.../Modules/Minecraft/ModDownload.vb | 38 +-
.../Modules/Minecraft/ModJava.vb | 9 +-
.../Modules/Minecraft/ModLaunch.vb | 12 +-
.../Modules/Minecraft/ModMinecraft.vb | 27 +-
.../Modules/Minecraft/ModMod.vb | 46 +-
.../Modules/Minecraft/ModWatcher.vb | 2 +-
Plain Craft Launcher 2/Modules/ModMusic.vb | 2 +-
.../My Project/AssemblyInfo.vb | 4 +-
.../Pages/PageDownload/ModDownloadLib.vb | 178 +-
.../PageDownload/PageDownloadCompDetail.xaml | 1 -
.../PageDownloadCompDetail.xaml.vb | 4 -
.../Pages/PageLaunch/PageLaunchLeft.xaml | 2 +-
.../Pages/PageLaunch/PageLaunchLeft.xaml.vb | 4 +-
.../Pages/PageLink/PageLinkHiper.xaml.vb | 559 --
.../Pages/PageLink/PageLinkIoi.xaml.vb | 760 +--
.../Pages/PageLink/PageLinkLeft.xaml.vb | 83 +-
.../Pages/PageSetup/PageSetupLaunch.xaml | 4 +-
.../Pages/PageSetup/PageSetupSystem.xaml | 4 +
.../Pages/PageSetup/PageSetupSystem.xaml.vb | 4 +-
.../Pages/PageVersion/PageVersionMod.xaml.vb | 41 +-
.../Pages/PageVersion/PageVersionSetup.xaml | 6 +-
.../PageVersion/PageVersionSetup.xaml.vb | 13 +-
.../Plain Craft Launcher 2.vbproj | 3 -
Plain Craft Launcher 2/Resources/Custom.xaml | 10 +-
Plain Craft Launcher 2/Resources/Help.zip | Bin 75159 -> 75015 bytes
Plain Craft Launcher 2/Resources/ModData.txt | 4585 +++++++++++------
.../Resources/java-wrapper.jar | Bin 17734 -> 16526 bytes
37 files changed, 3234 insertions(+), 3401 deletions(-)
delete mode 100644 Plain Craft Launcher 2/Images/Heads/MCBBS.png
diff --git a/Plain Craft Launcher 2/Application.xaml b/Plain Craft Launcher 2/Application.xaml
index d5dc1df..ec434f0 100644
--- a/Plain Craft Launcher 2/Application.xaml
+++ b/Plain Craft Launcher 2/Application.xaml
@@ -277,7 +277,7 @@
-
+
diff --git a/Plain Craft Launcher 2/Application.xaml.vb b/Plain Craft Launcher 2/Application.xaml.vb
index 8c8a4cc..e838cfc 100644
--- a/Plain Craft Launcher 2/Application.xaml.vb
+++ b/Plain Craft Launcher 2/Application.xaml.vb
@@ -47,9 +47,12 @@ Public Class Application
Environment.Exit((My.Computer.Info.AvailablePhysicalMemory - Ram) / 1024) '返回清理的内存量(K)
End If
#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)
#End If
End If
@@ -112,13 +115,11 @@ Public Class Application
Setup.Load("SystemDebugMode")
Setup.Load("SystemDebugAnim")
Setup.Load("ToolDownloadThread")
+ Setup.Load("ToolDownloadCert")
'网络配置初始化
ServicePointManager.Expect100Continue = True
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
ServicePointManager.DefaultConnectionLimit = 1024
- 'ServicePointManager.ServerCertificateValidationCallback = New Security.RemoteCertificateValidationCallback(Function() As Boolean
- ' Return True
- ' End Function)
'计时
Log("[Start] 第一阶段加载用时:" & GetTimeTick() - ApplicationStartTick & " ms")
ApplicationStartTick = GetTimeTick()
diff --git a/Plain Craft Launcher 2/Controls/MyResizer.vb b/Plain Craft Launcher 2/Controls/MyResizer.vb
index 28afd44..e0c21ed 100644
--- a/Plain Craft Launcher 2/Controls/MyResizer.vb
+++ b/Plain Craft Launcher 2/Controls/MyResizer.vb
@@ -128,7 +128,7 @@ Public Class MyResizer
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)
AddHandler element.MouseLeftButtonDown, AddressOf Me.element_MouseLeftButtonDown
diff --git a/Plain Craft Launcher 2/FormMain.xaml b/Plain Craft Launcher 2/FormMain.xaml
index 331d370..2dd9671 100644
--- a/Plain Craft Launcher 2/FormMain.xaml
+++ b/Plain Craft Launcher 2/FormMain.xaml
@@ -134,7 +134,7 @@
-
+
diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb
index 33646c9..8089006 100644
--- a/Plain Craft Launcher 2/FormMain.xaml.vb
+++ b/Plain Craft Launcher 2/FormMain.xaml.vb
@@ -129,6 +129,12 @@ Public Class FormMain
'3:BUG+ IMP* FEAT-
'2:BUG* IMP-
'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
FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复无法启动 Forge 1.18.3+ 的 Bug"))
FeatureCount += 6
@@ -906,7 +912,7 @@ Public Class FormMain
Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
ElseIf Not (PageCurrent = PageType.VersionSetup AndAlso PageCurrentSub = PageSubType.VersionMod) Then
'未处于 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
'处于 Mod 管理页面
Install:
@@ -994,7 +1000,6 @@ Install:
Left -= 10000
ShowInTaskbar = False
Visibility = Visibility.Hidden
- 'SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) Or WS_EX_TOOLWINDOW)
Log("[System] 窗口已隐藏,位置:(" & Left & "," & Top & ")")
Else
'取消隐藏
@@ -1017,16 +1022,9 @@ Install:
Topmost = False
SetForegroundWindow(Handle)
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)
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
@@ -1573,7 +1571,11 @@ Install:
'''
Public Sub BackToTop() Handles BtnExtraBack.Click
Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
- RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset)
+ If RealScroll IsNot Nothing Then
+ RealScroll.PerformVerticalOffsetDelta(-RealScroll.VerticalOffset)
+ Else
+ Log("[UI] 无法返回顶部,未找到合适的 RealScroll", LogLevel.Hint)
+ End If
End Sub
Private Function BtnExtraBack_ShowCheck() As Boolean
Dim RealScroll As MyScrollViewer = BtnExtraBack_GetRealChild()
@@ -1581,9 +1583,7 @@ Install:
End Function
Private Function BtnExtraBack_GetRealChild() As MyScrollViewer
If PanMainRight.Child Is Nothing OrElse TypeOf PanMainRight.Child IsNot MyPageRight Then Return Nothing
- Dim Scroll = CType(PanMainRight.Child, MyPageRight).PanScroll
- If Scroll Is Nothing OrElse Scroll.Visibility <> Visibility.Visible Then Return Nothing
- Return Scroll
+ Return CType(PanMainRight.Child, MyPageRight).PanScroll
End Function
#End Region
diff --git a/Plain Craft Launcher 2/Images/Heads/MCBBS.png b/Plain Craft Launcher 2/Images/Heads/MCBBS.png
deleted file mode 100644
index b76dd5afda7421c23141d1053d934a09ba362886..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 715
zcmV;+0yO=JP)u<0$52z
zK~zY`&6P2aoG=iDFBNTu$WdC1xIP&12SD`p9`^$RLPCt
z*GwpV%LDrhh`cJLuRV~zURE_+<>353kFnkj#wLO)!V2PRZY|C+?O6=eK$Qd?>s8{BlAAIEF=Eokghtpxc;8v&m+Xd{_*~
zme0}e4*@>zZ!bUCHPF}JL#e+0l;{>2oNhK52Bq+qGa$Xbc7V!D`lHyE=#*c%JBus8
z??)037`d6lumA$1c6tDO%x4EK;X{OHL8(;T3^*Ob%$6@IB1-3!OUM=tT`q_tJ*qSW
zc?!6WTLfVLMQJc>MB0J^?P*~0@Sqgz+L>ji^q4c)%ZZ7*koeD_JedFh
diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
index 0e805db..b6d5130 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
@@ -11,12 +11,12 @@ Public Module ModBase
#Region "声明"
'下列版本信息由更新器自动修改
- Public Const VersionBaseName As String = "2.6.13" '不含分支前缀的显示用版本名
- Public Const VersionStandardCode As String = "2.6.13." & VersionBranchCode '标准格式的四段式版本号
+ Public Const VersionBaseName As String = "2.6.14" '不含分支前缀的显示用版本名
+ Public Const VersionStandardCode As String = "2.6.14." & VersionBranchCode '标准格式的四段式版本号
#If BETA Then
Public Const VersionCode As Integer = 313 'Release
#Else
- Public Const VersionCode As Integer = 312 'Snapshot
+ Public Const VersionCode As Integer = 314 'Snapshot
#End If
'自动生成的版本信息
Public Const VersionDisplayName As String = VersionBranchName & " " & VersionBaseName
@@ -158,9 +158,13 @@ Public Module ModBase
'''
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"
'''
- ''' 图标按钮,微软,0.9x
+ ''' 图标按钮,盾牌,1x
'''
- 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"
+ '''
+ ''' 图标按钮,离线,0.85x
+ '''
+ 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"
'''
''' 图标,音符,1x
'''
@@ -1980,9 +1984,10 @@ NextElement:
Log("[System] 执行外部命令并等待返回结果:" & FileName & " " & Arguments)
Using Program As New Process() With {.StartInfo = Info}
Program.Start()
+ Dim Result As String = Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
Program.WaitForExit(Timeout)
If Not Program.HasExited Then Program.Kill()
- Return Program.StandardOutput.ReadToEnd & Program.StandardError.ReadToEnd
+ Return Result
End Using
End Function
@@ -2111,6 +2116,7 @@ NextElement:
'''
Public Sub OpenWebsite(Url As String)
Try
+ If Not Url.ToLower.StartsWith("http") Then Throw New Exception(Url & " 不是一个有效的网址,它必须以 http 开头!")
Log("[System] 正在打开网页:" & Url)
Process.Start(Url)
Catch ex As Exception
diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
index aee04de..82673de 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb
+++ b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
@@ -192,7 +192,6 @@ RequestFinished:
If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Request.ProtocolVersion = HttpVersion.Version11
Request.Timeout = Timeout
Request.Accept = Accept
- Request.KeepAlive = False
SecretHeadersSign(Url, Request)
Using res As HttpWebResponse = Request.GetResponse()
Using HttpStream As Stream = res.GetResponseStream()
@@ -393,7 +392,6 @@ RequestFinished:
End If
Req.ContentType = ContentType
Req.Timeout = Timeout
- Req.KeepAlive = False
SecretHeadersSign(Url, Req)
If Url.StartsWith("https", StringComparison.OrdinalIgnoreCase) Then Req.ProtocolVersion = HttpVersion.Version11
If Method = "POST" OrElse Method = "PUT" Then
@@ -965,13 +963,12 @@ Capture:
'是否禁用多线程,以及规定碎片大小
Dim TargetUrl As String = GetSource().Url
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
For Each Thread As NetThread In Threads
If Thread.DownloadUndone > FilePieceMax.DownloadUndone Then FilePieceMax = Thread
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
StartSource = GetSource()
@@ -1021,7 +1018,7 @@ StartThread:
Dim HttpRequest As HttpWebRequest
Dim ResultStream As Stream = Nothing
'部分下载源真的特别慢,并且只需要一个请求,例如 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)
Info.State = NetState.Connect
Try
@@ -1206,7 +1203,9 @@ SourceBreak:
'End SyncLock
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 > NetTaskThreadLimit Then
Dim IsThisFail As Boolean = False
@@ -1225,7 +1224,7 @@ Wrong:
'本线程引发下载源被禁用
If IsThisFail Then
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
'没有可用源
Log("[Download] 文件 " & LocalName & " 已无可用下载源")
@@ -1475,7 +1474,7 @@ Retry:
End Get
Set(value As Integer)
_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)
On Error Resume Next
Dim ExList As New List(Of Exception)
@@ -1846,69 +1845,70 @@ Retry:
Private Sub StartManager()
If IsManagerStarted Then Exit Sub
IsManagerStarted = True
- RunInNewThread(Sub()
- Try
- Dim LastLoopTime As Long
- While True
- LastLoopTime = GetTimeTick()
- '若已完成,则清空
- If FileRemain = 0 AndAlso Files.Count > 0 Then
- SyncLock LockFiles
- Files.Clear()
- End SyncLock
- End If
- '开启新线程
- If Speed < NetTaskSpeedLimitLow OrElse FileRemain > NetTaskThreadLimit Then
- '速度小于下限或剩余文件还贼多,尝试开启线程
- Dim IsSuccess As Boolean = False
- Dim NewThreadCount As Integer
- '确定最大线程追加数
- NewThreadCount = Math.Max(FileRemain, MathClamp(NetTaskThreadCount / 2, 1, 4))
- NewThreadCount = Math.Floor(NewThreadCount / 2) '双线程启用减半
- NewThreadCount = MathClamp(NewThreadCount, 1, NetTaskThreadLimit)
- '循环追加
- Do
- IsSuccess = False
- '启动 Wait 的文件后立即会变成 Connect,导致第二次循环再次调用,所以需要先暂时存储进去……
- '此外为了减少 LockFiles 的占用时间,所以先遍历列表再开始
- Dim FilesWaiting As New List(Of NetFile)
- Dim FilesLoading As New List(Of NetFile)
- SyncLock LockFiles
- For Each File As NetFile In Files.Values
- If File.RandomCode Mod 2 = 0 Then Continue For
- If File.State = NetState.WaitForDownload Then
- FilesWaiting.Add(File)
- ElseIf File.State < NetState.Merge Then
- FilesLoading.Add(File)
- End If
- Next
- End SyncLock
- '为文件列表中的文件开始线程
- For Each File As NetFile In FilesWaiting
- If NewThreadCount = 0 Then Exit For
- If File.TryBeginThread() Then
- IsSuccess = True
- NewThreadCount -= 1
- End If
- Next
- For Each File As NetFile In FilesLoading
- If NewThreadCount = 0 Then Exit For
- If File.TryBeginThread() Then
- IsSuccess = True
- NewThreadCount -= 1
- End If
- Next
- Loop While NewThreadCount > 0 AndAlso IsSuccess
- End If
- '等待直至 120 ms
- Do While GetTimeTick() - LastLoopTime < 120
- Thread.Sleep(10)
- Loop
- End While
- Catch ex As Exception
- Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert)
- End Try
- End Sub, "NetManager ThreadStarter Single")
+ RunInNewThread(
+ Sub()
+ Try
+ Dim LastLoopTime As Long
+ While True
+ LastLoopTime = GetTimeTick()
+ '若已完成,则清空
+ If FileRemain = 0 AndAlso Files.Count > 0 Then
+ SyncLock LockFiles
+ Files.Clear()
+ End SyncLock
+ End If
+ '开启新线程
+ If Speed < NetTaskSpeedLimitLow OrElse FileRemain > NetTaskThreadLimit Then
+ '速度小于下限或剩余文件还贼多,尝试开启线程
+ Dim IsSuccess As Boolean = False
+ Dim NewThreadCount As Integer
+ '确定最大线程追加数
+ NewThreadCount = Math.Max(FileRemain, MathClamp(NetTaskThreadCount / 2, 1, 4))
+ NewThreadCount = Math.Floor(NewThreadCount / 2) '双线程启用减半
+ NewThreadCount = MathClamp(NewThreadCount, 1, NetTaskThreadLimit)
+ '循环追加
+ Do
+ IsSuccess = False
+ '启动 Wait 的文件后立即会变成 Connect,导致第二次循环再次调用,所以需要先暂时存储进去……
+ '此外为了减少 LockFiles 的占用时间,所以先遍历列表再开始
+ Dim FilesWaiting As New List(Of NetFile)
+ Dim FilesLoading As New List(Of NetFile)
+ SyncLock LockFiles
+ For Each File As NetFile In Files.Values
+ If File.RandomCode Mod 2 = 0 Then Continue For
+ If File.State = NetState.WaitForDownload Then
+ FilesWaiting.Add(File)
+ ElseIf File.State < NetState.Merge Then
+ FilesLoading.Add(File)
+ End If
+ Next
+ End SyncLock
+ '为文件列表中的文件开始线程
+ For Each File As NetFile In FilesWaiting
+ If NewThreadCount = 0 Then Exit For
+ If File.TryBeginThread() Then
+ IsSuccess = True
+ NewThreadCount -= 1
+ End If
+ Next
+ For Each File As NetFile In FilesLoading
+ If NewThreadCount = 0 Then Exit For
+ If File.TryBeginThread() Then
+ IsSuccess = True
+ NewThreadCount -= 1
+ End If
+ Next
+ '等待 40 ms:BMCLAPI 部分时候有 20QPS 的限制
+ Thread.Sleep(40)
+ Loop While NewThreadCount > 0 AndAlso IsSuccess
+ End If
+ '等待 40 ms
+ Thread.Sleep(40)
+ End While
+ Catch ex As Exception
+ Log(ex, "下载管理启动线程 1 出错", LogLevel.Assert)
+ End Try
+ End Sub, "NetManager ThreadStarter Single")
RunInNewThread(Sub()
Try
Dim LastLoopTime As Long
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
index c5fcd7b..e443eba 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModComp.vb
@@ -59,7 +59,6 @@
Entry.WikiId = i
If SplitedLine.Count >= 2 Then
Entry.ChineseName = SplitedLine(1)
- Entry.MCBBS = If(SplitedLine.Count >= 3, SplitedLine(2), Nothing)
If Entry.ChineseName.Contains("*") Then '处理 *
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))) & ")")
@@ -82,10 +81,6 @@
'''
Public ChineseName As String = ""
'''
- ''' MCBBS 发布帖编号(例如 195107)。若为 Nothing 则没有发布帖。
- '''
- Public MCBBS As Integer?
- '''
''' CurseForge Slug(例如 advanced-solar-panels)。
'''
Public CurseForgeSlug As String = Nothing
@@ -95,7 +90,7 @@
Public ModrinthSlug As String = Nothing
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 Class
@@ -200,14 +195,6 @@
End Get
End Property
'''
- ''' MCBBS 的帖子 ID。若为 0 则没有。
- '''
- Public ReadOnly Property MCBBS As Integer
- Get
- Return If(DatabaseEntry Is Nothing, 0, If(DatabaseEntry.MCBBS, 0))
- End Get
- End Property
- '''
''' 翻译后的中文名。若数据库没有则等同于 RawName。
'''
Public ReadOnly Property TranslatedName As String
@@ -884,10 +871,10 @@ NoSubtitle:
Dim SearchResult As String = ""
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 SearchResults(i).Item.CurseForgeSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.CurseForgeSlug.Replace("-", " ") & " "
- If SearchResults(i).Item.ModrinthSlug IsNot Nothing Then SearchResult += SearchResults(i).Item.ModrinthSlug.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("-", " ").Replace("/", " ") & " "
SearchResult += SearchResults(i).Item.ChineseName.Split(" (").Last.TrimEnd(") ").Split(" - ").First.
- Replace(":", "").Replace("(", "").Replace(")", "").ToLower & " "
+ Replace(":", "").Replace("(", "").Replace(")", "").ToLower.Replace("/", " ") & " "
Next
Log("[Comp] 中文搜索原始关键词:" & SearchResult, LogLevel.Developer)
'去除常见连接词
@@ -911,9 +898,8 @@ NoSubtitle:
For Each Keyword In AllPossibleKeywords.Split(" ")
Keyword = Keyword.Trim("["c, "]"c)
If Keyword = "" Then Continue For
- If {"forge", "fabric", "for", "mod",
- "forge/fabric", "fabric/forge", "fabric/quilt", "quilt/fabric"}.Contains(Keyword) Then '#208
- Log("[Comp] 已跳过搜索关键词:" & Keyword)
+ If {"forge", "fabric", "for", "mod", "quilt"}.Contains(Keyword) Then '#208
+ Log("[Comp] 已跳过搜索关键词:" & Keyword, LogLevel.Developer)
Continue For
End If
RightKeywords.Add(Keyword)
@@ -927,7 +913,7 @@ NoSubtitle:
'例外项:OptiForge、OptiFabric(拆词后因为包含 Forge/Fabric 导致无法搜到实际的 Mod)
If RawFilter.Replace(" ", "").ToLower.Contains("optiforge") Then Task.Input.SearchText = "optiforge"
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
#End Region
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
index 028175f..bf48d19 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModCrash.vb
@@ -583,7 +583,8 @@ Done:
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("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 Sub
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
index a14c55d..d9c8eb5 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModDownload.vb
@@ -221,9 +221,7 @@
'''
Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
- Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json",
- BackupUrl:="https://download.mcbbs.net/mc/game/version_manifest.json",
- IsJson:=True)
+ Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
Try
Dim Versions As JArray = Json("versions")
If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
@@ -384,8 +382,7 @@
'''
Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
- Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList",
- BackupUrl:="https://download.mcbbs.net/optifine/versionList", IsJson:=True)
+ Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
Try
Dim Versions As New List(Of DlOptiFineListEntry)
For Each Token As JObject In Json
@@ -469,8 +466,7 @@
'''
Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
- Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default,
- BackupUrl:="https://download.mcbbs.net/forge/minecraft")
+ Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
'获取所有版本信息
Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
@@ -664,8 +660,7 @@
''' Forge 版本列表,BMCLAPI。
'''
Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
- Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input,
- BackupUrl:="https://download.mcbbs.net/forge/minecraft/" & Loader.Input, IsJson:=True)
+ Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, IsJson:=True)
Dim Versions As New List(Of DlForgeVersionEntry)
Try
Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
@@ -827,8 +822,7 @@
'''
Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
- Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json",
- BackupUrl:="https://download.mcbbs.net/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
+ Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
Try
Dim Json As JObject = Result("versions")
Dim Versions As New List(Of DlLiteLoaderListEntry)
@@ -914,8 +908,7 @@
'''
Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
- Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions",
- BackupUrl:="https://download.mcbbs.net/fabric-meta/v2/versions", IsJson:=True)
+ Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
Try
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("获取到的列表缺乏必要项")
@@ -943,26 +936,25 @@
Public Function DlSourceResourceGet(MojangBase As String) As String()
MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
- Return {MojangBase.Replace("https://piston-data.mojang.com", "https://download.mcbbs.net/assets").Replace("https://piston-meta.mojang.com", "https://download.mcbbs.net/assets").Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
- MojangBase,
- MojangBase.Replace("https://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")
+ Return {
+ 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
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"),
- 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,
+ Return {
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
Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
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"),
- 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")
+ Return {
+ 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
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
index 5789b60..abd3dfc 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModJava.vb
@@ -727,8 +727,7 @@ Wait:
Private Sub JavaFileList(Loader As LoaderTask(Of Integer, List(Of NetFile)))
Log("[Java] 开始获取 Java 下载信息")
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"},
IsJson:=True)
'获取下载地址
@@ -740,8 +739,7 @@ Wait:
Log($"[Java] 准备下载 Java {TargetEntry.Value("version")("name")}({TargetEntry.Key}):{Address}")
'获取文件列表
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},
IsJson:=True)
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 '跳过已存在的文件
Dim Url As String = Info("url")
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},
BaseDir & File.Name, Checker))
Next
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
index 6168f97..998c88a 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
@@ -1911,9 +1911,9 @@ IgnoreCustomSkin:
'获取自定义命令
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)
- If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion)
+ If CustomCommandVersion <> "" Then CustomCommandVersion = ArgumentReplace(CustomCommandVersion, True)
'输出 bat
Try
@@ -2072,7 +2072,7 @@ IgnoreCustomSkin:
'获取窗口标题
Dim WindowTitle As String = Setup.Get("VersionArgumentTitle", Version:=McVersionCurrent)
If WindowTitle = "" Then WindowTitle = Setup.Get("LaunchArgumentTitle")
- WindowTitle = ArgumentReplace(WindowTitle)
+ WindowTitle = ArgumentReplace(WindowTitle, False)
'初始化等待
Dim Watcher As New Watcher(Loader, McVersionCurrent, WindowTitle)
@@ -2124,7 +2124,7 @@ IgnoreCustomSkin:
'''
''' 在启动结束时,对 PCL 约定的替换标记进行处理。
'''
- 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
'路径替换
Raw = Raw.Replace("{minecraft}", PathMcFolder)
@@ -2134,6 +2134,10 @@ IgnoreCustomSkin:
'普通替换
Raw = Raw.Replace("{user}", McLoginLoader.Output.Name)
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
Case McLoginType.Legacy
If PageLinkHiper.HiperState = LoadState.Finished Then
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
index 9e4f922..4ffb811 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
@@ -42,15 +42,19 @@
#Region "读取默认(Original)文件夹,即当前、官启文件夹,可能没有结果"
'扫描当前文件夹
- If CheckPermission(Path) AndAlso 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
- 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})
- Next
+ 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
+ If Directory.Exists(Folder.FullName & "versions\") OrElse Folder.Name = ".minecraft" Then CacheMcFolderList.Add(New McFolder With {.Name = "当前文件夹", .Path = Folder.FullName & "\", .Type = McFolderType.Original})
+ Next
+ Catch ex As Exception
+ Log(ex, "扫描 PCL 所在文件夹中是否有 MC 文件夹失败")
+ End Try
'扫描官启文件夹
Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\"
- If (CacheMcFolderList.Count = 0 OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹
- CheckPermission(MojangPath) AndAlso Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹
+ If (Not CacheMcFolderList.Any OrElse MojangPath <> CacheMcFolderList(0).Path) AndAlso '当前文件夹不是官启文件夹
+ Directory.Exists(MojangPath & "versions\") Then '具有权限且存在 versions 文件夹
CacheMcFolderList.Add(New McFolder With {.Name = "官方启动器文件夹", .Path = MojangPath, .Type = McFolderType.Original})
End If
@@ -406,7 +410,7 @@
End If
'无法获取
_Version.McName = "Unknown"
- Info = "PCL 无法识别该版本,请向作者反馈此问题"
+ Info = "PCL 无法识别该版本的 MC 版本号"
Catch ex As Exception
Log(ex, "识别 Minecraft 版本时出错")
_Version.McName = "Unknown"
@@ -1915,7 +1919,7 @@ NextVersion:
'获取下载信息
Try
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
Log(ex, "获取 Authlib-Injector 下载信息失败")
End Try
@@ -1926,9 +1930,7 @@ NextVersion:
'开始下载
Dim DownloadAddress As String = DownloadInfo("download_url")
Log("[Minecraft] Authlib-Injector 需要更新:" & DownloadAddress, LogLevel.Developer)
- Result.Add(New NetFile({
- DownloadAddress.Replace("bmclapi2.bangbang93.com", "download.mcbbs.net"), DownloadAddress
- }, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
+ Result.Add(New NetFile({DownloadAddress}, TargetFile, New FileChecker(Hash:=DownloadInfo("checksums")("sha256").ToString)))
End If
End If
End If
@@ -1958,7 +1960,7 @@ NextVersion:
'获取 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("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
If Token.LocalPath.Contains("transformer-discovery-service") Then
'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("-", "_")
OptiFineBase = "/maven/com/optifine/" & OptiFineBase
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)
ElseIf Urls.Count <= 2 Then
'普通文件
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
index 2eae992..f768279 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModMod.vb
@@ -13,6 +13,14 @@ Public Module ModMod
Public Sub New(Path As String)
Me.Path = If(Path, "")
End Sub
+ '''
+ ''' Mod 的完整路径,去除最后的 .disabled 和 .old。
+ '''
+ Public ReadOnly Property RawPath As String
+ Get
+ Return GetPathFromFullPath(Path) & RawFileName
+ End Get
+ End Property
'''
''' Mod 的完整文件名。
@@ -768,6 +776,18 @@ VersionFindFail:
End Property
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
'''
@@ -844,6 +864,17 @@ VersionFindFail:
'加载 McMod 对象
Dim ModEntry As New McMod(ModFile.FullName)
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)
'读取 Comp 缓存
If ModEntry.State = McMod.McModState.Unavaliable Then Continue For
@@ -860,13 +891,14 @@ VersionFindFail:
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
- If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
- Return Left.State = McMod.McModState.Unavaliable
- Else
- Return Not Right.FileName.CompareTo(Left.FileName)
- End If
- End Function)
+ ModList = Sort(ModList,
+ Function(Left As McMod, Right As McMod) As Boolean
+ If (Left.State = McMod.McModState.Unavaliable) <> (Right.State = McMod.McModState.Unavaliable) Then
+ Return Left.State = McMod.McModState.Unavaliable
+ Else
+ Return Not Right.FileName.CompareTo(Left.FileName)
+ End If
+ End Function)
'回设
If Loader.IsAborted Then Exit Sub
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
index ab5859a..9926e4b 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
+++ b/Plain Craft Launcher 2/Modules/Minecraft/ModWatcher.vb
@@ -96,7 +96,7 @@
'设置窗口标题
For i = 1 To 3
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)
End If
Thread.Sleep(64)
diff --git a/Plain Craft Launcher 2/Modules/ModMusic.vb b/Plain Craft Launcher 2/Modules/ModMusic.vb
index b9fa956..3df0efe 100644
--- a/Plain Craft Launcher 2/Modules/ModMusic.vb
+++ b/Plain Craft Launcher 2/Modules/ModMusic.vb
@@ -25,7 +25,7 @@
For Each File In EnumerateFiles(Path & "PCL\Musics\")
'文件夹可能会被加入 .ini 文件夹配置文件、一些乱七八糟的 .jpg 文件啥的
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)
Next
End If
diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
index 433f0b8..d402fe4 100644
--- a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
+++ b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*”
-
-
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
index dd9510e..1eaffa1 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
+++ b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
@@ -519,10 +519,8 @@ Public Module ModDownloadLib
Dim Sources As New List(Of String)
'BMCLAPI 源
If DownloadInfo.IsPreview Then
- Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
Else
- Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
End If
'官方源
@@ -682,30 +680,28 @@ Retry:
Dim Loaders As New List(Of LoaderBase)
'获取下载地址
Loaders.Add(New LoaderTask(Of DlOptiFineListEntry, List(Of NetFile))("获取 OptiFine 下载地址",
- Sub(Task As LoaderTask(Of DlOptiFineListEntry, List(Of NetFile)))
- Dim Sources As New List(Of String)
- 'BMCLAPI 源
- If DownloadInfo.IsPreview Then
- Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
- Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
- Else
- Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
- Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
- End If
- '官方源
- Dim PageData As String
- Try
- PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
- Task.Progress = 0.8
- Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
- Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
- Catch ex As Exception
- Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
- End Try
- Task.Progress = 0.9
- '构造文件请求
- Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, TargetFolder, New FileChecker(MinSize:=64 * 1024))}
- End Sub) With {.ProgressWeight = 6})
+ Sub(Task As LoaderTask(Of DlOptiFineListEntry, List(Of NetFile)))
+ Dim Sources As New List(Of String)
+ 'BMCLAPI 源
+ If DownloadInfo.IsPreview Then
+ Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
+ Else
+ Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
+ End If
+ '官方源
+ Dim PageData As String
+ Try
+ PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
+ Task.Progress = 0.8
+ Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
+ Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
+ Catch ex As Exception
+ Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
+ End Try
+ Task.Progress = 0.9
+ '构造文件请求
+ Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, TargetFolder, New FileChecker(MinSize:=64 * 1024))}
+ End Sub) With {.ProgressWeight = 6})
'下载
Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 10, .Block = True})
Return Loaders
@@ -852,22 +848,11 @@ Retry:
Case "1.6.2"
Address.Add("https://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar")
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")
Case Else
Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")")
End Select
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)
End If
@@ -1097,15 +1082,14 @@ Retry:
Dim Loaders As New List(Of LoaderBase)
'获取下载地址
Loaders.Add(New LoaderTask(Of DlForgeVersionEntry, List(Of NetFile))("获取下载地址",
- Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
- Dim Files As New List(Of 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://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
- }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
- Task.Output = Files
- End Sub) With {.ProgressWeight = 0.1, .Show = False})
+ Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
+ Dim Files As New List(Of NetFile)
+ Files.Add(New NetFile({
+ "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
+ }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
+ Task.Output = Files
+ End Sub) With {.ProgressWeight = 0.1, .Show = False})
'下载
Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile)) With {.ProgressWeight = 6})
@@ -1123,7 +1107,7 @@ Retry:
Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
'选择 Java
- Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+ Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
If Java Is Nothing Then
If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
'开始自动下载
@@ -1133,7 +1117,7 @@ Retry:
Thread.Sleep(10)
Loop
'检查下载结果
- Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
+ Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 60))
If Task.IsAborted Then Exit Sub
If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
End If
@@ -1160,50 +1144,52 @@ Retry:
Dim LastResults As New Queue(Of String)
Using outputWaitHandle As New AutoResetEvent(False)
Using errorWaitHandle As New AutoResetEvent(False)
- AddHandler process.OutputDataReceived, Function(sender, e)
- Try
- If e.Data Is Nothing Then
- outputWaitHandle.[Set]()
- Else
- LastResults.Enqueue(e.Data)
- If LastResults.Count > 100 Then LastResults.Dequeue()
- ForgeInjectorLine(e.Data, Task)
- End If
- Catch ex As ObjectDisposedException
- Catch ex As Exception
- Log(ex, "读取 Forge 安装器信息失败")
- End Try
- Try
- If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
- Log("[Installer] 由于任务取消,已中止 Forge 安装")
- process.Kill()
- End If
- Catch
- End Try
- Return Nothing
- End Function
- AddHandler process.ErrorDataReceived, Function(sender, e)
- Try
- If e.Data Is Nothing Then
- errorWaitHandle.[Set]()
- Else
- LastResults.Enqueue(e.Data)
- If LastResults.Count > 100 Then LastResults.Dequeue()
- ForgeInjectorLine(e.Data, Task)
- End If
- Catch ex As ObjectDisposedException
- Catch ex As Exception
- Log(ex, "读取 Forge 安装器错误信息失败")
- End Try
- Try
- If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
- Log("[Installer] 由于任务取消,已中止 Forge 安装")
- process.Kill()
- End If
- Catch
- End Try
- Return Nothing
- End Function
+ AddHandler process.OutputDataReceived,
+ Function(sender, e)
+ Try
+ If e.Data Is Nothing Then
+ outputWaitHandle.[Set]()
+ Else
+ LastResults.Enqueue(e.Data)
+ If LastResults.Count > 100 Then LastResults.Dequeue()
+ ForgeInjectorLine(e.Data, Task)
+ End If
+ Catch ex As ObjectDisposedException
+ Catch ex As Exception
+ Log(ex, "读取 Forge 安装器信息失败")
+ End Try
+ Try
+ If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
+ Log("[Installer] 由于任务取消,已中止 Forge 安装")
+ process.Kill()
+ End If
+ Catch
+ End Try
+ Return Nothing
+ End Function
+ AddHandler process.ErrorDataReceived,
+ Function(sender, e)
+ Try
+ If e.Data Is Nothing Then
+ errorWaitHandle.[Set]()
+ Else
+ LastResults.Enqueue(e.Data)
+ If LastResults.Count > 100 Then LastResults.Dequeue()
+ ForgeInjectorLine(e.Data, Task)
+ End If
+ Catch ex As ObjectDisposedException
+ Catch ex As Exception
+ Log(ex, "读取 Forge 安装器错误信息失败")
+ End Try
+ Try
+ If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
+ Log("[Installer] 由于任务取消,已中止 Forge 安装")
+ process.Kill()
+ End If
+ Catch
+ End Try
+ Return Nothing
+ End Function
process.Start()
process.BeginOutputReadLine()
process.BeginErrorReadLine()
@@ -1321,8 +1307,7 @@ Retry:
'添加主文件
Dim Files As New List(Of NetFile) From {New NetFile({
"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://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
+ "https://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
}, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))}
Task.Output = Files
End Sub) With {.ProgressWeight = 0.5, .Show = False})
@@ -1650,7 +1635,7 @@ Retry:
RunInNewThread(Sub()
Try
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 & ")")
Dim ResultJson As JContainer = GetJson(Result)
'获取所有推荐版本列表
@@ -1721,7 +1706,6 @@ Retry:
'下载
'BMCLAPI 不支持 Fabric Installer 下载
Dim Address As New List(Of String)
- 'Address.Add(Url.Replace("maven.fabricmc.net", "download.mcbbs.net/maven"))
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})
'启动
@@ -1759,7 +1743,7 @@ Retry:
Task.Progress = 0.5
'构造文件请求
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"
}, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))}
'新建 mods 文件夹
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml
index f7a4199..7333268 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml
+++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml
@@ -16,7 +16,6 @@
-
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
index 4197cb2..e68051c 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompDetail.xaml.vb
@@ -145,7 +145,6 @@
'决定按钮显示
BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
- BtnIntroMCBBS.Visibility = If(Project.MCBBS = 0, Visibility.Collapsed, Visibility.Visible)
AniControlEnabled -= 1
End Sub
@@ -323,9 +322,6 @@
Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
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
ClipboardSet(CompItem.LabTitle.Text)
End Sub
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml
index 488a381..4b6a83c 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml
@@ -33,7 +33,7 @@
-
+
diff --git a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
index 27219b8..afa79c1 100644
--- a/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.vb
@@ -286,7 +286,7 @@ UnknownType:
Setup.Set("LoginType", McLoginType.Ms)
PanType.Visibility = Visibility.Collapsed
PanTypeOne.Visibility = Visibility.Visible
- PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonMs)
+ PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield)
LabTypeOne.Text = "正版登录"
RadioLoginType5.Visibility = Visibility.Visible
RadioLoginType0.Visibility = Visibility.Collapsed
@@ -295,7 +295,7 @@ UnknownType:
Setup.Set("LoginType", McLoginType.Legacy)
PanType.Visibility = Visibility.Collapsed
PanTypeOne.Visibility = Visibility.Visible
- PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonUser)
+ PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline)
LabTypeOne.Text = "离线登录"
Case 3 '统一通行证
If Setup.Get("CacheNideAccess") = "" Then
diff --git a/Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb b/Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb
index 656ac59..7f95d6f 100644
--- a/Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLink/PageLinkHiper.xaml.vb
@@ -58,307 +58,21 @@
'检查网络状态
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
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
Private Shared PingTime As Integer, IpCheckStatus As LoadState = LoadState.Loading, IpIsInChina As Boolean
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
'获取所需文件
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
Public Class CertOutdatedException
Inherits Exception
End Class
'启动联机模块
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
Private Shared PingNodes As Integer, AllNodes As List(Of String)
@@ -389,40 +103,6 @@ FinishMcbFileCheck:
'''
Public Shared Function HiperStop(SleepWhenKilled As Boolean) As Boolean
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
'''
''' 启动程序,并等待初始化完成后退出运行,同时更新 HiperIp。
@@ -430,125 +110,10 @@ FinishMcbFileCheck:
''' 若程序正在运行,则会先停止其运行。
'''
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
'Hiper 日志
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
Private Shared PossibleFailReason As String = Nothing
@@ -643,42 +208,6 @@ FinishMcbFileCheck:
'每 15 秒执行的 Timer
Private Shared HostPing As Integer = -1
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 Region
@@ -711,14 +240,6 @@ FinishMcbFileCheck:
'创建房间
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
Private Sub RoomCreate(Port As Integer)
'记录信息
@@ -730,34 +251,6 @@ FinishMcbFileCheck:
'加入房间
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
Private Sub RoomJoin(Ip As String, Port As Integer)
'记录信息
@@ -773,54 +266,6 @@ FinishMcbFileCheck:
'承接状态切换的 UI 改变
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
Private Shared LoadStep 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
- 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
'Ping 房主
diff --git a/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb b/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb
index 6fad614..a91aaff 100644
--- a/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLink/PageLinkIoi.xaml.vb
@@ -1,204 +1,10 @@
-Imports System.Net
-Imports System.Net.Sockets
+Imports System.Net.Sockets
Public Class PageLinkIoi
Public Const RequestVersion As Integer = 4
Public Const IoiVersion As Integer = 10 '由于已关闭更新渠道,在提升 IoiVersion 时必须提升 RequestVersion
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 "进程管理"
Private Shared IoiId As String, IoiPassword As String
@@ -209,38 +15,7 @@ StartDownload:
''' 若 Ioi 正在运行,则结束 Ioi 进程,同时初始化状态数据。返回是否关闭了对应进程。
'''
Public Shared Function IoiStop(SleepWhenKilled As Boolean) As Boolean
- IoiStop = 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)
+ Return False
End Function
'''
''' 启动 Ioi,并等待初始化完成后退出运行,同时更新 IoiId 与 IoiPassword。
@@ -248,130 +23,11 @@ StartDownload:
''' 若 Ioi 正在运行,则会先停止其运行。
'''
Public Shared Function IoiStart() As Boolean
- IoiStop(True)
- 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
+ Return False
End Function
'Ioi 日志
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
Private Shared LogLinesCount As Integer = 0
Private Shared LastPortsId As String = "" '上一个收到 portssub 的 ID,用于记录端口
@@ -384,94 +40,10 @@ StartDownload:
Private Shared UserListIdentifyCache As String = ""
Private Shared RoomListIdentifyCache As String = ""
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
'工作线程刷新
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 Region
@@ -482,65 +54,6 @@ StartDownload:
''' 发送 Portsub 请求并等待获取控制台端口。进度将从 0 变化至 80%。
'''
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
'''
@@ -584,20 +97,6 @@ Done:
''' 尝试发送断开请求,并将其从用户列表中移除。
'''
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 Region
@@ -606,29 +105,9 @@ Done:
'刷新连接
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
'断开连接
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
'复制联机码
Public Shared Sub BtnLeftCopy_Click() Handles BtnLeftCopy.Click
@@ -680,46 +159,10 @@ Done:
#Region "请求核心"
- Private Shared Listener As Socket = Nothing
'''
''' 启动 Socket 监听核心。
'''
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 Region
@@ -738,40 +181,9 @@ Done:
'请求管理
Public Socket As Socket = Nothing
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
Public ListenerThread As Thread = Nothing
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
Public Sub BindSocket(Socket As Socket)
If Me.Socket IsNot Nothing Then Throw New Exception("该用户已经绑定了 Socket")
@@ -975,135 +387,12 @@ Done:
'正向与反向连接
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
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
'创建房间
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
Private Shared Sub SendUpdateRequestToAllUsers()
For i = 0 To UserList.Count - 1
@@ -1119,18 +408,6 @@ Done:
End Sub
'修改房间名称
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
'加入房间
Private Shared Sub BtnRoom_Click(sender As MyListItem, e As EventArgs)
@@ -1141,25 +418,6 @@ Done:
End Sub
'关闭房间
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
'获取数据包
@@ -1238,21 +496,9 @@ Done:
''' 从用户列表中移除一位用户。提示信息视作该用户主动离开。
'''
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
- '关闭联机模块按钮
- 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()
- IoiStop(False)
- InitLoader.Error = New Exception("联机模块已关闭,点击以重新启动")
- InitLoader.State = LoadState.Failed
- IoiState = LoadState.Failed
End Sub
End Class
diff --git a/Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb b/Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb
index 7f3fb16..7d62918 100644
--- a/Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageLink/PageLinkLeft.xaml.vb
@@ -1,6 +1,4 @@
-Imports NetFwTypeLib
-
-Public Class PageLinkLeft
+Public Class PageLinkLeft
Private IsLoad As Boolean = False
Private IsPageSwitched As Boolean = False '如果在 Loaded 前切换到其他页面,会导致触发 Loaded 时再次切换一次
@@ -15,84 +13,6 @@ Public Class PageLinkLeft
IsPageSwitched = False
End Sub
-#Region "Windows 防火墙"
-
- '为联机模块添加防火墙通行权限的函数库
- '不加防火墙权限你要我怎么办,这联机根本连都连不上,球球你们别看到防火墙就说是后门了,真的很恼火……
-
- '''
- ''' 获取某个文件是否被 Windows 防火墙阻止。
- '''
- 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
- '''
- ''' 添加防火墙通行权限。需要管理员权限。
- '''
- 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 "页面切换"
'''
@@ -184,7 +104,6 @@ Public Class PageLinkLeft
PageLinkHiper.ModuleStopManually()
End Sub
Private Sub BtnIoiStop_Loaded(sender As Object, e As RoutedEventArgs)
- sender.Visibility = If(PageLinkIoi.InitLoader.State = LoadState.Finished, Visibility.Visible, Visibility.Collapsed)
End Sub
Private Sub BtnIoiStop_Click(sender As Object, e As EventArgs)
PageLinkIoi.ModuleStopManually()
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
index bd902d3..33b44fe 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupLaunch.xaml
@@ -79,7 +79,7 @@
-
+
@@ -235,7 +235,7 @@
+ ToolTip="在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。
可以使用以下替换标记实现相对路径(路径均以 \ 结尾):
· {path}:PCL 的 exe 文件所在的文件夹
· {minecraft}:.minecraft 文件夹
· {verpath}:版本文件夹(.minecraft\versions\版本名\)
· {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹
· {java}:游戏运行时的 Java 文件夹
除此之外,也支持以下替换标记:
· {user}:玩家名字
· {login}:玩家的登录方式
· {uuid}:玩家的 UUID
· {name}:游戏版本名
· {date}、{time}:当前的系统时间
· {version}:游戏对应的原版版本号
例如:
· "{verpath}test.exe" :运行版本文件夹下的 test.exe 程序
· "{java}java.exe" -jar "{verpath}test.jar" :用 Java 运行版本文件夹下的 test.jar
· notepad "{verindie}option.txt" :使用记事本打开该版本的设置文件
涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。
执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
index 134ffaa..f235841 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml
@@ -26,6 +26,7 @@
+
@@ -55,6 +56,9 @@
+
diff --git a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb
index e610c79..14dbfef 100644
--- a/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageSetup/PageSetupSystem.xaml.vb
@@ -58,6 +58,7 @@
ComboDownloadTranslate.SelectedIndex = Setup.Get("ToolDownloadTranslate")
CheckDownloadKeepModpack.Checked = Setup.Get("ToolDownloadKeepModpack")
CheckDownloadIgnoreQuilt.Checked = Setup.Get("ToolDownloadIgnoreQuilt")
+ CheckDownloadCert.Checked = Setup.Get("ToolDownloadCert")
'Minecraft 更新提示
CheckUpdateRelease.Checked = Setup.Get("ToolUpdateRelease")
@@ -88,6 +89,7 @@
Setup.Reset("ToolDownloadTranslate")
Setup.Reset("ToolDownloadKeepModpack")
Setup.Reset("ToolDownloadIgnoreQuilt")
+ Setup.Reset("ToolDownloadCert")
Setup.Reset("ToolUpdateRelease")
Setup.Reset("ToolUpdateSnapshot")
Setup.Reset("ToolHelpChinese")
@@ -109,7 +111,7 @@
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)
End Sub
Private Shared Sub SliderChange(sender As MySlider, e As Object) Handles SliderDebugAnim.Change, SliderDownloadThread.Change, SliderDownloadSpeed.Change
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
index f9e8f09..5888762 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionMod.xaml.vb
@@ -1,4 +1,6 @@
-Public Class PageVersionMod
+Imports System.Security.Principal
+
+Public Class PageVersionMod
#Region "初始化"
@@ -65,12 +67,12 @@
PanBack.Visibility = Visibility.Collapsed
Exit Sub
End If
- SearchBox.Text = ""
'输出结果
ModItems.Clear()
For Each ModEntity As McMod In Mods
ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
Next
+ SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
RefreshResult(Mods)
Catch ex As Exception
Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
@@ -333,20 +335,33 @@
End Sub
Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
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
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
'启用
- NewPath = ModEntity.Path.Replace(".disabled", "").Replace(".old", "")
+ NewPath = ModEntity.RawPath
Else
Continue For
End If
'重命名
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)
FileSystem.Rename(ModEntity.Path, NewPath)
Catch ex As FileNotFoundException
@@ -389,6 +404,16 @@
Try
Dim IsSuccessful As Boolean = True
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
'删除
Try
@@ -410,8 +435,8 @@
'更改 Loader 和 UI 中的列表
McModLoader.Output.Remove(ModEntity)
ModItems.Remove(ModEntity.RawFileName)
- Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).First(Function(i) i.Entry Is ModEntity))
- PanList.Children.RemoveAt(IndexOfUi)
+ Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
+ If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
Next
RefreshTitle()
If Not IsSuccessful Then
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml
index 7555e49..e97baf5 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml
@@ -26,7 +26,7 @@
-
+
@@ -168,10 +168,8 @@
-
-
@@ -199,7 +197,7 @@
+ ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行版本设置的命令。
在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。
可以使用以下替换标记实现相对路径(路径均以 \ 结尾):
· {path}:PCL 的 exe 文件所在的文件夹
· {minecraft}:.minecraft 文件夹
· {verpath}:版本文件夹(.minecraft\versions\版本名\)
· {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹
· {java}:游戏运行时的 Java 文件夹
除此之外,也支持以下替换标记:
· {user}:玩家名字
· {login}:玩家的登录方式
· {uuid}:玩家的 UUID
· {name}:游戏版本名
· {date}、{time}:当前的系统时间
· {version}:游戏对应的原版版本号
例如:
· "{verpath}test.exe" :运行版本文件夹下的 test.exe 程序
· "{java}java.exe" -jar "{verpath}test.jar" :用 Java 运行版本文件夹下的 test.jar
· notepad "{verindie}option.txt" :使用记事本打开该版本的设置文件
涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。
执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />
diff --git a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
index 187fd03..b54cc26 100644
--- a/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
+++ b/Plain Craft Launcher 2/Pages/PageVersion/PageVersionSetup.xaml.vb
@@ -121,9 +121,10 @@
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
If AniControlEnabled = 0 Then
- Dim HandledText As String = sender.Text
- If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/")
- Setup.Set(sender.Tag, HandledText, Version:=PageVersionLeft.Version)
+ '#3194,不能删减 /
+ 'Dim HandledText As String = sender.Text
+ 'If sender.Tag = "VersionServerAuthServer" OrElse sender.Tag = "VersionServerAuthRegister" Then HandledText = HandledText.TrimEnd("/")
+ Setup.Set(sender.Tag, sender.Text, Version:=PageVersionLeft.Version)
End If
End Sub
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
OpenWebsite("https://login.mc-user.com:233/server/intro")
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
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
- MyMsgBox("即将把第三方登录设置覆盖为自动使用 LittleSkin 登录,是否继续?", "覆盖确认", "确定", "取消") = 2 Then Exit Sub
+ MyMsgBox("即将把第三方登录设置覆盖为 LittleSkin 登录。" & vbCrLf & "除非你是服主,或者服主要求你这样做,否则请不要继续。" & vbCrLf & vbCrLf & "是否确实需要覆盖当前设置?",
+ "设置覆盖确认", "继续", "取消") = 2 Then Exit Sub
TextServerAuthServer.Text = "https://littleskin.cn/api/yggdrasil"
TextServerAuthRegister.Text = "https://littleskin.cn/auth/register"
TextServerAuthName.Text = "LittleSkin 登录"
diff --git a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
index 20b8ebf..9eb1b72 100644
--- a/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
+++ b/Plain Craft Launcher 2/Plain Craft Launcher 2.vbproj
@@ -838,9 +838,6 @@
-
-
-
diff --git a/Plain Craft Launcher 2/Resources/Custom.xaml b/Plain Craft Launcher 2/Resources/Custom.xaml
index 3502147..f35be1d 100644
--- a/Plain Craft Launcher 2/Resources/Custom.xaml
+++ b/Plain Craft Launcher 2/Resources/Custom.xaml
@@ -30,7 +30,7 @@
Text="任意项目都可以添加 Width 与 Height 属性来控制宽高。HorizontalAlignment 属性可以控制对齐:Center 代表居中,Right 代表居右,例如这段文本就被居右了。" />
-
+
@@ -58,7 +58,7 @@
+ Text="打开 Minecraft Wiki" EventType="打开网页" EventData="https://zh.minecraft.wiki/" />
+ Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
+ Text="下载百度的 Logo" EventType="下载文件" EventData="https://www.baidu.com/img/flexible/logo/pc/result.png" />
@@ -227,7 +227,7 @@
-
diff --git a/Plain Craft Launcher 2/Resources/Help.zip b/Plain Craft Launcher 2/Resources/Help.zip
index 23362398a84faad7577517ac242411f10073db96..5a3fbe3fd5b02f22f27ecc22ebd76202cd722591 100644
GIT binary patch
delta 31432
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