Files
Gitcloned-PCL-CE/Plain Craft Launcher 2/Modules/ModLink.vb
2025-11-02 09:56:52 +08:00

283 lines
15 KiB
VB.net
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Imports System.Runtime.InteropServices
Imports PCL.Core.IO
Imports PCL.Core.Link
Imports PCL.Core.Link.EasyTier
Imports PCL.Core.Link.Lobby
Imports PCL.Core.Link.Natayark.NatayarkProfileManager
Imports PCL.Core.Utils.OS
Public Module ModLink
#Region "端口查找"
Public Class PortFinder
' 定义需要的结构和常量
<StructLayout(LayoutKind.Sequential)>
Public Structure MIB_TCPROW_OWNER_PID
Public dwState As Integer
Public dwLocalAddr As Integer
Public dwLocalPort As Integer
Public dwRemoteAddr As Integer
Public dwRemotePort As Integer
Public dwOwningPid As Integer
End Structure
<DllImport("iphlpapi.dll", SetLastError:=True)>
Public Shared Function GetExtendedTcpTable(
ByVal pTcpTable As IntPtr,
ByRef dwOutBufLen As Integer,
ByVal bOrder As Boolean,
ByVal ulAf As Integer,
ByVal TableClass As Integer,
ByVal reserved As Integer) As Integer
End Function
Public Shared Function GetProcessPort(ByVal dwProcessId As Integer) As List(Of Integer)
Dim ports As New List(Of Integer)
Dim tcpTable As IntPtr = IntPtr.Zero
Dim dwSize As Integer = 0
Dim dwRetVal As Integer
If dwProcessId = 0 Then
Return ports
End If
dwRetVal = GetExtendedTcpTable(IntPtr.Zero, dwSize, True, 2, 3, 0)
If dwRetVal <> 0 AndAlso dwRetVal <> 122 Then ' 122 表示缓冲区不足
Return ports
End If
tcpTable = Marshal.AllocHGlobal(dwSize)
Try
If GetExtendedTcpTable(tcpTable, dwSize, True, 2, 3, 0) <> 0 Then
Return ports
End If
Dim tablePtr As IntPtr = tcpTable
Dim dwNumEntries As Integer = Marshal.ReadInt32(tablePtr)
tablePtr = IntPtr.Add(tablePtr, 4)
For i As Integer = 0 To dwNumEntries - 1
Dim row As MIB_TCPROW_OWNER_PID = Marshal.PtrToStructure(Of MIB_TCPROW_OWNER_PID)(tablePtr)
If row.dwOwningPid = dwProcessId Then
ports.Add(row.dwLocalPort >> 8 Or (row.dwLocalPort And &HFF) << 8) ' 转换端口号
End If
tablePtr = IntPtr.Add(tablePtr, Marshal.SizeOf(Of MIB_TCPROW_OWNER_PID)())
Next
Finally
Marshal.FreeHGlobal(tcpTable)
End Try
Return ports
End Function
End Class
#End Region
#Region "Minecraft 实例探测"
Public Async Function MCInstanceFinding() As Task(Of List(Of Tuple(Of Integer, McPingResult, String)))
'Java 进程 PID 查询
Dim PIDLookupResult As New List(Of String)
Dim JavaNames As New List(Of String)
JavaNames.Add("java")
JavaNames.Add("javaw")
For Each TargetJava In JavaNames
Dim JavaProcesses As Process() = Process.GetProcessesByName(TargetJava)
Log($"[MCDetect] 找到 {TargetJava} 进程 {JavaProcesses.Length} 个")
If JavaProcesses Is Nothing OrElse JavaProcesses.Length = 0 Then
Continue For
Else
For Each p In JavaProcesses
Log("[MCDetect] 检测到 Java 进程PID: " + p.Id.ToString())
PIDLookupResult.Add(p.Id.ToString())
Next
End If
Next
Dim res As New List(Of Tuple(Of Integer, McPingResult, String))
Try
If Not PIDLookupResult.Any Then Return res
Dim lookupList As New List(Of Tuple(Of Integer, Integer))
For Each pid In PIDLookupResult
Dim infos As New List(Of Tuple(Of Integer, Integer))
Dim ports = PortFinder.GetProcessPort(Integer.Parse(pid))
For Each port In ports
infos.Add(New Tuple(Of Integer, Integer)(port, pid))
Next
lookupList.AddRange(infos)
Next
Log($"[MCDetect] 获取到端口数量 {lookupList.Count}")
'并行查找本地,超时 3s 自动放弃
Dim checkTasks = lookupList.Select(Function(lookup) Task.Run(Async Function()
Log($"[MCDetect] 找到疑似端口,开始验证:{lookup}")
Using test As New McPing("127.0.0.1", lookup.Item1, 3000)
Dim info As McPingResult
Try
info = Await test.PingAsync()
Dim launcher = GetLauncherBrand(lookup.Item2)
If Not String.IsNullOrWhiteSpace(info.Version.Name) Then
Log($"[MCDetect] 端口 {lookup} 为有效 Minecraft 世界")
res.Add(New Tuple(Of Integer, McPingResult, String)(lookup.Item1, info, launcher))
Return
End If
Catch ex As Exception
If TypeOf ex.InnerException Is ObjectDisposedException Then
Log($"[McDetect] {lookup} 验证超时,已强制断开连接,将尝试旧版检测")
Else
Log(ex, $"[McDetect] {lookup} 验证出错,将尝试旧版检测")
End If
End Try
Try
info = Await test.PingOldAsync()
If Not String.IsNullOrWhiteSpace(info.Version.Name) Then
Log($"[MCDetect] 端口 {lookup} 为有效 Minecraft 世界")
res.Add(New Tuple(Of Integer, McPingResult, String)(lookup.Item1, info, String.Empty))
Return
End If
Catch ex As Exception
If TypeOf ex.InnerException Is ObjectDisposedException Then
Log($"[McDetect] {lookup} 验证超时,已强制断开连接")
Else
Log(ex, $"[McDetect] {lookup} 验证出错")
End If
End Try
End Using
End Function)).ToArray()
Await Task.WhenAll(checkTasks)
Catch ex As Exception
Log(ex, "[MCDetect] 获取端口信息错误", LogLevel.Debug)
End Try
Return res
End Function
Public Function GetLauncherBrand(pid As Integer) As String
Try
Dim cmd = ProcessInterop.GetCommandLine(pid)
If cmd.Contains("-Dminecraft.launcher.brand=") Then
Return cmd.AfterFirst("-Dminecraft.launcher.brand=").BeforeFirst("-").TrimEnd("'", " ")
Else
Return cmd.AfterFirst("--versionType ").BeforeFirst("-").TrimEnd("'", " ")
End If
Catch ex As Exception
Log(ex, $"[MCDetect] 检测 PID {pid} 进程的启动参数失败")
Return ""
End Try
End Function
#End Region
#Region "EasyTier"
Public DlEasyTierLoader As LoaderCombo(Of JObject) = Nothing
Public Function DownloadEasyTier()
Dim dlTargetPath As String = PathTemp + $"EasyTier\EasyTier-{ETInfoProvider.ETVersion}.zip"
RunInNewThread(Sub()
Try
'构造步骤加载器
Dim loaders As New List(Of LoaderBase)
'下载
Dim address As New List(Of String)
address.Add($"https://staticassets.naids.com/resources/pclce/static/easytier/easytier-windows-{If(IsArm64System, "arm64", "x86_64")}-v{ETInfoProvider.ETVersion}.zip")
address.Add($"https://s3.pysio.online/pcl2-ce/static/easytier/easytier-windows-{If(IsArm64System, "arm64", "x86_64")}-v{ETInfoProvider.ETVersion}.zip")
loaders.Add(New LoaderDownload("下载 EasyTier", New List(Of NetFile) From {New NetFile(address.ToArray, dlTargetPath, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
loaders.Add(New LoaderTask(Of Integer, Integer)("解压文件", Sub() ExtractFile(dlTargetPath, IO.Path.Combine(FileService.LocalDataPath, "EasyTier", ETInfoProvider.ETVersion))) With {.Block = True})
loaders.Add(New LoaderTask(Of Integer, Integer)("清理缓存与冗余组件", Sub()
File.Delete(dlTargetPath)
CleanupEasyTierCache()
End Sub))
loaders.Add(New LoaderTask(Of Integer, Integer)("刷新界面", Sub() Hint("联机组件下载完成!", HintType.Finish)) With {.Show = False})
'启动
DlEasyTierLoader = New LoaderCombo(Of JObject)("大厅初始化", loaders)
DlEasyTierLoader.Start()
LoaderTaskbarAdd(DlEasyTierLoader)
FrmMain.BtnExtraDownload.ShowRefresh()
FrmMain.BtnExtraDownload.Ribble()
Catch ex As Exception
Log(ex, "[Link] 下载 EasyTier 依赖文件失败", LogLevel.Hint)
Hint("下载 EasyTier 依赖文件失败,请检查网络连接", HintType.Critical)
End Try
End Sub)
Return 0
End Function
Private Sub CleanupEasyTierCache()
Dim subDirs As String() = Directory.GetDirectories(IO.Path.Combine(FileService.LocalDataPath, "EasyTier"))
For Each folderPath As String In subDirs
Dim name As String = IO.Path.GetFileName(folderPath)
If Not name.Equals(ETInfoProvider.ETVersion) Then
Try
Directory.Delete(folderPath, True)
Catch ex As Exception
Log(ex, "[Link] 清理旧版本 EasyTier 出错")
End Try
End If
Next
End Sub
#End Region
#Region "大厅操作"
Public Function LobbyPrecheck() As Boolean
If Not LobbyInfoProvider.IsLobbyAvailable Then
Hint("大厅功能暂不可用,请稍后再试", HintType.Critical)
Return False
End If
If SelectedProfile IsNot Nothing Then
If SelectedProfile.Username.Contains("|") Then
Hint("MC 玩家 ID 不可包含分隔符 (|) ")
Return False
End If
End If
If LobbyInfoProvider.RequiresLogin Then
If String.IsNullOrWhiteSpace(Setup.Get("LinkNaidRefreshToken")) Then
Hint("请先前往联机设置并登录至 Natayark Network 再进行联机!", HintType.Critical)
Return False
End If
Try
GetNaidData(Setup.Get("LinkNaidRefreshToken"), True)
Catch ex As Exception
Log("[Link] 刷新 Natayark ID 信息失败,需要重新登录")
Hint("请重新登录 Natayark Network 账号再试!", HintType.Critical)
Return False
End Try
Dim waitCount As Integer = 0
While String.IsNullOrWhiteSpace(NaidProfile.Username)
If waitCount > 30 Then Exit While
Thread.Sleep(500)
waitCount += 1
End While
If String.IsNullOrWhiteSpace(NaidProfile.Username) Then
Hint("尝试获取 Natayark ID 信息失败", HintType.Critical)
Return False
End If
If LobbyInfoProvider.RequiresRealName AndAlso Not NaidProfile.IsRealNamed Then
Hint("请先前往 Natayark 账户中心进行实名验证再尝试操作!", HintType.Critical)
Return False
End If
If Not NaidProfile.Status = 0 Then
Hint("你的 Natayark Network 账号状态异常,可能已被封禁!", HintType.Critical)
Return False
End If
End If
If String.IsNullOrWhiteSpace(Setup.Get("LinkUsername")) AndAlso String.IsNullOrWhiteSpace(NaidProfile.Username) Then
Hint("请先前往设置输入一个用户名,或登录至 Natayark Network 再进行联机!", HintType.Critical)
Return False
End If
If ETController.Precheck() = 1 Then
Hint("正在下载联机依赖组件,请稍后...")
DownloadEasyTier()
Return False
End If
If DlEasyTierLoader IsNot Nothing Then
If DlEasyTierLoader.State = LoadState.Loading Then
Hint("EasyTier 尚未下载完成,请等待其下载完成后再试!")
Return False
ElseIf DlEasyTierLoader.State = LoadState.Failed OrElse DlEasyTierLoader.State = LoadState.Aborted Then
Hint("正在下载 EasyTier请稍后...")
DownloadEasyTier()
Return False
End If
End If
Return True
End Function
#End Region
End Module