Imports Ionic.Zip Public Module modMain #Region "声明" '常量 Public Const VERSION_NAME As String = "1.0.9" Public Const VERSION_CODE As Integer = 52 Public Const MC_VERSION_CODE As Integer = 7 Public Const PUSH_VERSION_CODE As Integer = 2 Public Const MAINFORM_HEIGHT As Integer = 435 Public Const MAINFORM_WIDTH As Integer = 760 Public Const MAINFORM_NAME As String = "Plain Craft Launcher" Public Const APPLICATION_SHORT_NAME As String = "PCL" Public Const APPLICATION_FULL_NAME As String = "Plain Craft Launcher" Public Const DOWNLOADING_END As String = ".PCLdownloading" Public Const TX_SREVER_1 As String = "http://pcl-1253424809.cosgz.myqcloud.com/" Public Const TX_SREVER_2 As String = "http://pcl-1254159202.costj.myqcloud.com/" Public Const QN_SERVER As String = "http://op7rrgq1p.bkt.clouddn.com/" Public MODE_DEBUG As Boolean = False Public MODE_OFFLINE As Boolean = False Public MODE_DEVELOPER As Boolean = False '各种路径 ''' ''' 程序内嵌图片文件夹路径。 ''' ''' Public PATH_IMAGE As String = "pack://application:,,,/images/" ''' ''' 用户设置的下载文件夹路径。 ''' ''' Public PATH_DOWNLOAD As String ''' ''' Java路径。不包含“javaw.exe”,不以“\”结尾。 ''' ''' Public PATH_JAVA As String ''' ''' .minecraft 文件夹路径。以“\”结尾。 ''' ''' Public PATH_MC As String '窗体 Public frmStart As formStart Public frmMain As New formMain Public frmHomeLeft As New formHomeLeft Public frmHomeRight As New formHomeRight Public frmDownloadLeft As New formDownloadLeft Public frmDownloadRight As New formDownloadRight Public frmSetup As formSetup '尚未使用的窗体 Public frmManageLeft As formManageLeft Public frmManageRight As formManageLeft Public frmHelpLeft As formHelpLeft Public frmHelpRight As formHelpLeft '通用信息 ''' ''' 是否正在显示蓝屏。 ''' ''' Public IsShowingDeathBlue As Boolean = False ''' ''' 是否启用基础控件动画。 ''' ''' Public UseControlAnimation As Boolean = False ''' ''' 程序加载耗时的计时器。 ''' ''' Public LoadTimeCost As Integer ''' ''' 正版登录结果。可以通过判断它是否为空来确定正版登录是否成功。 ''' ''' Public LoginResult As String = "" ''' ''' 所有Minecraft版本的列表。 ''' ''' Public VersionsList As New Dictionary(Of VersionSwapState, ArrayList) From {{VersionSwapState.UNKNOWN, New ArrayList}, {VersionSwapState.NORMAL, New ArrayList}, {VersionSwapState.SWAP, New ArrayList}, {VersionSwapState.OLD, New ArrayList}, {VersionSwapState.WRONG, New ArrayList}} Private _PathEnv As String = "" ''' ''' Path环境变量。 ''' ''' ''' ''' Public Property PathEnv As String Get If _PathEnv = "" Then _PathEnv = Environment.GetEnvironmentVariable("Path") Return _PathEnv End Get Set(ByVal value As String) _PathEnv = PathEnv End Set End Property ''' ''' 是否正在播放音乐。 ''' ''' Public IsPlayingMusic As Boolean = False ''' ''' 是否允许收集操作信息。 ''' ''' Public AllowFeedback As Boolean = True #End Region #Region "枚举" ''' ''' 加载状态,如加载中、成功、失败等。 ''' ''' Public Enum LoadState As Byte ''' ''' 尚未开始加载,正在等待 ''' ''' Waiting = 0 ''' ''' 正在加载中 ''' ''' Loading = 1 ''' ''' 加载成功 ''' ''' Loaded = 2 ''' ''' 加载失败 ''' ''' Failed = 3 End Enum ''' ''' 提示信息种类,如警告、完成、错误等。 ''' ''' Public Enum HintState As Integer ''' ''' 信息,通常是蓝色的“i”。 ''' ''' Info = 0 ''' ''' 已完成,通常是绿色的“√”。 ''' ''' Finish = 1 ''' ''' 警告,通常是黄色的“!”。 ''' ''' Warn = 2 ''' ''' 错误,通常是红色的“×”。 ''' ''' Critical = 3 End Enum ''' ''' Minecraft登陆方式。 ''' ''' Public Enum LoginMethods As Integer ''' ''' 离线。 ''' ''' Legacy = 0 ''' ''' 正版登录。 ''' ''' Mojang = 1 ''' ''' 未知。这用来标记未加载时的状态。 ''' ''' Unknown = 2 End Enum ''' ''' Minecraft版本检查结果。 ''' ''' Public Enum VersionCheckState As Byte ''' ''' 尚未检查。 ''' ''' NOT_LOAD = 0 ''' ''' 已经检查,没有问题。 ''' ''' NO_PROBLEM = 1 ''' ''' 文件夹 或 Json与Jar 均不存在。 ''' ''' EMPTY_FOLDER = 2 ''' ''' Json不存在,但是Jar存在。 ''' ''' JSON_NOT_EXIST = 3 ''' ''' Json存在但是无法读取。 ''' ''' JSON_CANT_READ = 4 ''' ''' 依赖版本文件夹不存在。 ''' ''' INHERITS_NOT_EXIST = 5 ''' ''' 依赖版本检查出错。 ''' ''' INHERITS_EXCEPTION = 6 ''' ''' Json存在,但是主要Jar不存在。 ''' ''' JAR_NOT_EXIST = 7 End Enum ''' ''' Minecraft版本类型。 ''' ''' Public Enum MCVersionType As Byte ''' ''' 未获取版本或版本获取失败。 ''' ''' UNKNOWN = 0 ''' ''' 快照和预览版。 ''' ''' SNAPSHOT = 1 ''' ''' 正式版。 ''' ''' RELEASE = 2 ''' ''' 愚人节版。 ''' ''' FOOL = 6 ''' ''' OptiFine版。 ''' ''' OPTIFINE = 3 ''' ''' Forge版。 ''' ''' FORGE = 4 ''' ''' 发布时间在2012年及更早的版本。 ''' ''' OLD = 5 End Enum ''' ''' 版本折叠分类。 ''' ''' Public Enum VersionSwapState As Byte ''' ''' 尚未确认。 ''' ''' UNKNOWN = 255 ''' ''' 正常,无需折叠。 ''' ''' NORMAL = 0 ''' ''' 折叠。 ''' ''' SWAP = 1 ''' ''' 老版本。 ''' ''' OLD = 2 ''' ''' 错误的版本。 ''' ''' WRONG = 3 End Enum #End Region #Region "类" ''' ''' 提示信息种类转换。 ''' ''' Public Class HintConverter ''' ''' 提示信息文本。 ''' ''' Public Text As String ''' ''' 提示信息种类。 ''' ''' Public Type As HintState = 0 Public Sub New(ByVal Text As String) Me.Text = Text End Sub Public Sub New(ByVal Text As String, ByVal Type As HintState) Me.Text = Text Me.Type = Type End Sub Public Shared Widening Operator CType(ByVal Text As String) As HintConverter Return New HintConverter(Text) End Operator Public Shared Widening Operator CType(ByVal Conv As HintConverter) As Color Select Case Conv.Type Case HintState.Info Return Color.FromRgb(26, 148, 252) Case HintState.Finish Return Color.FromRgb(29, 160, 29) Case HintState.Warn Return Color.FromRgb(216, 137, 8) Case Else Return Color.FromRgb(255, 46, 0) End Select End Operator Public Shadows Function ToString() As String Return "[" & {"Hint", "Finish", "Warn", "Critical"}(Type) & "] " & Text End Function ''' ''' 获取目前提示种类的英文名。 ''' ''' 提示种类的英文名,如“Critical”。 ''' Public Function GetTypeName() As String Return HintState.GetName(Type.GetType, Type) End Function End Class ''' ''' 一个不包含UI支持库的Minecraft版本类。 ''' ''' Public Class MCVersion Implements IComparable(Of MCVersion) ''' ''' 这些版本信息是否都是从已经配置好的文件中读取的。 ''' ''' Public LoadedByFile As Boolean = True ''' ''' 版本折叠分类。 ''' ''' Public SwapType As VersionSwapState = VersionSwapState.UNKNOWN ''' ''' 对应的Minecraft版本。这与Forge等无关。 ''' ''' Public Version As String = "未知" ''' ''' 版本的发布时间。用于鉴别版本号。 ''' ''' Public ReleaseTime As String = "1970-01-01T00:00:00" ''' ''' 主版本号(如1.9.2的9)。快照版为0。 ''' ''' Public MainVersionCode As Integer = 0 ''' ''' Minecraft版本种类。 ''' ''' Public Type As MCVersionType = MCVersionType.UNKNOWN ''' ''' Logo的内容。用于ListItem显示。 ''' ''' Public Logo As String = PATH_IMAGE & "Block-Grass.png" ''' ''' 描述内容。用于ListItem显示。 ''' ''' Public Description As String = "" ''' ''' 版本检查结果。在检查版本时完成。 ''' ''' Public VersionCheckResult As VersionCheckState = VersionCheckState.NOT_LOAD ''' ''' 该版本的依赖版本。在检查版本时完成。 ''' ''' Public InheritVersion As String = "" ''' ''' 该版本的资源文件号。在检查版本时完成。 ''' ''' Public Assets As String '自处理属性 ''' ''' 文件夹名称,或者说是这个版本的名称。 ''' ''' Public Name As String Private _Json As JObject ''' ''' Json对象。 ''' ''' ''' ''' Public ReadOnly Property Json As JObject Get If IsNothing(_Json) Then ReadJson() Return If(_JsonText = "ERROR", "", _Json) End Get End Property Private _JsonText As String = "" ''' ''' Json文件的内容。 ''' ''' Public ReadOnly Property JsonText As String Get If _JsonText = "" Then ReadJson() Return If(_JsonText = "ERROR", "", _JsonText) End Get End Property ''' ''' 完整的文件夹地址。以“\”结尾。 ''' ''' Public ReadOnly Property Path As String Get If Name = "" Then Throw New Exception("没有指定本版本的文件夹。") Return PATH_MC & "versions\" & Name & "\" End Get End Property '事件 Public Sub New(ByVal FolderName As String) Name = FolderName End Sub Private Sub ReadJson() '读取Json For Each Encode As Encoding In {Encoding.Default, Encoding.Unicode, New UTF8Encoding(False), Encoding.ASCII} Try _JsonText = ReadFileToEnd(Path & Name & ".json", Encode) If _JsonText = "" Then GoTo Fail _Json = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(_JsonText), JObject) Exit Sub Catch '编码错误 _JsonText = "ERROR" End Try NextFormat: Next Fail: '各种编码都没戏了 log("[System] Json文件读取失败:" & Path & Name & ".json", True) Throw New FileFormatException("Json文件读取失败:" & Path & Name & ".json") End Sub Public Overrides Function ToString() As String Return Name & " / " & GetStringFromEnum(SwapType) & If(Version = Name, "", " / " & Version) & If(VersionCheckResult = VersionCheckState.NO_PROBLEM, "", " / " & GetStringFromEnum(VersionCheckResult)) End Function Public Overloads Function CompareTo(ByVal Other As MCVersion) As Integer Implements IComparable(Of MCVersion).CompareTo If IsNothing(Other) Then Return 1 Return String.Compare(Me.Name, Other.Name) End Function End Class #End Region #Region "登录" ''' ''' 进行Login方式的正版登录。 ''' ''' 邮箱地址。 ''' 明文密码。 ''' ''' Public Function OnlineLogin(ByVal Email As String, ByVal Pass As String) As String '没联网登录个毛哦 If MODE_OFFLINE Then Throw New WebException("没有网络连接,无法登录!") '尝试登录 Try log("[System] 正版登录(Login):" & Email) Dim request As WebRequest = WebRequest.Create("https://authserver.mojang.com/authenticate") request.Method = "POST" Dim postData As String = "{""agent"": {""name"": ""Minecraft"",""version"": 1},""username"":""" & Email & """,""password"":""" & Pass & """,""requestUser"":true}" Dim byteArray As Byte() = New UTF8Encoding(False).GetBytes(postData) request.ContentType = "application/json; charset=utf-8" request.ContentLength = byteArray.Length request.Timeout = 20000 Dim dataStream As Stream = request.GetRequestStream() dataStream.Write(byteArray, 0, byteArray.Length) dataStream.Close() Dim response As WebResponse = request.GetResponse() dataStream = response.GetResponseStream() Dim reader As New StreamReader(dataStream) Dim responseFromServer As String = reader.ReadToEnd() OnlineLogin = responseFromServer '输出信息 Dim LoginJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(OnlineLogin), JObject) WriteReg("AccessToken", SerAdd(LoginJson("accessToken").ToString)) WriteReg("ClientToken", SerAdd(LoginJson("clientToken").ToString)) WriteReg("MojangPlayerName", LoginJson("selectedProfile")("name").ToString) WriteReg("MojangPlayerUUID", LoginJson("selectedProfile")("id").ToString) log("[System] 正版登录成功") reader.Close() dataStream.Dispose() response.Close() Catch ex As Exception ExShow(ex, "正版登录失败", ErrorLevel.Slient) If ex.Message.Contains("403") Then Throw New WebException("正版登录失败,请检查你的邮箱和密码是否正确或等待5分钟再试!") Else Throw New WebException("正版登录失败!" & vbCrLf & "详细的错误信息:" & ex.Message) End If End Try End Function ''' ''' 进行Refresh方式的正版登录。 ''' ''' 在Login登录中存储的AccessToken。 ''' 在Login登录中存储的ClientToken。 ''' ''' Public Function RefreshLogin(ByVal AccessToken As String, ByVal ClientToken As String) As String '没联网登录个毛哦 If MODE_OFFLINE Then Throw New WebException("没有网络连接,无法登录!") '尝试登录 Try log("[System] 刷新正版登录") Dim request As WebRequest = WebRequest.Create("https://authserver.mojang.com/refresh") request.Method = "POST" Dim postData As String = "{""accessToken"":""" & AccessToken & """,""clientToken"":""" & ClientToken & """}" Dim byteArray As Byte() = New UTF8Encoding(False).GetBytes(postData) request.ContentType = "application/json; charset=utf-8" request.ContentLength = byteArray.Length request.Timeout = 20000 Dim dataStream As Stream = request.GetRequestStream() dataStream.Write(byteArray, 0, byteArray.Length) dataStream.Close() Dim response As WebResponse = request.GetResponse() dataStream = response.GetResponseStream() Dim reader As New StreamReader(dataStream) Dim responseFromServer As String = reader.ReadToEnd() RefreshLogin = responseFromServer '输出信息 Dim LoginJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(RefreshLogin), JObject) WriteReg("AccessToken", SerAdd(LoginJson("accessToken").ToString)) WriteReg("ClientToken", SerAdd(LoginJson("clientToken").ToString)) WriteReg("MojangPlayerName", LoginJson("selectedProfile")("name").ToString) WriteReg("MojangPlayerUUID", LoginJson("selectedProfile")("id").ToString) log("[System] 刷新正版登录成功") reader.Close() dataStream.Dispose() response.Close() Catch ex As Exception ExShow(ex, "刷新正版登录失败", ErrorLevel.Slient) Throw New WebException("刷新正版登录失败!" & vbCrLf & "详细的错误信息:" & ex.Message) End Try End Function #End Region #Region "线程池" Public PoolCount As Integer = 0 '目前运行中的线程计数 Public Const POOL_MAXCOUNT As Integer = 15 '最大线程数 Public Pool As New ArrayList '线程池 Public Sub PoolLoader() On Error Resume Next Do While True If Pool.Count > 0 Then ReSearch: For i = 0 To Pool.Count - 1 If IsNothing(Pool(i)) Then Pool.RemoveAt(i) Exit For End If Select Case Pool(i).ThreadState Case System.Threading.ThreadState.Unstarted If PoolCount < POOL_MAXCOUNT Then '线程尚未启动 Pool(i).Start() PoolCount = PoolCount + 1 End If Case System.Threading.ThreadState.Stopped, System.Threading.ThreadState.Aborted '线程已经停止 PoolCount = PoolCount - 1 Pool.RemoveAt(i) i = i - 1 End Select If i >= Pool.Count - 1 Then Exit For Next i End If Thread.Sleep(20) Loop End Sub ''' ''' 加载线程:Java文件夹检测。它会检测PATH_JAVA的值,如果无Java则初始化。 ''' ''' Public Sub PoolJavaFolder(IsJustFind As Boolean) '初始化 PATH_JAVA = If(IsJustFind, "", ReadReg("SetupJavaPath")) Try '如果注册表已经存在 Java 的正确路径就结束 Dim Env As String = PathEnv If File.Exists(PATH_JAVA & "\javaw.exe") And (File.Exists(PATH_JAVA & "\jli.dll") Or PATH_JAVA.Contains("javapath")) Then log("[Pool] Java 路径:" & PATH_JAVA) Exit Sub End If '检查环境变量中的 Java Dim EachPath As String() = Split(Env, ";") For Each PathFind As String In EachPath '格式化字符串,保证不以“\”结尾 If PathFind.EndsWith("\") Then PathFind = Left(PathFind, Len(PathFind) - 1) '检查有效性 If File.Exists(PathFind & "\javaw.exe") And (File.Exists(PathFind & "\jli.dll") Or PathFind.Contains("javapath")) Then PATH_JAVA = PathFind log("[Pool] 环境变量中找到 Java:" & PATH_JAVA) WriteReg("SetupJavaPath", PATH_JAVA) Exit Sub End If Next '人工查找 log("[Pool] 遍历寻找 Java 开始") Dim Result As String '循环每个盘 For Each Disk As DriveInfo In DriveInfo.GetDrives() Result = SearchJava(Disk.Name) '如果找到了的话就跳出,并且设置环境变量 If Not Result = "" Then PATH_JAVA = Mid(Result, 1, Result.LastIndexOf("\")) GoTo FinishSearch End If Next '查找当前启动器目录 Result = SearchJava(PATH, True) If Not Result = "" Then PATH_JAVA = Mid(Result, 1, Result.LastIndexOf("\")) GoTo FinishSearch End If '人工查找失败 log("[Pool] 遍历寻找 Java 失败") PATH_JAVA = "" ShowHint(New HintConverter("未找到可用的 Java", HintState.Warn)) Exit Sub Catch ex As Exception ExShow(ex, "查找 Java 时出错", ErrorLevel.MsgboxAndFeedback) End Try FinishSearch: If Not IsJustFind Then SetJavaEnvironment() End Sub ''' ''' 检查指定路径下的文件夹并且模糊搜索Java。这不会搜索全部路径。 ''' ''' 开始搜索的起始路径 ''' 搜索当前文件夹下的全部文件夹(这不会传递到子级文件夹) ''' 搜索到的Java路径,如果失败则为空 ''' Private Function SearchJava(ByVal path As String, Optional ByVal fullSearch As Boolean = False) As String Try SearchJava = "" Dim AllPath() As String Dim LastEntry As String '文件夹或文件名 If Directory.Exists(path) Then '该目录存在 AllPath = Directory.GetFileSystemEntries(path) For Each Entry As String In AllPath LastEntry = Entry.Split("\")(Entry.Split("\").Length - 1) '获取文件夹或文件名 Dim SearchEntry = LastEntry.ToLower.Replace(" ", "") '用于搜索的字符串 If fullSearch Or SearchEntry.Contains("java") Or SearchEntry.Contains("jdk") Or SearchEntry.Contains("jre") Or SearchEntry.Contains("bin") Or SearchEntry.Contains("mc") Or SearchEntry.Contains("minecraft") Or SearchEntry.Contains("program") Or SearchEntry.Contains("我的世界") Or SearchEntry.Contains("net") Or SearchEntry.Contains("runtime") Or SearchEntry.Contains("oracle") Or SearchEntry.Contains("1.") Or SearchEntry.Contains("启") Then If File.Exists(Entry) Then '如果是文件 If LastEntry = "javaw.exe" And File.Exists(Mid(Entry, 1, Entry.LastIndexOf("\")) & "\jli.dll") Then '找到Java SearchJava = Entry Exit For End If ElseIf Directory.Exists(Entry) Then '如果是文件夹 Dim Result As String = SearchJava(Entry) If Not Result = "" Then Return Result End If End If Next End If Catch ex As Exception ExShow(ex, "遍历查找Java时出错", ErrorLevel.Slient) '防止无文件夹权限而导致崩溃 SearchJava = "" End Try End Function ''' ''' 加载线程:Minecraft文件夹检测。 ''' ''' Public Sub PoolMinecraftFolder() Try '等待版本列表加载结束 Do While IsPoolVersionListRunning Thread.Sleep(25) Loop Dim MojangPath As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\.minecraft\" Dim AllFoldersList As New ArrayList '加载可用文件夹地址 Dim AllFolders As String = ReadIni("setup", "LaunchFolders", "") If Not AllFolders = "" Then '如果存在可用文件夹地址,则再次检查它们 Dim NewFolderList As New ArrayList For Each Dir As String In AllFolders.Split("|") If CheckDirectoryPermission(Dir) Then NewFolderList.Add(Dir) AllFoldersList.Add(Dir) End If Next AllFolders = Join(NewFolderList.ToArray, "|") WriteIni("setup", "LaunchFolders", AllFolders) End If '加载设置中的Minecraft文件夹地址 PATH_MC = ReadIni("setup", "LaunchFolderSelect", "") If CheckDirectoryPermission(MojangPath & "versions\") Then If Not Directory.GetDirectories(MojangPath & "versions\").Length = 0 Then AllFoldersList.Add(MojangPath) End If '如果为当前目录则自动创建 If PATH_MC = PATH & ".minecraft\" Then If Not CheckDirectoryPermission(PATH_MC) Then Directory.CreateDirectory(PATH_MC) Directory.CreateDirectory(PATH_MC & "versions\") End If GoTo FinishMinecraftFolderCheck End If '如果在可用文件夹列表且有效则结束 If AllFoldersList.Contains(PATH_MC) And CheckDirectoryPermission(PATH_MC) Then GoTo FinishMinecraftFolderCheck '如果是用户设置的路径,则显示一个通知 If Not PATH_MC = "" Then ShowHint("原先的 Minecraft 文件夹路径已失效:" & PATH_MC) '优先尝试设置为当前文件夹 If CheckDirectoryPermission(PATH & ".minecraft\versions") Then PATH_MC = PATH & ".minecraft\" GoTo FinishMinecraftFolderCheck End If '优先尝试设置为官启文件夹 If AllFoldersList.Contains(MojangPath) Then If MyMsgbox("官方启动器的 Minecraft 文件夹下存在 " & Directory.GetDirectories(MojangPath & "versions").Length & " 个版本,是否使用官方启动器的文件夹?" & vbCrLf & "你可以在设置页面更改这个设置。", "Minecraft 文件夹确认", "确定", "取消") = 1 Then '使用官启文件夹 PATH_MC = MojangPath GoTo FinishMinecraftFolderCheck End If End If AllFoldersList.Remove(MojangPath) '从列表中查找新的目录 For Each Dir As String In AllFolders.Split("|") If CheckDirectoryPermission(Dir) Then PATH_MC = Dir GoTo FinishMinecraftFolderCheck End If Next '执行到这里就需要创建一个新的.minecraft文件夹了 PATH_MC = PATH & ".minecraft\" Directory.CreateDirectory(PATH_MC) Directory.CreateDirectory(PATH_MC & "versions\") FinishMinecraftFolderCheck: log("[Pool] .minecraft文件夹:" & PATH_MC) WriteIni("setup", "LaunchFolderSelect", PATH_MC) '输出启动器信息 If Not File.Exists(PATH_MC & "launcher_profiles.json") Then WriteFile(PATH_MC & "launcher_profiles.json", "{" & vbCrLf & " ""profiles"": {" & vbCrLf & " """ & APPLICATION_SHORT_NAME & """: {" & vbCrLf & " ""name"": """ & APPLICATION_SHORT_NAME & """," & vbCrLf & " }," & vbCrLf & " ""(Default)"": {" & vbCrLf & " ""name"": ""(" & APPLICATION_SHORT_NAME & ")""" & vbCrLf & " }" & vbCrLf & " }," & vbCrLf & " ""selectedProfile"": ""(Default)""," & vbCrLf & " ""clientToken"": ""88888888-8888-8888-8888-888888888888""" & vbCrLf & "}") log("[Pool] 已创建 launcher_profiles.json") End If If Not File.Exists(PATH_MC & "options.txt") Then WriteFile(PATH_MC & "options.txt", "lang:zh_cn" & vbCrLf) log("[Pool] 已创建 options.txt") End If '继续加载版本列表 Pool.Add(New Thread(AddressOf PoolVersionList)) Catch ex As Exception ExShow(ex, "检测MC文件夹失败", ErrorLevel.MsgboxAndFeedback) End Try End Sub Private PoolLoginLock As New Object ''' ''' 加载线程:自动登录。 ''' ''' Public Sub PoolLogin(IsAutoLogin As Boolean) Try If MODE_OFFLINE Then Exit Sub SyncLock PoolLoginLock If (Not ReadReg("Email").Contains("@")) Or (Len(ReadReg("Password")) < 4) Or (IsAutoLogin And ReadReg("HomeSave", "True") = "False") Then GoTo ExitSub frmHomeRight.StartButtonIsLogining = True '尝试登录 If ReadReg("ClientToken") = "" Then PoolLoginRun(New LoginInfo With {.IsRefresh = False, .Email = ReadReg("Email"), .Password = SerRemove(ReadReg("Password"))}, True) Else If Not PoolLoginRun(New LoginInfo With {.IsRefresh = True, .AccessToken = SerRemove(ReadReg("AccessToken")), .ClientToken = SerRemove(ReadReg("ClientToken"))}, False) Then PoolLoginRun(New LoginInfo With {.IsRefresh = False, .Email = ReadReg("Email"), .Password = SerRemove(ReadReg("Password"))}, True) End If End If ExitSub: frmHomeRight.StartButtonIsLogining = False End SyncLock '加载皮肤 If LoginResult.Contains("selectedProfile") Then Dim UUID As String = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(LoginResult), JObject)("selectedProfile")("id").ToString WriteIni("cache\skin\UUID", CType(Newtonsoft.Json.JsonConvert.DeserializeObject(LoginResult), JObject)("selectedProfile")("name").ToString, UUID) Dim SkinAddress As String = DownloadSkin(UUID) If Not SkinAddress = "" Then frmMain.Dispatcher.Invoke(Sub() frmHomeRight.LoadMojangSkin(SkinAddress)) End If Catch ex As Exception frmHomeRight.StartButtonIsLogining = False ExShow(ex, "自动登录出错", ErrorLevel.MsgboxAndFeedback) End Try End Sub ''' ''' 进行正版登录所需的信息包。 ''' ''' Public Structure LoginInfo Public IsRefresh As Boolean Public Email As String Public Password As String Public AccessToken As String Public ClientToken As String End Structure ''' ''' 按照给定的方式进行登录尝试。 ''' ''' Private Function PoolLoginRun(ByVal Info As LoginInfo, ByVal IsShowHint As Boolean) As Boolean '正版登录 Try log("[Poll] 登录开始") If Info.IsRefresh Then LoginResult = RefreshLogin(Info.AccessToken, Info.ClientToken) Else LoginResult = OnlineLogin(Info.Email, Info.Password) End If log("[Poll] 登录成功") Return True Catch ex As Exception ExShow(ex, "登录失败") If IsShowHint Then ShowHint(New HintConverter(GetStringFromException(ex), HintState.Warn)) LoginResult = "" Return False End Try End Function Public NeedUpdate As LoadState = LoadState.Waiting ''' ''' 加载线程:自动更新。 ''' ''' Public Sub PoolUpdate() If MODE_OFFLINE Then NeedUpdate = LoadState.Failed : Exit Sub '更新 Try NeedUpdate = LoadState.Loading Dim data As String = GetWebsiteCode(TX_SREVER_1 & "update" & If(ReadIni("setup", "SysUpdateTest", "False"), "_snapshot", "_release") & ".ini", Encoding.Default) If Not data.Contains("VersionCode") Then Throw New Exception("获取自动更新网页代码失败。") Exit Sub End If WriteFile("PCL\update.ini", data, , , False) '检查更新 Dim newVersionCode As Integer = ReadIni(PATH & "PCL\update.ini", "VersionCode", "0") '为了避免在首次打开时自动更新到 PCL2,这一段暂时注释了 'If newVersionCode > VERSION_CODE Then ' NeedUpdate = LoadState.Loaded ' If ReadIni("setup", "SysUpdate", "True") = "False" Then GoTo NoHint '设置为不检查更新 ' log("[Pool] 发现新版本(" & newVersionCode & ")") ' '更新可用 ' If ReadIni("setup", "SysUpdateHint", "True") Then ' '提示更新 ' frmHomeRight.Dispatcher.Invoke(Sub() frmHomeRight.ShowUpdate("发现启动器更新:" & ReadIni("update", "Version", "未知版本"), formHomeRight.UpdateType.PCL)) ' Else ' '自动开始更新 ' ShowHint("正在自动更新,更新结束后 PCL 将会自动重启") ' Dim th As New Thread(AddressOf DownloadUpdate) ' th.Start() ' End If 'Else NeedUpdate = LoadState.Failed Log("[Pool] 已经是最新版本(" & newVersionCode & ")") 'End If NoHint: Catch ex As Exception NeedUpdate = LoadState.Failed ExShow(ex, "检查更新失败") '更新检测失败 Dim th As New Thread(Sub() log("[Pool] 开始测试服务器 Ping") Try log("[Pool] Ping 测试成功:" & Ping(TX_SREVER_1, 10000) & "ms") Catch exx As Exception ExShow(exx, "无法连接至 PCL 服务器") End Try End Sub) th.Start() End Try '通知 Dim newNotice As Integer = ReadIni(PATH & "PCL\update.ini", "Notice", "0") Dim currentNotice As Integer = ReadIni("setup", "Notice", "0") If newNotice > currentNotice Then Try log("[Pool] 发现公告(" & newNotice & ")") Dim Notices As JObject = ReadJson(GetWebsiteCode(TX_SREVER_1 & "notice.json", Encoding.Default)) For Each Notice As JToken In Notices("all") If Val(Notice("id")) > currentNotice Then '确认要求 For Each Require As JToken In If(Notice("require"), {}) Select Case Require("type").ToString Case "open_count_min" '{"type":"open_count_min","value":50} If Val(ReadIni("setup", "Count", "0")) < Val(Require("value")) Then SendStat("公告", "跳过", Notice("id")) : GoTo NextNotice Case "open_count_max" '{"type":"open_count_max","value":55} If Val(ReadIni("setup", "Count", "0")) > Val(Require("value")) Then SendStat("公告", "跳过", Notice("id")) : GoTo NextNotice Case "version_code_min" '{"type":"version_code_min","value":5} If VERSION_CODE < Val(Require("value")) Then SendStat("公告", "跳过", Notice("id")) : GoTo NextNotice Case "version_code_max" '{"type":"version_code_max","value":10} If VERSION_CODE > Val(Require("value")) Then SendStat("公告", "跳过", Notice("id")) : GoTo NextNotice End Select Next '初始化 SendStat("公告", "显示", Notice("id")) Dim Arguments() As String = Notice("text").ToString.Split("|") '确认类型 Select Case Notice("type").ToString Case "msgbox" '弹窗 '"text":"弹窗测试标题|弹窗测试内容\n第二行|知道啦" MyMsgbox(Arguments(1), Arguments(0).Replace("\n", vbCrLf), Arguments(2)) Case "url" '可选调用URL '"text":"网页测试标题|网页测试\n第二行|知道啦|不打开|http://www.mcbbs.net/" If MyMsgbox(Arguments(1), Arguments(0).Replace("\n", vbCrLf), Arguments(2), Arguments(3)) = 1 Then Process.Start(Arguments(4)) End If Case "update" '要求更新 '"text":"更新测试标题|更新测试\n第二行|知道啦|打死不更新" If NeedUpdate Then If MyMsgbox(Arguments(1), Arguments(0).Replace("\n", vbCrLf), Arguments(2), Arguments(3)) = 1 Then frmMain.Dispatcher.Invoke(Sub() ShowHint("正在更新,更新结束后 PCL 将会自动重启") Dim th As New Thread(AddressOf DownloadUpdate) th.Start() AniStart({ AaHeight(frmHomeRight.panLaunch, 277 - frmHomeRight.panLaunch.Height, , , New AniEaseEnd), AaOpacity(frmHomeRight.btnUpdate, -frmHomeRight.btnUpdate.Opacity, 200) }, "HomeRightHideUpdate") End Sub) End If End If End Select End If NextNotice: Next WriteIni("setup", "Notice", newNotice) Catch ex As Exception ExShow(ex, "处理公告失败") End Try End If End Sub Public IsPoolVersionListRunning As Boolean = False ''' ''' 加载线程:加载版本列表。 ''' ''' Public Sub PoolVersionList() '保证不同时加载版本列表 Do While IsPoolVersionListRunning Thread.Sleep(RandomInteger(15, 65)) Loop IsPoolVersionListRunning = True frmHomeRight.StartButtonIsLoading = True Try log("[Pool] 预加载版本列表开始") Dim ReloadAll As Boolean = False '是否重载全部版本 '检查Versions文件夹 If Not Directory.Exists(PATH_MC & "versions\") Then VersionsList = New Dictionary(Of VersionSwapState, ArrayList) From {{VersionSwapState.UNKNOWN, New ArrayList}, {VersionSwapState.NORMAL, New ArrayList}, {VersionSwapState.SWAP, New ArrayList}, {VersionSwapState.OLD, New ArrayList}, {VersionSwapState.WRONG, New ArrayList}} GoTo LoadEnd End If '遍历文件夹 Dim FolderList As New ArrayList For Each Folder As DirectoryInfo In (New DirectoryInfo(PATH_MC & "versions")).GetDirectories FolderList.Add(Folder.Name) Next '对比文件夹列表 Dim FolderListCheck As String = (MC_VERSION_CODE & "#" & Join(FolderList.ToArray, "#")).Replace(vbCrLf, "\n").Replace(":", "..").Replace("LastCheckedFolder", "LCF") If Not ReadIni(PATH_MC & "PCL.ini", "LastCheckedFolder") = FolderListCheck Then '文件夹列表不符 log("[Pool] 文件夹列表变更,重载所有版本") ReloadAll = True WriteIni(PATH_MC & "PCL.ini", "LastCheckedFolder", FolderListCheck) End If Reload: VersionsList = New Dictionary(Of VersionSwapState, ArrayList) From {{VersionSwapState.UNKNOWN, New ArrayList}, {VersionSwapState.NORMAL, New ArrayList}, {VersionSwapState.SWAP, New ArrayList}, {VersionSwapState.OLD, New ArrayList}, {VersionSwapState.WRONG, New ArrayList}} '遍历每个版本 For Each VersionName As String In FolderList If Not CheckDirectoryPermission(PATH_MC & "versions\" & VersionName & "\") Then GoTo NextFolder Dim Version As New MCVersion(VersionName) Directory.CreateDirectory(Version.Path & "\PCL") If ReadIni(Version.Path & "\PCL\Version.ini", "VersionListCode", "0") = MC_VERSION_CODE And Not ReloadAll Then '存在有效的PCL配置文件,读取配置文件 Version.LoadedByFile = True With Version .SwapType = ReadIni(Version.Path & "\PCL\Version.ini", "SwapType") .Version = ReadIni(Version.Path & "\PCL\Version.ini", "Version") .ReleaseTime = ReadIni(Version.Path & "\PCL\Version.ini", "ReleaseTime") .Assets = ReadIni(Version.Path & "\PCL\Version.ini", "Assets") .MainVersionCode = ReadIni(Version.Path & "\PCL\Version.ini", "MainVersionCode") .Type = ReadIni(Version.Path & "\PCL\Version.ini", "Type") .Logo = ReadIni(Version.Path & "\PCL\Version.ini", "Logo") .Description = ReadIni(Version.Path & "\PCL\Version.ini", "Description") .VersionCheckResult = ReadIni(Version.Path & "\PCL\Version.ini", "VersionCheckResult") .InheritVersion = ReadIni(Version.Path & "\PCL\Version.ini", "InheritVersion") End With '重新检测 Dim OldResult As VersionCheckState = Version.VersionCheckResult CheckMCVersion(Version, Version.VersionCheckResult = VersionCheckState.INHERITS_EXCEPTION) If Not Version.VersionCheckResult = OldResult Then ReloadAll = True GoTo Reload End If '图片的重新检测 If Not Version.Logo.StartsWith(PATH_IMAGE) Then If Not File.Exists(Version.Logo) Then GoTo NotLoadFile End If Else NotLoadFile: '不存在PCL配置文件,读取版本信息 Version.LoadedByFile = False CheckMCVersion(Version, True) '版本存在错误 If Not Version.VersionCheckResult = VersionCheckState.NO_PROBLEM Then '错误的版本 Version.SwapType = VersionSwapState.WRONG GoTo NextVersion End If '尝试从继承版本处获取版本号 If Not Version.InheritVersion = "" Then Version.Version = Version.InheritVersion GoTo VersionSearchEnd End If '从json获取版本失败,试图从文件夹名称获取版本号,如果失败则标记“未知” Dim CharCheck As ArrayList = RegexSearch(Version.Name, "1\.1?[0-9]{1}(\.[1-9]{1}([0-9]{1})?)?(-pre[1-9]?)?|1[2-9]{1}w[0-9]{1,2}[a-z]{1}") If CharCheck.Count > 0 Then Version.Version = CharCheck(0) Else Version.Version = "未知" End If VersionSearchEnd: '获取发布时间 If Version.JsonText.Contains("releaseTime") Then Version.ReleaseTime = RegexSearch(Version.JsonText.Replace(" ", ""), "(?<=releaseTime"":"")([^T]+)([^\+\-]+)")(0) Else Version.ReleaseTime = "" End If '设置主版本号 Dim MainVersion As ArrayList = RegexSearch(Version.Version, "1.[0-9]+") Version.MainVersionCode = Math.Min(20, If(MainVersion.Count = 1, Int(MainVersion(0).Replace("1.", "")), 0)) '获取版本种类 Version.Type = MCVersionType.RELEASE If Version.InheritVersion = "" Then '无继承版本的 If Version.Version = "未知" Then Version.Type = MCVersionType.UNKNOWN If Version.Version.ToLower.Contains("w") Or Version.Version.ToLower.Contains("pre") Or Version.JsonText.Replace(" ", "").Contains("""type"":""snapshot""") Then Version.Type = MCVersionType.SNAPSHOT If Len(Version.ReleaseTime) > 6 Then If Val(Mid(Version.ReleaseTime, 3, 2)) < 14 Then Version.Type = MCVersionType.OLD '未知版本可能显示为1970年,故取后两位数 If If(IsNothing(Version.Json("type")), False, Version.Json("type") = "fool") Then Version.Type = MCVersionType.FOOL If Version.JsonText.Contains("minecraftforge:minecraftforge") Then Version.Type = MCVersionType.FORGE Else '有继承版本的 Version.Type = If(Version.JsonText.Contains("Forge"), MCVersionType.FORGE, MCVersionType.OPTIFINE) End If '检测自定义种类信息 Version.SwapType = Val(ReadIni(Version.Path & "PCL\Setup.ini", "CustomType", "255")) If Version.SwapType = VersionSwapState.UNKNOWN Then 'UNKNOWN为255 Select Case Version.Type Case MCVersionType.SNAPSHOT, MCVersionType.RELEASE, MCVersionType.OPTIFINE '需要再次确认是否应该折叠,不加处理 Case MCVersionType.OLD '老版本,直接归类 Version.SwapType = VersionSwapState.OLD Case MCVersionType.FOOL '愚人节版本,直接折叠 Version.SwapType = VersionSwapState.SWAP Case Else '未知版本、Forge、不折叠版本,直接列入列表 Version.SwapType = VersionSwapState.NORMAL End Select End If NextVersion: '设置图标 If File.Exists(Version.Path & "PCL\Icon.png") Then Try Dim LogoTryLoad = New MyBitmap(Version.Path & "PCL\Icon.png") Version.Logo = Version.Path & "PCL\Icon.png" Catch ex As Exception File.Delete(Version.Path & "PCL\Icon.png") '加载失败就删了图片,因为下一次还是会失败的…… GoTo CommonPicture End Try Else CommonPicture: Select Case Version.Type Case MCVersionType.FORGE Version.Logo = PATH_IMAGE & "Block-Anvil.png" Case MCVersionType.OLD Version.Logo = PATH_IMAGE & "Block-CobbleStone.png" Case MCVersionType.SNAPSHOT Version.Logo = PATH_IMAGE & "Block-CommandBlock.png" Case MCVersionType.FOOL Version.Logo = PATH_IMAGE & "Block-Dirt.png" Case Else 'OptiFine、原版、未知 If Version.SwapType = VersionSwapState.WRONG Then Version.Logo = PATH_IMAGE & "Block-RedstoneBlock.png" Else Version.Logo = PATH_IMAGE & "Block-Grass.png" End If End Select End If '设置描述文本 If Version.SwapType = VersionSwapState.WRONG Then '错误描述文本 Select Case Version.VersionCheckResult Case VersionCheckState.EMPTY_FOLDER Version.Description = "空文件夹" Case VersionCheckState.INHERITS_EXCEPTION Version.Description = "依赖版本出错:" & Version.InheritVersion Case VersionCheckState.INHERITS_NOT_EXIST Version.Description = "依赖版本丢失:" & Version.InheritVersion Case VersionCheckState.JAR_NOT_EXIST Version.Description = "Jar 文件缺失" Case VersionCheckState.JSON_CANT_READ Version.Description = "Json 读取失败" Case VersionCheckState.JSON_NOT_EXIST Version.Description = "Json 文件缺失" Case Else Version.Description = "未知问题" End Select Else '加载自定义文本 Version.Description = ReadIni(Version.Path & "PCL\Setup.ini", "Description") If Version.Description = "" Then '程序输出文本 Dim Description As String = If(Version.Type = MCVersionType.OPTIFINE, "OptiFine ", If(Version.Type = MCVersionType.FORGE, "Forge ", "")) & Version.Version If Not Version.Name = Description And Not Description = "未知" Then Version.Description = Description End If End If End If VersionsList(Version.SwapType).Add(Version) NextFolder: Next '判断是否需要重载列表次序 If VersionsList(VersionSwapState.UNKNOWN).Count = 0 Then GoTo LoadEnd '计算最新的版本 Dim LargestTimeVer As New MCVersion("") '最新的版本 Dim LargestTime As ULong = 0 '最新的版本的时间 Dim CurrentTime As ULong '当前的版本的时间 Dim NewVersion As New Dictionary(Of String, MCVersion) '存储版本列表,键为“MainVersionCode-Type” For Each ver As MCVersion In VersionsList(VersionSwapState.UNKNOWN) CurrentTime = Val(ver.ReleaseTime.Replace("-", "").Replace(":", "").Replace("T", "")) '取最大时间 If CurrentTime > LargestTime Then LargestTime = CurrentTime LargestTimeVer = ver End If 'OptiFine与正式版判断 If Not ver.Type = MCVersionType.SNAPSHOT Then Dim CurrentVersion As New MCVersion("") If NewVersion.TryGetValue(ver.MainVersionCode & "-" & ver.Type, CurrentVersion) Then If CurrentTime > Val(CurrentVersion.ReleaseTime.Replace("-", "").Replace(":", "").Replace("T", "")) Then NewVersion(ver.MainVersionCode & "-" & ver.Type) = ver End If Else NewVersion.Add(ver.MainVersionCode & "-" & ver.Type, ver) End If End If Next '如果最新的版本为快照,则添加入列表 If LargestTimeVer.Type = MCVersionType.SNAPSHOT Then LargestTimeVer.SwapType = VersionSwapState.NORMAL '准备检查后的主版本列表 Dim MainCodeList As New ArrayList '主版本列表 For Each Key As String In NewVersion.Keys If Not MainCodeList.Contains(Integer.Parse(Key.Split("-")(0))) Then MainCodeList.Add(Integer.Parse(Key.Split("-")(0))) Next '把各个OptiFine和原版加入列表 Dim WillAdd As New ArrayList For Each MainCode As Integer In MainCodeList If NewVersion.ContainsKey(MainCode & "-" & MCVersionType.OPTIFINE) Then '存在OptiFine版本 WillAdd.Add(NewVersion(MainCode & "-" & MCVersionType.OPTIFINE)) If NewVersion.ContainsKey(MainCode & "-" & MCVersionType.RELEASE) Then '如果原版比OptiFine版更新也显示原版 If Val(NewVersion(MainCode & "-" & MCVersionType.RELEASE).ReleaseTime.Replace("-", "").Replace(":", "").Replace("T", "")) > Val(NewVersion(MainCode & "-" & MCVersionType.OPTIFINE).ReleaseTime.Replace("-", "").Replace(":", "").Replace("T", "")) Then WillAdd.Add(NewVersion(MainCode & "-" & MCVersionType.RELEASE)) End If End If Else '不存在OptiFine版本,检查原版版本 If NewVersion.ContainsKey(MainCode & "-" & MCVersionType.RELEASE) Then WillAdd.Add(NewVersion(MainCode & "-" & MCVersionType.RELEASE)) End If Next '列表处理 For Each Ver As MCVersion In WillAdd Ver.SwapType = VersionSwapState.NORMAL Next For Each Ver As MCVersion In VersionsList(VersionSwapState.UNKNOWN) If Ver.SwapType = VersionSwapState.UNKNOWN Then Ver.SwapType = VersionSwapState.SWAP VersionsList(Ver.SwapType).Add(Ver) Next LoadEnd: VersionsList.Remove(VersionSwapState.UNKNOWN) '根据设置合并折叠与老版本 If ReadIni("setup", "HomeVersionSwap", "True") = "False" Then VersionsList(VersionSwapState.NORMAL).AddRange(VersionsList(VersionSwapState.SWAP)) VersionsList(VersionSwapState.SWAP).Clear() End If If ReadIni("setup", "HomeVersionOld", "True") = "False" Then VersionsList(VersionSwapState.NORMAL).AddRange(VersionsList(VersionSwapState.OLD)) VersionsList(VersionSwapState.OLD).Clear() End If '保存文件 & 列表排序 For Each List As ArrayList In VersionsList.Values '排序 Dim Sorter As MCVersion() = ArrayConventer(Of MCVersion, ArrayList)(List) Array.Sort(Sorter) List.Clear() For Each Item In Sorter If Not IsNothing(Item) Then List.Add(Item) Next '写入文件 For Each Version As MCVersion In List If Version.LoadedByFile = False Then '写入PCL配置文件 Using iniWriter As New StreamWriter(Version.Path & "PCL\Version.ini", False, New UTF8Encoding(False)) iniWriter.WriteLine("VersionListCode:" & MC_VERSION_CODE) iniWriter.WriteLine("SwapType:" & Version.SwapType) iniWriter.WriteLine("Version:" & Version.Version) iniWriter.WriteLine("ReleaseTime:" & Version.ReleaseTime) iniWriter.WriteLine("MainVersionCode:" & Version.MainVersionCode) iniWriter.WriteLine("Type:" & Version.Type) iniWriter.WriteLine("Logo:" & Version.Logo) iniWriter.WriteLine("Description:" & Version.Description) iniWriter.WriteLine("VersionCheckResult:" & Version.VersionCheckResult) iniWriter.WriteLine("InheritVersion:" & Version.InheritVersion) iniWriter.Flush() End Using End If Next Next log("[Pool] 预加载版本列表结束") Catch ex As Exception ExShow(ex, "加载版本列表失败", ErrorLevel.MsgboxAndFeedback) '清空列表 VersionsList = New Dictionary(Of VersionSwapState, ArrayList) Finally frmHomeRight.Dispatcher.Invoke(Sub() frmHomeRight.LoadVersionList()) End Try End Sub ''' ''' 加载线程:刷新背景图片和顶部图片。 ''' ''' Public Sub PoolLoadPictures() Try If Not ReadIni("setup", "UiBackgroundURL", "") = "" Then log("[Pool] 刷新背景图片开始") DownloadFile(ReadIni("setup", "UiBackgroundURL", ""), PATH & "PCL\back.png" & DOWNLOADING_END) File.Delete(PATH & "PCL\back.png") FileSystem.Rename(PATH & "PCL\back.png" & DOWNLOADING_END, PATH & "PCL\back.png") log("[Pool] 刷新背景图片结束") End If Catch ex As Exception ExShow(ex, "刷新背景图片失败", ErrorLevel.AllUsers) End Try Try If Not ReadIni("setup", "UiBarURL", "") = "" Then log("[Pool] 刷新顶栏图片开始") DownloadFile(ReadIni("setup", "UiBarURL", ""), PATH & "PCL\top.png" & DOWNLOADING_END) File.Delete(PATH & "PCL\top.png") FileSystem.Rename(PATH & "PCL\top.png" & DOWNLOADING_END, PATH & "PCL\top.png") log("[Pool] 刷新顶栏图片结束") End If Catch ex As Exception ExShow(ex, "刷新顶栏图片失败", ErrorLevel.AllUsers) End Try frmMain.Dispatcher.Invoke(New RefreshThemeInvoke(AddressOf RefreshTheme)) End Sub Private PoolPushCount As Integer = 0 ''' ''' 加载线程:首页。 ''' ''' Public Class PushSource Public Name As String Public URL As String Public Introduce As String Public IsEnabled As String Public LocalPath As String End Class Public Sub PoolPush() If MODE_OFFLINE Then frmStart.IsPushLoading = False : Exit Sub '加载特殊处理的第三方推荐 If ReadIni("setup", "HomeMCBBSPush", "True") = "True" Then Dim th As New Thread(AddressOf PoolMainMCBBS) : th.Priority = ThreadPriority.BelowNormal : th.Start() : PoolPushCount = PoolPushCount + 1 If ReadIni("setup", "HomeTbPush", "True") = "True" Then Dim th As New Thread(AddressOf PoolMainTb) : th.Priority = ThreadPriority.BelowNormal : th.Start() : PoolPushCount = PoolPushCount + 1 If ReadIni("setup", "HomeMojangPush", "True") = "True" Then Dim th As New Thread(AddressOf PoolMainMojang) : th.Priority = ThreadPriority.BelowNormal : th.Start() : PoolPushCount = PoolPushCount + 1 If ReadIni("setup", "HomeForumPush", "False") = "True" Then Dim th As New Thread(AddressOf PoolMainForum) : th.Priority = ThreadPriority.BelowNormal : th.Start() : PoolPushCount = PoolPushCount + 1 '加载默认推荐 Dim AllLocals As New ArrayList If ReadIni("setup", "HomePCLPush", "True") = "True" Then AllLocals.Add(New PushSource With {.Name = "PCL 推荐", .URL = TX_SREVER_1 & "push.ini", .IsEnabled = True, .Introduce = "PCL 官方的优质内容推荐(不含广告请放心食用)。"}) '加载每个推荐 Dim Count As Integer = 0 For Each Push As PushSource In AllLocals If Push.IsEnabled Then PoolPushCount = PoolPushCount + 1 Dim Source As PushSource = Push Dim thurl As New Thread(Sub() PoolMainPushURL(Source)) thurl.Start() Count = Count + 1 End If Next If PoolPushCount = 0 Then frmStart.IsPushLoading = False log("[Pool-HomeLeft] 订阅信息加载完成") End Sub #Region "推荐源" ''' ''' 贴吧源。 ''' ''' Private Sub PoolMainTb() log("[Pool-HomeLeft] 加载推荐源:Minecraft 吧精品") If File.Exists(PATH & "PCL\cache\source\CA28E736032912D2801529958AB8937EBB351E2338212FA49529\cache.ini") Then '如果缓存存在,读取->(下载->处理) PoolMainTbLoad() PoolPushCount = PoolPushCount - 1 If PoolPushCount = 0 Then frmStart.IsPushLoading = False Thread.Sleep(5000) PoolMainTbDownload() Else '如果缓存不存在,(下载->处理)->读取 If PoolMainTbDownload() Then PoolMainTbLoad() PoolPushCount = PoolPushCount - 1 If PoolPushCount = 0 Then frmStart.IsPushLoading = False End If End Sub Private Function PoolMainTbDownload() As Boolean '———————————— '下载 '———————————— Dim SourceCode As String Try SourceCode = GetWebsiteCode("https://tieba.baidu.com/f?kw=minecraft&ie=utf-8&tab=good", New UTF8Encoding(False)) If SourceCode.Length < 100000 Then Throw New Exception("获取的代码长度不足:" & SourceCode) Catch ex As Exception ExShow(ex, "下载推荐源失败(Minecraft 吧精品)") Return False End Try '———————————— '处理推荐 '———————————— Dim AllImages As New ArrayList Dim AllLinks As New ArrayList Dim AllTexts As New ArrayList Try '预处理范围 SourceCode = Mid(SourceCode, SourceCode.IndexOf("""thread_list""")) SourceCode = Mid(SourceCode, 1, SourceCode.IndexOf("thread_list_bottom")) Dim Threads As ArrayList = RegexSearch(SourceCode, "col2_right[\w\W]+?author_name") Dim i As Integer = 0 For Count As Integer = 0 To 5 '每个帖子 AllLinks.Add("https://tieba.baidu.com" & RegexSearch(Threads(i), "/p/[^""]+")(0)) Threads(i) = Threads(i).ToString.Substring(Threads(i).ToString.IndexOf("a href=")) AllTexts.Add(RegexSearch(Threads(i), "(?<=title="")[^""]+")(0)) Dim ImageTry = RegexSearch(Threads(i), "(?<=data-original="")[^""]+") AllImages.Add(If(ImageTry.Count > 0, ImageTry(0), "")) i = i + RandomInteger(1, 3) If i > Threads.Count - 1 Then Exit For Next Catch ex As Exception ExShow(ex, "预处理推荐源失败(Minecraft 吧精品)") Return False End Try '———————————— '项目过滤 '———————————— Dim Sources As New ArrayList Try For i = 0 To AllTexts.Count - 1 '检测是否为其它启动器广告 If Not (AllTexts(i).Contains("启动器") Or AllTexts(i).Contains("吧") Or AllTexts(i).Contains("水楼") Or AllTexts(i).Contains("赛") Or AllTexts(i).Contains("招聘") Or AllTexts(i).Contains("联机") Or AllTexts(i).Contains("服务器") Or AllTexts(i).Contains("HMCL") Or AllTexts(i).Contains("Launcher") Or AllTexts(i).Contains("Baka")) Then Sources.Add(New InfoBoxSource With { .OnClickURL = AllLinks(i), .Title = AllTexts(i), .Source = "Minecraft 吧精品", .PictureName = AllImages(i), .Type = ""}) End If Next '———————————— '图片种类、纯文本处理 '———————————— For Each Source As InfoBoxSource In Sources GetSourceType(Source) Dim regex As New RegularExpressions.Regex("\[[0-9]{2,4}-[0-9]{1,2}-[0-9]{1,2}\]") Source.Title = regex.Replace(Source.Title, "") If RegexSearch(Source.Title, "【[^0]{2,4}】").Count > 0 Then If Source.Title.StartsWith(RegexSearch(Source.Title, "【[^0]{2,4}】")(0)) Then Source.Title = Source.Title.Replace(RegexSearch(Source.Title, "【[^0]{2,4}】")(0), "") ElseIf RegexSearch(Source.Title, "\[[^0]{2,4}\]").Count > 0 Then If Source.Title.StartsWith(RegexSearch(Source.Title, "\[[^0]{2,4}\]")(0)) Then Source.Title = Source.Title.Replace(RegexSearch(Source.Title, "\[[^0]{2,4}\]")(0), "") End If Next Catch ex As Exception ExShow(ex, "处理推荐源失败(Minecraft 吧精品)") Return False End Try '———————————— '输出 '———————————— Dim OutputIni As String = "Version:" & PUSH_VERSION_CODE & vbCrLf For i = 1 To Sources.Count Dim Source As InfoBoxSource = Sources(i - 1) OutputIni = OutputIni & vbCrLf OutputIni = OutputIni & "Picture" & i & ":" & Source.PictureName & vbCrLf OutputIni = OutputIni & "Title" & i & ":" & Source.Title & vbCrLf OutputIni = OutputIni & "Link" & i & ":" & Source.OnClickURL & vbCrLf OutputIni = OutputIni & "Type" & i & ":" & Source.Type & vbCrLf Next WriteFile("PCL\cache\source\CA28E736032912D2801529958AB8937EBB351E2338212FA49529\cache.ini", OutputIni, isFullPath:=False) Return True End Function Private Sub PoolMainTbLoad() PoolMainPushURLLoad(New PushSource With { .LocalPath = PATH & "PCL\cache\source\CA28E736032912D2801529958AB8937EBB351E2338212FA49529\cache.ini", .Name = "Minecraft 吧精品"}) End Sub ''' ''' Mojang 源。 ''' ''' Private Sub PoolMainMojang() log("[Pool-HomeLeft] 加载推荐源:Mojang 官方") If File.Exists(PATH & "PCL\cache\source\A407DF2F142912192C15239087B89C1BBB82\cache.ini") Then PoolMainMojangLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False Thread.Sleep(5000) PoolMainMojangDownload() Else '如果缓存不存在,(下载->处理)->读取 If PoolMainMojangDownload() Then PoolMainMojangLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False End If End Sub Private Function PoolMainMojangDownload() As Boolean '———————————— '下载 '———————————— Dim SourceCode As String Try SourceCode = GetWebsiteCode("http://www.mcbbs.net/portal.php", New UTF8Encoding(False)) If SourceCode.Length < 10000 Then Throw New Exception("获取的代码长度不足:" & SourceCode) Catch ex As Exception ExShow(ex, "下载推荐源失败(Mojang)") Return False End Try '———————————— '处理推荐 '———————————— Dim AllImages As New ArrayList Dim AllLinks As New ArrayList Dim AllTexts As New ArrayList Dim AllTypes As New ArrayList '———————————— '加载 '———————————— Try '去头尾 SourceCode = Mid(SourceCode, SourceCode.IndexOf("portal_zb")) SourceCode = Mid(SourceCode, 1, SourceCode.IndexOf("titletext")) '获取文本 AllImages = RegexSearch(SourceCode, "(?<=)[^<]+") For i = 0 To AllTexts.Count - 1 AllTypes.Add("") Next Catch ex As Exception ExShow(ex, "处理推荐源失败(Mojang)") AllImages.Clear() AllLinks.Clear() AllTexts.Clear() AllTypes.Clear() End Try '———————————— 'URL 二次处理及项目过滤 '———————————— Dim Sources As New ArrayList Try For i = 0 To AllLinks.Count - 1 If RegexSearch(AllLinks(i), "[0-9]{5,7}").Count = 0 Then AllLinks(i) = "" Else AllLinks(i) = RegexSearch(AllLinks(i), "[0-9]{5,7}")(0) End If Next Dim NoDoubledArray As ArrayList = ArrayNoDouble(AllLinks.ToArray) For Each NoDoubledURL As String In NoDoubledArray Dim Index As Integer = AllLinks.IndexOf(NoDoubledURL) If Not Index >= AllTexts.Count Then '检测是否为其它启动器广告 If Not (AllTexts(Index).Contains("启动器") Or AllTexts(Index).Contains("赛") Or AllTexts(Index).Contains("招聘") Or AllTexts(Index).Contains("联机") Or AllTexts(Index).Contains("服务器") Or AllTexts(Index).Contains("HMCL") Or AllTexts(Index).Contains("Launcher") Or AllTexts(Index).Contains("Baka") Or NoDoubledURL = "") Then Sources.Add(New InfoBoxSource With {.OnClickURL = "http://www.mcbbs.net/thread-" & NoDoubledURL & "-1-1.html", .Title = AllTexts(Index), .Source = "Mojang 官方", .PictureName = AllImages(Index), .Type = AllTypes(Index)}) End If End If Next '———————————— '图片种类、纯文本处理 '———————————— For Each Source As InfoBoxSource In Sources Dim Title2 = Source.Title GetSourceType(Source) Source.Title = Title2 Next Catch ex As Exception ExShow(ex, "处理推荐源失败(Mojang)") Return False End Try '———————————— '输出 '———————————— Dim OutputIni As String = "Version:" & PUSH_VERSION_CODE & vbCrLf Dim IsWeekly As Boolean = False '每周的只显示一个 Dim OutI As Integer = 1 '用于输出的 i For i = 1 To Sources.Count If Sources(i - 1).Title.Contains("周") Then If IsWeekly Then GoTo NextSource Else IsWeekly = True End If End If Dim Source As InfoBoxSource = Sources(i - 1) OutputIni = OutputIni & vbCrLf OutputIni = OutputIni & "Picture" & OutI & ":" & Source.PictureName & vbCrLf OutputIni = OutputIni & "Title" & OutI & ":" & Source.Title & vbCrLf OutputIni = OutputIni & "Link" & OutI & ":" & Source.OnClickURL & vbCrLf OutputIni = OutputIni & "Type" & OutI & ":" & Source.Type & vbCrLf OutI = OutI + 1 NextSource: Next WriteFile("PCL\cache\source\A407DF2F142912192C15239087B89C1BBB82\cache.ini", OutputIni, isFullPath:=False) Return True End Function Private Sub PoolMainMojangLoad() PoolMainPushURLLoad(New PushSource With { .LocalPath = PATH & "PCL\cache\source\A407DF2F142912192C15239087B89C1BBB82\cache.ini", .Name = "Mojang 官方"}) End Sub ''' ''' Forum 源。 ''' ''' Private Sub PoolMainForum() log("[Pool-HomeLeft] 加载推荐源:Minecraft Forum") If File.Exists(PATH & "PCL\cache\source\" & Left(SerAdd("Minecraft Forum"), 100) & "\cache.ini") Then PoolMainForumLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False Thread.Sleep(5000) PoolMainForumDownload() Else '如果缓存不存在,(下载->处理)->读取 If PoolMainForumDownload() Then PoolMainForumLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False End If End Sub Private Function PoolMainForumDownload() As Boolean '———————————— '下载 '———————————— Dim SourceCode As String Try SourceCode = GetWebsiteCode("http://www.minecraftforum.net/", New UTF8Encoding(False)) If SourceCode.Length < 500 Then Throw New Exception("获取的代码长度不足:" & SourceCode) Catch ex As Exception ExShow(ex, "下载推荐源失败(Minecraft Forum)") Return False End Try '———————————— '处理推荐 '———————————— Dim AllLinks As New ArrayList Dim AllTexts As New ArrayList Dim AllImages As New ArrayList Try Dim Threads As ArrayList = RegexSearch(SourceCode, "
[\w\W]+?
") Dim i As Integer = 0 For Count As Integer = 0 To 2 '每条Article AllLinks.Add(RegexSearch(Threads(i), "http://www.minecraftforum.net/[^""]+")(0)) AllTexts.Add(RegexSearch(Threads(i), "(?<=]+>)[^<]{5,100}")(0)) AllImages.Add(RegexSearch(Threads(i), "(?<= Threads.Count - 1 Then Exit For Next Catch ex As Exception ExShow(ex, "预处理推荐源失败(Minecraft Forum)") Return False End Try '———————————— '项目过滤 '———————————— Dim Sources As New ArrayList Try For i = 0 To AllTexts.Count - 1 If AllTexts(i).ToString.StartsWith(" ") Then AllTexts(i) = Right(AllTexts(i), Len(AllTexts(i)) - 1) Sources.Add(New InfoBoxSource With { .OnClickURL = AllLinks(i), .Title = AllTexts(i), .Source = "Minecraft Forum", .PictureName = AllImages(i), .Type = ""}) GetSourceType(Sources(Sources.Count - 1)) Next Catch ex As Exception ExShow(ex, "处理推荐源失败(Minecraft Forum)") Return False End Try '———————————— '输出 '———————————— Dim OutputIni As String = "Version:" & PUSH_VERSION_CODE & vbCrLf For i = 1 To Sources.Count Dim Source As InfoBoxSource = Sources(i - 1) OutputIni = OutputIni & vbCrLf OutputIni = OutputIni & "Picture" & i & ":" & Source.PictureName & vbCrLf OutputIni = OutputIni & "Title" & i & ":" & Source.Title & vbCrLf OutputIni = OutputIni & "Link" & i & ":" & Source.OnClickURL & vbCrLf OutputIni = OutputIni & "Type" & i & ":" & Source.Type & vbCrLf Next WriteFile("PCL\cache\source\" & Left(SerAdd("Minecraft Forum"), 100) & "\cache.ini", OutputIni, isFullPath:=False) Return True End Function Private Sub PoolMainForumLoad() PoolMainPushURLLoad(New PushSource With { .LocalPath = PATH & "PCL\cache\source\" & Left(SerAdd("Minecraft Forum"), 100) & "\cache.ini", .Name = "Minecraft Forum"}) End Sub ''' ''' MCBBS 源。 ''' ''' Private Sub PoolMainMCBBS() log("[Pool-HomeLeft] 加载推荐源:我的世界中文论坛") If File.Exists(PATH & "PCL\cache\source\C6378196067760DF918A0145291AB651\cache.ini") Then PoolMainMCBBSLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False Thread.Sleep(5000) PoolMainMCBBSDownload() Else '如果缓存不存在,(下载->处理)->读取 If PoolMainMCBBSDownload() Then PoolMainMCBBSLoad() PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False End If End Sub Private Function PoolMainMCBBSDownload() As Boolean '———————————— '下载 '———————————— Dim SourceCode As String Try SourceCode = GetWebsiteCode("http://www.mcbbs.net/forum.php", New UTF8Encoding(False)) If SourceCode.Length < 10000 Then Throw New Exception("获取的代码长度不足:" & SourceCode) Catch ex As Exception ExShow(ex, "下载推荐源失败(我的世界中文论坛)") Return False End Try '———————————— '处理推荐 '———————————— Dim AllImages As New ArrayList Dim AllLinks As New ArrayList Dim AllTexts As New ArrayList Dim AllTypes As New ArrayList '———————————— '首页图片推荐 '———————————— Try SourceCode = Mid(SourceCode, SourceCode.IndexOf("var files = '")).Replace(" var files = '", "") AllImages.AddRange(Mid(SourceCode, 1, SourceCode.IndexOf("';")).Split("|")) SourceCode = Mid(SourceCode, SourceCode.IndexOf("var links = '")).Replace(" var links = '", "") AllLinks.AddRange(Mid(SourceCode, 1, SourceCode.IndexOf("';")).Split("|")) SourceCode = Mid(SourceCode, SourceCode.IndexOf("var texts = '")).Replace(" var texts = '", "") AllTexts.AddRange(Mid(SourceCode, 1, SourceCode.IndexOf("';")).Split("|")) For i = 0 To AllTexts.Count - 1 AllTypes.Add("") Next Catch ex As Exception ExShow(ex, "处理推荐源失败(我的世界中文论坛/图片推荐)") AllImages.Clear() AllLinks.Clear() AllTexts.Clear() AllTypes.Clear() End Try '———————————— '新闻资讯与最新精华 '———————————— Try '最新精华 SourceCode = Mid(SourceCode, SourceCode.IndexOf("div id=""kk_xshow_cont_3""")) Dim TextGood As String = Mid(SourceCode, 1, SourceCode.IndexOf("div id=""kk_xshow_cont_4""")) Dim GoodLinks As New ArrayList(RegexSearch(TextGood, "(?<=a href="")[^""]+[0-9]{4,7}[^""]+(?="")")) Dim GoodTexts As New ArrayList(RegexSearch(TextGood, "(?<=标题: )[^\n]+")) For i = 0 To Math.Min(GoodLinks.Count - 1, 6) Step 0 AllLinks.Add(GoodLinks(i)) AllTexts.Add(GoodTexts(i)) AllImages.Add("") AllTypes.Add("文本推荐") i = i + RandomInteger(1, 3) Next '新闻资讯 SourceCode = Mid(SourceCode, SourceCode.IndexOf("div id=""diy_right")) Dim SourceString = RegexSearch(SourceCode, "(?<=a href="")[^""]*[0-9]{5,7}[^""]*"" title=""[^""""]+(?="")")(0) '处理源字符串 AllLinks.Add(Mid(SourceString, 1, SourceString.IndexOf(""" title="""))) AllTexts.Add(Mid(SourceString, 10 + SourceString.IndexOf(""" title="""))) AllImages.Add("") AllTypes.Add("新闻") Catch ex As Exception ExShow(ex, "处理推荐源失败(我的世界中文论坛/文本推荐)") End Try '———————————— 'URL 二次处理及项目过滤 '———————————— Dim Sources As New ArrayList Try For i = 0 To AllLinks.Count - 1 If RegexSearch(AllLinks(i), "[0-9]{5,7}").Count = 0 Then AllLinks(i) = "" Else AllLinks(i) = RegexSearch(AllLinks(i), "[0-9]{5,7}")(0) End If Next Dim NoDoubledArray As ArrayList = ArrayNoDouble(AllLinks.ToArray) For Each NoDoubledURL As String In NoDoubledArray Dim Index As Integer = AllLinks.IndexOf(NoDoubledURL) If Not Index >= AllTexts.Count Then '检测是否为其它启动器广告 If Not (AllTexts(Index).Contains("启动器") Or AllTexts(Index).Contains("赛") Or AllTexts(Index).Contains("招聘") Or AllTexts(Index).Contains("联机") Or AllTexts(Index).Contains("服务器") Or AllTexts(Index).Contains("HMCL") Or AllTexts(Index).Contains("Launcher") Or AllTexts(Index).Contains("Baka") Or NoDoubledURL = "") Then Sources.Add(New InfoBoxSource With {.OnClickURL = "http://www.mcbbs.net/thread-" & NoDoubledURL & "-1-1.html", .Title = AllTexts(Index), .Source = "我的世界中文论坛", .PictureName = AllImages(Index), .Type = AllTypes(Index)}) End If End If Next '———————————— '图片种类、纯文本处理 '———————————— For Each Source As InfoBoxSource In Sources Dim Title2 = Source.Title GetSourceType(Source) Source.Title = Title2 Next Catch ex As Exception ExShow(ex, "处理推荐源失败(我的世界中文论坛)") Return False End Try '———————————— '输出 '———————————— Dim OutputIni As String = "Version:" & PUSH_VERSION_CODE & vbCrLf For i = 1 To Sources.Count Dim Source As InfoBoxSource = Sources(i - 1) OutputIni = OutputIni & vbCrLf OutputIni = OutputIni & "Picture" & i & ":" & Source.PictureName & vbCrLf OutputIni = OutputIni & "Title" & i & ":" & Source.Title & vbCrLf OutputIni = OutputIni & "Link" & i & ":" & Source.OnClickURL & vbCrLf OutputIni = OutputIni & "Type" & i & ":" & Source.Type & vbCrLf Next WriteFile("PCL\cache\source\C6378196067760DF918A0145291AB651\cache.ini", OutputIni, isFullPath:=False) Return True End Function Private Sub PoolMainMCBBSLoad() PoolMainPushURLLoad(New PushSource With { .LocalPath = PATH & "PCL\cache\source\C6378196067760DF918A0145291AB651\cache.ini", .Name = "我的世界中文论坛"}) Thread.Sleep(2000) End Sub Public IsHaveAd As Boolean = False ''' ''' ini 源。 ''' ''' Private Sub PoolMainPushURL(ByVal Source As PushSource) log("[Pool-HomeLeft] 加载推荐源:" & Source.Name) Source.LocalPath = PATH & "PCL\cache\source\" & Left(SerAdd(Source.Name), 100) & "\cache.ini" If File.Exists(Source.LocalPath) Then '如果缓存存在,读取->下载 PoolMainPushURLLoad(Source) PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False Thread.Sleep(5000) PoolMainPushURLDownload(Source) Else '如果缓存不存在,(下载->处理)->读取 If PoolMainPushURLDownload(Source) Then PoolMainPushURLLoad(Source) PoolPushCount = PoolPushCount - 1 : If PoolPushCount = 0 Then frmStart.IsPushLoading = False End If End Sub Private Function PoolMainPushURLDownload(ByVal Source As PushSource) As Boolean If DownloadFile(Source.URL, Source.LocalPath) Then If Val(ReadIni(Source.LocalPath, "Version", "0")) < PUSH_VERSION_CODE Then log("[Pool-HomeLeft] 推荐源版本过老(" & Source.Name & "):目前为 " & ReadIni(Source.LocalPath, "Version", "0") & ",要求为 " & PUSH_VERSION_CODE, True) Return False End If Return True Else log("[Pool-HomeLeft] 下载推荐源失败:" & Source.Name, True) Return False End If End Function Private Sub PoolMainPushURLLoad(ByVal Source As PushSource) '版本检查 If Val(ReadIni(Source.LocalPath, "Version", "0")) < PUSH_VERSION_CODE Then log("[Pool-HomeLeft] 推荐源版本过老(" & Source.Name & "):目前为 " & ReadIni(Source.LocalPath, "Version", "0") & ",要求为 " & PUSH_VERSION_CODE, True) Exit Sub End If '处理代码 Try Dim CanLoadItem As New ArrayList Dim ii As Integer = 1 Do Until ReadIni(Source.LocalPath, "Title" & ii) = "" '过滤 Dim Context As String = ReadIni(Source.LocalPath, "Title" & ii) If Context.Contains("Plain Craft Launcher") Or Context.Contains("PCL") Or Not (Context.Contains("启动器") Or Context.Contains("HMCL") Or Context.Contains("Launcher") Or Context.Contains("Baka") Or ReadIni(Source.LocalPath, "Link" & ii).Contains("twitter")) Then Dim InfoSource As New InfoBoxSource With { .OnClickURL = ReadIni(Source.LocalPath, "Link" & ii), .PictureName = ReadIni(Source.LocalPath, "Picture" & ii), .Title = Context, .Source = Source.Name, .Type = ReadIni(Source.LocalPath, "Type" & ii)} '纯文本订阅只保留一半 If Not InfoSource.PictureName.Contains("://") Then If ReadIni("setup", "HomeHide", "True") = "True" And Not RandomInteger(0, 1) = 1 Then GoTo NextItem End If frmHomeLeft.LoadingItem.Add(InfoSource) InfoSource.Load() End If NextItem: ii = ii + 1 Loop Catch ex As Exception log("[Pool-HomeLeft] 处理推荐源失败(" & Source.Name & "):" & GetStringFromException(ex, True), True) End Try End Sub Private Sub GetSourceType(ByRef Source As InfoBoxSource) If Source.Type = "新闻" Then ElseIf Source.Title.Contains("教程") Or Source.Title.Contains("新手") Or Source.Title.Contains("技巧") Or Source.Title.ToLower.Contains("wiki") Or Source.Title.Contains("翻译") Then Source.Type = "教程" ElseIf Source.Title.Contains("编辑器") Or Source.Title.Contains("软件") Or Source.Title.Contains("工具") Or Source.Title.Contains("生成器") Then Source.Type = "软件" ElseIf Source.Title.Contains("联机") Or Source.Title.Contains("插件") Or Source.Title.Contains("服务端") Or Source.Title.Contains("开服") Or Source.Title.Contains("服务器") Then Source.Type = "多人" ElseIf Source.Title.Contains("皮肤") Then Source.Type = "皮肤" ElseIf Source.Title.ToLower.Contains("mod") Then Source.Type = "Mod" ElseIf Source.Title.Contains("解密") Or Source.Title.Contains("逃") Or Source.Title.Contains("跑酷") Or Source.Title.Contains("空岛") Or Source.Title.Contains("RPG") Or Source.Title.Contains("生存") Or Source.Title.Contains("小游戏") Or Source.Title.Contains("CB") Or Source.Title.Contains("红石") Or Source.Title.Contains("闯关") Or Source.Title.Contains("PVP") Or Source.Title.Contains("PVE") Or Source.Title.Contains("命令") Or Source.Title.Contains("战") Or Source.Title.Contains("冒险") Then Source.Type = "地图" ElseIf Source.Title.Contains("创作") Or Source.Title.Contains("小说") Or Source.Title.Contains("漫画") Or Source.Title.Contains("物语") Or Source.Title.Contains("插画") Or Source.Title.Contains("短篇") Or Source.Title.Contains("长篇") Then Source.Type = "创作" ElseIf Source.Title.Contains("材质") Or Source.Title.Contains("资源包") Then Source.Type = "资源包" ElseIf Source.Title.Contains("整合") Or Source.Title.Contains("懒人包") Then Source.Type = "懒人包" ElseIf Source.Title.Contains("阁") Or Source.Title.Contains("航母") Or Source.Title.Contains("港") Or Source.Title.Contains("宫") Or Source.Title.Contains("上古之石") Or Source.Title.Contains("楼") Or Source.Title.Contains("城") Or Source.Title.Contains("镇") Or Source.Title.Contains("殿") Or Source.Title.Contains("厅") Or Source.Title.Contains("桥") Or Source.Title.Contains("陵") Or Source.Title.Contains("园") Or Source.Title.Contains("宅") Or Source.Title.Contains("街") Or Source.Title.Contains("筑") Or Source.Title.Contains("山") Or Source.Title.Contains("景") Or Source.Title.ToLower.Contains("city") Then Source.Type = "建筑" ElseIf Source.Title.Contains("地图") Or Source.Title.Contains("作品") Then Source.Type = "地图" ElseIf Source.Source = "Minecraft Forum" Or Source.Title.Contains("发布") Or Source.Title.Contains("eleas") Then Source.Type = "新闻" Else Source.Type = "" End If End Sub #End Region #End Region #Region "自动安装" ''' ''' 自动安装文件或文件夹。 ''' ''' 文件或文件夹完整路径。文件夹不以“\”结尾。 ''' Public Sub AutoSetup(ByVal Path As String) log("[System] 安装文件请求:" & Path) Try '基础检测 If Directory.Exists(Path) Then '文件夹 Path = Path & "\" Dim a = Path & GetFileNameFromPath(Mid(Path, 1, Len(Path) - 1)) & ".jar" ReCheckDir: If File.Exists(Path & "level.dat") And Directory.Exists(Path & "region") Then '存档文件夹 Dim th As New Thread(AddressOf AutoSetupSaveFolder) th.Start(Path) ElseIf File.Exists(Path & "pack.mcmeta") And Directory.Exists(Path & "assets") Then '资源包文件夹 Dim th As New Thread(AddressOf AutoSetupResFolder) th.Start(Path) ElseIf File.Exists(Path & GetFileNameFromPath(Mid(Path, 1, Len(Path) - 1)) & ".jar") And File.Exists(Path & GetFileNameFromPath(Mid(Path, 1, Len(Path) - 1)) & ".json") Then '版本文件夹 Dim th As New Thread(AddressOf AutoSetupVersionFolder) th.Start(Path) Else '进一层鉴别 Dim AllDir As String() = Directory.GetDirectories(Path) If AllDir.Length = 1 Then '发现进一层文件夹 Path = AllDir(0) & "\" log("[System] 进一层的文件夹:" & Path) GoTo ReCheckDir Else '没有进一层文件夹 ShowHint(New HintConverter("自动安装失败:不支持的文件夹", HintState.Warn)) Exit Sub End If End If ElseIf File.Exists(Path) Then '文件 '获取后缀 Dim SplitResult As String() = Path.Split(".") Dim EndDot As String = If(SplitResult.Length > 1, SplitResult(SplitResult.Length - 1), "") '判断后缀 If EndDot = "zip" Then 'zip处理线程 Dim th As New Thread(Sub() Try Using zip As New ZipFile(Path, Encoding.Default) Dim BasePath As String = "" '基础路径,用于一层嵌套的文件 ReCheckZip: '检查是否为版本文件夹 Dim LastPath As String If BasePath = "" Then LastPath = GetFileNameFromPath(Path).Replace(".zip", "") Else LastPath = Mid(BasePath, 1, Len(BasePath) - 1).Split("/")(Mid(BasePath, 1, Len(BasePath) - 1).Split("/").Length - 1) End If log("[System] 末端文件夹名:" & LastPath) '文件检查 If zip.ContainsEntry(BasePath & "level.dat") And zip.ContainsEntry(BasePath & "region/") Then '存档zip AutoSetupSaveZip(zip, BasePath, Path) ElseIf zip.ContainsEntry(BasePath & "pack.mcmeta") And zip.ContainsEntry(BasePath & "assets/") Then '资源包zip If BasePath = "" Then AutoSetupResZip(Path) Else ShowHint(New HintConverter("自动安装失败:资源包压缩包不支持内置文件夹", HintState.Warn)) Exit Sub End If ElseIf zip.ContainsEntry(BasePath & LastPath & ".jar") And zip.ContainsEntry(BasePath & LastPath & ".json") Then '版本zip AutoSetupVersionZip(zip, BasePath, Path) Else '进一层鉴别 Dim AllDir As New ArrayList For Each Entry As String In zip.EntryFileNames If Entry.IndexOf("/") = Len(Entry) - 1 Then AllDir.Add(Entry) Next If AllDir.Count = 1 Then BasePath = AllDir(0) log("[System] 进一层的压缩文件路径:" & BasePath) GoTo ReCheckZip Else ShowHint(New HintConverter("自动安装失败:不支持的文件", HintState.Warn)) Exit Sub End If End If End Using Catch ex As Exception ShowHint(New HintConverter("自动安装失败:" & GetStringFromException(ex), HintState.Warn)) End Try End Sub) th.Start() ElseIf EndDot = "json" Or EndDot = "jar" Then '版本文件 AutoSetupVersionFile(Path, EndDot) ElseIf EndDot = "rar" Or EndDot = "7z" Then ShowHint(New HintConverter("自动安装失败:PCL暂时只支持.zip文件", HintState.Warn)) Exit Sub Else ShowHint(New HintConverter("自动安装失败:未知的文件后缀", HintState.Warn)) Exit Sub End If Else ShowHint(New HintConverter("自动安装失败:它既不是文件也不是文件夹", HintState.Warn)) Exit Sub End If Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try End Sub ''' ''' 自动安装存档文件夹。需要异步调用。 ''' ''' 文件夹路径,以“\”结尾。 ''' Public Sub AutoSetupSaveFolder(ByVal DirPath As String) Try log("[System] 存档文件夹:" & DirPath) Dim SaveName As String = GetFileNameFromPath(Left(DirPath, Len(DirPath) - 1)) Dim TargetPath As String = PATH_MC & "saves\" & SaveName & "\" Directory.CreateDirectory(PATH_MC & "saves\") If TargetPath = DirPath Then Exit Sub '路径相同 '已存在检测 Recheck: If Directory.Exists(TargetPath) Then Select Case AutoSetupCheck("存档 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath, True) Case 1 '替换 ShowHint("正在删除原存档:" & SaveName) Directory.Delete(TargetPath, True) Case 2 '重命名 TargetPath = Left(TargetPath, Len(TargetPath) - 1) & "-\" SaveName = SaveName & "-" GoTo Recheck Case 3 '取消 Exit Sub End Select End If ShowHint("正在安装存档:" & SaveName) Directory.CreateDirectory(TargetPath) My.Computer.FileSystem.CopyDirectory(DirPath, TargetPath) ShowHint(New HintConverter("安装存档成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try End Sub ''' ''' 自动安装资源包文件夹。需要异步调用。 ''' ''' 文件夹路径,以“\”结尾。 ''' Public Sub AutoSetupResFolder(ByVal DirPath As String) Try log("[System] 资源包文件夹:" & DirPath) Dim SaveName As String = GetFileNameFromPath(Left(DirPath, Len(DirPath) - 1)) Dim TargetPath As String = PATH_MC & "resourcepacks\" & SaveName & "\" Directory.CreateDirectory(PATH_MC & "resourcepacks\") If TargetPath = DirPath Then Exit Sub '路径相同 '已存在检测 Recheck: If Directory.Exists(TargetPath) Then Select Case AutoSetupCheck("资源包 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath) Case 1 '替换 ShowHint("正在删除原资源包:" & SaveName) Directory.Delete(TargetPath, True) Case 2 '取消 Exit Sub End Select End If ShowHint("正在安装资源包:" & SaveName) Directory.CreateDirectory(TargetPath) My.Computer.FileSystem.CopyDirectory(DirPath, TargetPath) ShowHint(New HintConverter("安装资源包成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try End Sub ''' ''' 自动安装版本文件夹。需要异步调用。 ''' ''' 文件夹路径,以“\”结尾。 ''' Public Sub AutoSetupVersionFolder(ByVal DirPath As String) Try log("[System] 版本文件夹:" & DirPath) Dim SaveName As String = GetFileNameFromPath(Left(DirPath, Len(DirPath) - 1)) Dim TargetPath As String = PATH_MC & "versions\" & SaveName & "\" Directory.CreateDirectory(PATH_MC & "versions\") If TargetPath = DirPath Then Exit Sub '路径相同 '已存在检测 Recheck: If Directory.Exists(TargetPath) Then Select Case AutoSetupCheck("版本 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath) Case 1 '替换 ShowHint("正在删除原版本:" & SaveName) Directory.Delete(TargetPath, True) Case 2 '取消 Exit Sub End Select End If ShowHint("正在安装版本:" & SaveName) Directory.CreateDirectory(TargetPath) My.Computer.FileSystem.CopyDirectory(DirPath, TargetPath) ShowHint(New HintConverter("安装版本成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try '强制重载版本列表 WriteIni(PATH_MC & "PCL.ini", "LastCheckedFolder", "") Pool.Add(New Thread(AddressOf PoolVersionList)) End Sub ''' ''' 自动安装版本文件。需要异步调用。 ''' ''' 文件路径。 ''' 文件后缀。不包含小数点。 ''' Public Sub AutoSetupVersionFile(ByVal FilePath As String, ByVal EndDot As String) Try log("[System] 版本文件:" & FilePath) Dim SaveName As String = GetFileNameFromPath(FilePath).Replace("." & EndDot, "") Dim TargetPath As String = PATH_MC & "versions\" & SaveName & "\" & SaveName & "." & EndDot Directory.CreateDirectory(PATH_MC & "versions\") If TargetPath = FilePath Then Exit Sub '路径相同 '已存在检测 Recheck: If Directory.Exists(PATH_MC & "versions\" & SaveName & "\") Then If File.Exists(TargetPath) Then Select Case AutoSetupCheck("版本文件 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath, True) Case 1 '替换 ShowHint("正在删除原版本文件:" & SaveName) File.Delete(TargetPath) Case 2 '重命名 SaveName = SaveName & "-" TargetPath = PATH_MC & "versions\" & SaveName & "\" & SaveName & "." & EndDot GoTo Recheck Case 3 '取消 Exit Sub End Select End If End If ShowHint("正在安装版本文件:" & SaveName) Directory.CreateDirectory(PATH_MC & "versions\" & SaveName & "\") File.Copy(FilePath, TargetPath) ShowHint(New HintConverter("安装版本文件成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try '强制重载版本列表 WriteIni(PATH_MC & "PCL.ini", "LastCheckedFolder", "") Pool.Add(New Thread(AddressOf PoolVersionList)) End Sub ''' ''' 自动安装存档zip压缩包。需要异步调用。 ''' ''' ZipFile类型的已经打开的压缩文件对象。 ''' 在压缩文件中的基础路径。 ''' Public Sub AutoSetupSaveZip(ByVal Zip As ZipFile, ByVal BasePath As String, ByVal FilePath As String) Try log("[System] 存档zip" & If(BasePath = "", "", ":" & BasePath)) Dim SaveName As String = If(BasePath = "", GetFileNameFromPath(FilePath).Replace(".zip", ""), Left(BasePath, Len(BasePath) - 1)) Dim TargetPath As String = PATH_MC & "saves\" & SaveName & "\" Directory.CreateDirectory(PATH_MC & "saves\") '已存在检测 Dim Renamed As Boolean = False Recheck: If Directory.Exists(TargetPath) Then Select Case AutoSetupCheck("存档 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath, True) Case 1 '替换 ShowHint("正在删除原存档:" & SaveName) Directory.Delete(TargetPath, True) Case 2 '重命名 TargetPath = Left(TargetPath, Len(TargetPath) - 1) & "-\" SaveName = SaveName & "-" Renamed = True GoTo Recheck Case 3 '取消 Exit Sub End Select End If ShowHint("正在安装存档:" & SaveName) Directory.CreateDirectory(TargetPath) If BasePath = "" Then Zip.ExtractAll(TargetPath, ExtractExistingFileAction.OverwriteSilently) Else Zip.ExtractAll(PATH, ExtractExistingFileAction.OverwriteSilently) My.Computer.FileSystem.CopyDirectory(PATH & Left(BasePath, Len(BasePath) - 1), TargetPath) Directory.Delete(PATH & Left(BasePath, Len(BasePath) - 1), True) End If ShowHint(New HintConverter("安装存档成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try End Sub ''' ''' 自动安装资源包zip压缩包。需要异步调用。 ''' ''' Zip文件路径。 ''' Public Sub AutoSetupResZip(ByVal FilePath As String) Try log("[System] 资源包zip:" & FilePath) Dim SaveName As String = GetFileNameFromPath(FilePath) Dim TargetPath As String = PATH_MC & "resourcepacks\" & SaveName Directory.CreateDirectory(PATH_MC & "resourcepacks\") If TargetPath = FilePath Then Exit Sub '路径相同 '已存在检测 Recheck: If File.Exists(TargetPath) Then Select Case AutoSetupCheck("资源包 " & SaveName & " 已经存在。" & vbCrLf & "位置:" & TargetPath) Case 1 '替换 ShowHint("正在删除原资源包:" & SaveName) File.Delete(TargetPath) Case 2 '取消 Exit Sub End Select End If ShowHint("正在安装资源包:" & SaveName) File.Copy(FilePath, TargetPath) ShowHint(New HintConverter("安装资源包成功:" & SaveName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try End Sub ''' ''' 自动安装版本zip压缩包。需要异步调用。 ''' ''' ZipFile类型的已经打开的压缩文件对象。 ''' 在压缩文件中的基础路径。 ''' Public Sub AutoSetupVersionZip(ByVal Zip As ZipFile, ByVal BasePath As String, ByVal FilePath As String) Try log("[System] 版本zip" & If(BasePath = "", "", ":" & BasePath)) Dim VersionName As String = If(BasePath = "", GetFileNameFromPath(FilePath).Replace(".zip", ""), Left(BasePath, Len(BasePath) - 1)) Dim TargetPath As String = PATH_MC & "versions\" & VersionName & "\" Directory.CreateDirectory(PATH_MC & "versions\") '已存在检测 Dim Renamed As Boolean = False Recheck: If Directory.Exists(TargetPath) Then Select Case AutoSetupCheck("版本 " & VersionName & " 已经存在。" & vbCrLf & "位置:" & TargetPath) Case 1 '替换 ShowHint("正在删除原版本:" & VersionName) Directory.Delete(TargetPath, True) Case 2 '取消 Exit Sub End Select End If ShowHint("正在安装版本:" & VersionName) Directory.CreateDirectory(TargetPath) If BasePath = "" Then Zip.ExtractAll(TargetPath, ExtractExistingFileAction.OverwriteSilently) Else Zip.ExtractAll(PATH, ExtractExistingFileAction.OverwriteSilently) My.Computer.FileSystem.CopyDirectory(PATH & Left(BasePath, Len(BasePath) - 1), TargetPath) Directory.Delete(PATH & Left(BasePath, Len(BasePath) - 1), True) End If ShowHint(New HintConverter("安装版本成功:" & VersionName, HintState.Finish)) Catch ex As Exception ExShow(ex, "自动安装失败", ErrorLevel.AllUsers) End Try '强制重载版本列表 WriteIni(PATH_MC & "PCL.ini", "LastCheckedFolder", "") Pool.Add(New Thread(AddressOf PoolVersionList)) End Sub ''' ''' 弹出自动安装的确认消息。需要异步调用。 ''' ''' 显示的文本。 ''' 是否显示重命名选项。 ''' 用户做出的是否安装的决定。 ''' Public Function AutoSetupCheck(ByVal Text As String, Optional ByVal CanRename As Boolean = False) As Integer Return MyMsgbox(Text, "自动安装确认", "覆盖", If(CanRename, "重命名", "取消"), If(CanRename, "取消", ""), True) End Function #End Region #Region "设置" ''' ''' 切换页面隐藏。 ''' ''' Public Sub SetupRefreshHidden() frmMain.btnTopDown.Visibility = If(ReadIni("setup", "UiHiddenDownload", "False") = "True", Visibility.Collapsed, Visibility.Visible) frmMain.btnTopSetup.Visibility = If(ReadIni("setup", "UiHiddenSetup", "False") = "True", Visibility.Collapsed, Visibility.Visible) End Sub ''' ''' 切换背景图片的展示方式。 ''' ''' Public Sub SetupRefreshBackground() Select Case Val(ReadIni("setup", "UiBackgroundShow", "0")) Case 0 '右下 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Right frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Bottom frmMain.imgMainBg.Stretch = Stretch.None Case 1 '左上 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Left frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Top frmMain.imgMainBg.Stretch = Stretch.None Case 2 '右上 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Right frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Top frmMain.imgMainBg.Stretch = Stretch.None Case 3 '居中 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Center frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Center frmMain.imgMainBg.Stretch = Stretch.None Case 4 '拉伸 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Stretch frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Stretch frmMain.imgMainBg.Stretch = Stretch.Fill Case 5 '适应 frmMain.imgMainBg.HorizontalAlignment = HorizontalAlignment.Stretch frmMain.imgMainBg.VerticalAlignment = VerticalAlignment.Stretch frmMain.imgMainBg.Stretch = Stretch.UniformToFill End Select End Sub ''' ''' 根据进行版本隔离返回某版本所需的游戏目录,以“\”结尾。 ''' ''' ''' ''' Public Function GetVersionFolder(ByVal Version As MCVersion) As String Select Case Val(ReadIni("setup", "LaunchSplit", "0")) Case 0 Return PATH_MC Case 1 Return If(Version.Type = MCVersionType.FORGE, Version.Path, PATH_MC) Case Else Return Version.Path End Select End Function #End Region #Region "提示" ''' ''' 等待弹出的提示文本。 ''' ''' Public WaitingHint As ArrayList = If(IsNothing(WaitingHint), New ArrayList, WaitingHint) ''' ''' 在窗口左下角弹出提示文本。 ''' ''' 要显示的提示。 ''' Public Sub ShowHint(ByVal Hint As HintConverter) If IsNothing(WaitingHint) Then WaitingHint = New ArrayList 'MDZZ这初始化也太坑了 For Each NowHint As HintConverter In WaitingHint If Hint.Text = NowHint.Text Then Exit Sub Next WaitingHint.Add(Hint) End Sub ''' ''' 在窗口左下角弹出提示文本。 ''' ''' 要显示的提示。 ''' Public Sub ShowHint(ByVal Text As String) ShowHint(New HintConverter(Text)) End Sub ''' ''' 等待弹出的提示窗口。 ''' ''' Public WaitingHintWindow As ArrayList = If(IsNothing(WaitingHintWindow), New ArrayList, WaitingHintWindow) ''' ''' 弹出提示窗口。 ''' ''' 要显示的提示文本。 ''' Public Sub ShowHintWindow(ByVal Title As String, ByVal Hint As String) frmMain.Dispatcher.Invoke(Sub() If IsNothing(WaitingHintWindow) Then WaitingHintWindow = New ArrayList '初始化 If Not WaitingHintWindow.Contains({Title, Hint}) Then WaitingHintWindow.Add({Title, Hint}) End If End Sub) End Sub #End Region #Region "统计与反馈" Public SendInvokes As New ArrayList Private LastPage As String = QN_SERVER & "stats.html" ''' ''' 发送页面数据。 ''' ''' 统计的页面。 ''' Public Sub SendPage(ByVal Name As String) If MODE_DEVELOPER Then log("[System] 发送页面统计:" & Name) If MODE_OFFLINE Or MODE_DEVELOPER Then Exit Sub SendInvokes.Add("try{_czc.push([""_trackPageview"",""/" & Name & ".html"",""" & LastPage & """]);}catch (e){}") LastPage = QN_SERVER & Name & ".html" End Sub ''' ''' 发送事件统计。 ''' ''' Public Sub SendStat(ByVal MainType As String, ByVal Action As String, Optional ByVal Lab As String = "", Optional ByVal Number As Integer = 0) Try MainType = MainType.Replace(vbCrLf, "").Replace("""", " ").Replace("\", "/") Action = Action.Replace(vbCrLf, "").Replace("""", " ").Replace("\", "/") Lab = Lab.Replace(vbCrLf, "").Replace("""", " ").Replace("\", "/") If (Not AllowFeedback) And (Not MainType = "推荐源") Then Exit Sub If MODE_DEVELOPER Then log("[System] 发送事件统计:" & MainType & " > " & Action & If(Lab = "", "", " > " & Lab) & "(" & Number & ")") If MODE_OFFLINE Or MODE_DEVELOPER Then Exit Sub SendInvokes.Add("try{_czc.push([""_trackEvent"",""" & MainType & """,""" & Action & """,""" & Lab & """," & Number & ",]);}catch (e){}") Catch End Try End Sub ''' ''' 处理错误信息。 ''' ''' ''' 描述文本,如“下载文件时出错”。 ''' 错误的严重程度。 ''' Public Sub ExShow(ByVal ex As Exception, ByVal description As String, Optional ByVal errorLevel As ErrorLevel = ErrorLevel.DebugOnly, Optional ByVal msgboxText As String = "") On Error Resume Next '发送统计 If Not description.Contains("下载文件失败") Then SendStat("错误", GetStringFromEnum(errorLevel), description & ":" & GetStringFromException(ex), errorLevel) '处理错误 Select Case errorLevel Case modMain.ErrorLevel.Slient '不提示用户 log("[Error] " & description & ":" & GetStringFromException(ex, MODE_DEVELOPER),, True) Case modMain.ErrorLevel.DebugOnly '提示调试模式用户 log("[Error] " & description & ":" & GetStringFromException(ex, True), True) Case modMain.ErrorLevel.AllUsers '提示所有用户 log("[Error] " & description & ":" & GetStringFromException(ex, True)) ShowHint(New HintConverter(description & ":" & GetStringFromException(ex), HintState.Critical)) Case modMain.ErrorLevel.MsgboxWithoutFeedback '弹窗,不要求反馈 MyMsgbox(If(Not msgboxText = "", msgboxText & vbCrLf & vbCrLf, "") & "详细的错误信息:" & GetStringFromException(ex, True), description, IsWaitExit:=False) Case modMain.ErrorLevel.MsgboxAndFeedback '弹窗,要求反馈 If MyMsgbox(If(Not msgboxText = "", msgboxText & vbCrLf & vbCrLf, "") & "详细的错误信息:" & GetStringFromException(ex, True) & vbCrLf & vbCrLf & "你是否愿意反馈这个问题?", description, "反馈", "不反馈") = 1 Then Feedback() End If Case modMain.ErrorLevel.Barrier '尽可能显示错误信息,然后关闭程序 If MsgBox(If(Not msgboxText = "", msgboxText & vbCrLf & vbCrLf, "") & "详细的错误信息:" & GetStringFromException(ex, True) & vbCrLf & vbCrLf & "你是否愿意反馈这个问题?", MsgBoxStyle.YesNo + MsgBoxStyle.Critical, description) = MsgBoxResult.Yes Then Feedback() End If End End Select End Sub Public Enum ErrorLevel As Integer ''' ''' 正常情况下的常发错误。只记录 Log,不提示用户。 ''' ''' Slient = 0 ''' ''' 只需要提示调试模式下的用户。 ''' ''' DebugOnly = 1 ''' ''' 需要以弹出提示的方式提示所有用户。 ''' ''' AllUsers = 2 ''' ''' 需要以弹窗的方式提示所有用户,但是不要求反馈。 ''' ''' MsgboxWithoutFeedback = 3 ''' ''' 需要以弹窗的方式提示所有用户,并且要求反馈。 ''' ''' MsgboxAndFeedback = 4 ''' ''' 尽可能弹出提示,然后结束程序。 ''' ''' Barrier = 5 End Enum ''' ''' 开始用户反馈。本函数会直接打开log文件与反馈网页。 ''' Public Sub Feedback() If MyMsgbox("PCL2 正在制作中,因此 PCL1 已经停止更新与维护,反馈通道已经关闭。" & vbCrLf & "你可在 http://afdian.net/@LTCat 查看 PCL2 当前制作进度。(制作进度日更,制作完成后依然免费下载)", "反馈提示", "查看 PCL2 制作进度", "返回") = 1 Then Process.Start("https://afdian.net/@LTCat") End If 'Process.Start("https://www.wjx.cn/jq/14677608.aspx") 'Shell("notepad", PATH & "PCL\log.txt") End Sub Public Sub FeedbackSetup() Try SendStat("反馈", "设置等级", ReadReg("SysLevel", "Default (0)"), ReadReg("SysLevel", "0")) SendStat("反馈", "调试模式", ReadReg("SysTest", "Default (False)"), ReadReg("SysTest", "False") = "True") SendStat("反馈", "检查更新", ReadIni("setup", "SysUpdate", "Default (True)"), ReadIni("setup", "SysUpdate", "True") = "True") SendStat("反馈", "有更新时是否只提示", ReadIni("setup", "SysUpdateHint", "Default (True)"), ReadIni("setup", "SysUpdateHint", "True") = "True") SendStat("反馈", "低内存警告", ReadIni("setup", "raSystemWarn", "Default (100)"), ReadIni("setup", "raSystemWarn", "100")) SendStat("反馈", "接收测试版更新", ReadIni("setup", "SysUpdateTest", "Default (False)"), ReadIni("setup", "SysUpdateTest", "False") = "True") SendStat("反馈", "启动器主题", ReadIni("setup", "UiTheme", "Default (0)"), ReadIni("setup", "UiTheme", "0")) SendStat("反馈", "启用自定义背景", File.Exists(PATH & "PCL\back.png"), File.Exists(PATH & "PCL\back.png")) SendStat("反馈", "启用自定义顶部栏", File.Exists(PATH & "PCL\top.png"), File.Exists(PATH & "PCL\top.png")) SendStat("反馈", "启用 BGM", File.Exists(PATH & "PCL\bgm.wav") Or File.Exists(PATH & "PCL\bgm.mp3"), File.Exists(PATH & "PCL\bgm.wav") Or File.Exists(PATH & "PCL\bgm.mp3")) SendStat("反馈", "背景透明度", ReadIni("setup", "UiBackgroundOpacity", "Default (50)"), ReadIni("setup", "UiBackgroundOpacity", "50")) SendStat("反馈", "启动器透明度", ReadIni("setup", "UiLauncherOpacity", "Default (100)"), ReadIni("setup", "UiLauncherOpacity", "100")) SendStat("反馈", "游戏窗口置顶", ReadReg("LaunchTopmost", "Default (False)"), ReadReg("LaunchTopmost", "False") = "True") SendStat("反馈", "启动器可见性", ReadIni("setup", "LaunchVisibility", "Default (3)"), ReadIni("setup", "LaunchVisibility", "3")) SendStat("反馈", "游戏进程优先级", ReadReg("LaunchLevel", "Default (1)"), ReadReg("LaunchLevel", "1")) SendStat("反馈", "显示游戏日志", ReadIni("setup", "LaunchLog", "Default (False)"), ReadIni("setup", "LaunchLog", "False") = "True") SendStat("反馈", "离线皮肤", ReadReg("LaunchSkin", "Default (3)"), ReadReg("LaunchSkin", "3")) SendStat("反馈", "离线皮肤正版名", ReadReg("LaunchSkinName", "Default (None)")) SendStat("反馈", "版本隔离", ReadIni("setup", "LaunchSplit", "Default (0)"), ReadIni("setup", "LaunchSplit", "0")) SendStat("反馈", "游戏窗口模式", ReadIni("setup", "LaunchMode", "Default (0)"), ReadIni("setup", "LaunchMode", "0")) SendStat("反馈", "Java 路径", ReadReg("SetupJavaPath", "Default (None)")) SendStat("反馈", "最大内存", ReadReg("LaunchMaxRam", "Default (1024)"), ReadReg("LaunchMaxRam", "1024")) SendStat("反馈", "电脑最大内存", Int(My.Computer.Info.TotalPhysicalMemory / 256 / 1024 / 1024) * 256 & "M", Int(My.Computer.Info.TotalPhysicalMemory / 256 / 1024 / 1024) * 256) SendStat("反馈", "Java 版本", GetFileVersion(PATH_JAVA & "\javaw.exe")) SendStat("反馈", "游戏版本列表源", ReadIni("setup", "DownVersion", "Default (0)"), ReadIni("setup", "DownVersion", "0")) SendStat("反馈", "游戏数据文件源", ReadIni("setup", "DownAssets", "Default (0)"), ReadIni("setup", "DownAssets", "0")) SendStat("反馈", "游戏组件源", ReadIni("setup", "DownMinecraft", "Default (1)"), ReadIni("setup", "DownMinecraft", "1")) SendStat("反馈", "OptiFine 源", ReadIni("setup", "DownOptiFine", "Default (1)"), ReadIni("setup", "DownOptiFine", "1")) SendStat("反馈", "Forge 源", ReadIni("setup", "DownForge", "Default (1)"), ReadIni("setup", "DownForge", "1")) SendStat("反馈", "Forge 自动安装", ReadIni("setup", "DownForgeAction", "Default (1)"), ReadIni("setup", "DownForgeAction", "1")) SendStat("反馈", "数据缺失提示", ReadIni("setup", "DownMinecraftAssetsHint", "Default (True)"), ReadIni("setup", "DownMinecraftAssetsHint", "True") = "True") SendStat("反馈", "数据缺失自动下载", ReadIni("setup", "DownMinecraftAssetsAuto", "Default (True)"), ReadIni("setup", "DownMinecraftAssetsAuto", "True") = "True") SendStat("反馈", "同时下载文件数", ReadIni("setup", "DownMaxinum", "Default (15)"), ReadIni("setup", "DownMaxinum", "15")) SendStat("反馈", "游戏更新提示", ReadIni("setup", "HomeUpdate", "Default (True)"), ReadIni("setup", "HomeUpdate", "True") = "True") SendStat("反馈", "游戏更新提示仅正式版", ReadIni("setup", "HomeUpdateRelease", "Default (False)"), ReadIni("setup", "HomeUpdateRelease", "False") = "True") SendStat("反馈", "保存密码", ReadReg("HomeSave", "Default (False)"), ReadReg("HomeSave", "False") = "True") SendStat("反馈", "自动登录", ReadIni("setup", "HomeAutologin", "Default (False)"), ReadIni("setup", "HomeAutologin", "False") = "True") SendStat("反馈", "自动播放速度", ReadIni("setup", "raHomeAutoplaySpeed", "Default (1)"), ReadIni("setup", "raHomeAutoplaySpeed", "1")) SendStat("反馈", "推荐源:PCL", ReadIni("setup", "HomePCLPush", "Default (True)"), ReadIni("setup", "HomePCLPush", "True") = "True") SendStat("反馈", "推荐源:MCBBS", ReadIni("setup", "HomeMCBBSPush", "Default (True)"), ReadIni("setup", "HomeMCBBSPush", "True") = "True") SendStat("反馈", "推荐源:贴吧", ReadIni("setup", "HomeTbPush", "Default (True)"), ReadIni("setup", "HomeTbPush", "True") = "True") SendStat("反馈", "推荐源:Forum", ReadIni("setup", "HomeForumPush", "Default (False)"), ReadIni("setup", "HomeForumPush", "True") = "True") SendStat("反馈", "推荐源:Mojang", ReadIni("setup", "HomeMojangPush", "Default (True)"), ReadIni("setup", "HomeMojangPush", "True") = "True") SendStat("反馈", "推荐源:广告", ReadIni("setup", "HomeAdPush", "Default (True)"), ReadIni("setup", "HomeAdPush", "True") = "True") SendStat("反馈", "启动器版本", VERSION_NAME, VERSION_CODE) Catch ex As Exception ExShow(ex, "发送用户反馈失败", ErrorLevel.Slient) End Try End Sub #End Region #Region "皮肤" ''' ''' 从官方 UUID 获取本地缓存的皮肤路径。如果失败返回空字符串。 ''' ''' Public Function GetCacheSkinAddressByMojangUUID(UUID As String) As String Dim SkinName = ReadIni("cache\skin\SkinName", UUID) If SkinName = "" Then Return "" ElseIf SkinName = "Steve" Or SkinName = "Alex" Then Return SkinName Else GetCacheSkinAddressByMojangUUID = PATH & "PCL\cache\skin\" & SkinName & ".png" If Not File.Exists(GetCacheSkinAddressByMojangUUID) Then Return "" End If End Function ''' ''' 下载对应官方 UUID 的皮肤并返回文件路径。如果失败返回空字符串。 ''' ''' ''' Public Function DownloadSkin(UUID As String) As String Try '尝试读取缓存的皮肤 Dim CacheSkinAddress As String = GetCacheSkinAddressByMojangUUID(UUID) If Not CacheSkinAddress = "" Then Return CacheSkinAddress '获取皮肤下载地址 '获取皮肤地址 Dim StringConv = GetWebsiteCode("https://sessionserver.mojang.com/session/minecraft/profile/" & UUID, Encoding.Default) '如果返回为空,则为离线皮肤 If StringConv = "" Then Return GetSkinTypeFromUUID(UUID) '处理皮肤地址 StringConv = StringConv.Replace(" ", "") StringConv = Mid(StringConv, StringConv.IndexOf("textures"",""value"":""") + 1).Replace("textures"",""value"":""", "") StringConv = Mid(StringConv, 1, StringConv.IndexOf("""")).Replace(" ", "") StringConv = System.Text.Encoding.GetEncoding("utf-8").GetString(Convert.FromBase64String(StringConv)) StringConv = StringConv.Replace(" ", "") If Not StringConv.Contains("""url"":""") Then Return GetSkinTypeFromUUID(UUID) StringConv = Mid(StringConv, StringConv.IndexOf("""url"":""") + 1).Replace("""url"":""", "") StringConv = Mid(StringConv, 1, StringConv.IndexOf("""")).Replace(" ", "") '下载皮肤 Dim FileName As String = Mid(StringConv, StringConv.LastIndexOf("/") + 2) If Not File.Exists(PATH & "PCL\cache\skin\" & FileName & ".png") Then DownloadFile(StringConv, PATH & "PCL\cache\skin\" & FileName & ".png" & DOWNLOADING_END) File.Delete(PATH & "PCL\cache\skin\" & FileName & ".png") FileSystem.Rename(PATH & "PCL\cache\skin\" & FileName & ".png" & DOWNLOADING_END, PATH & "PCL\cache\skin\" & FileName & ".png") log("[System] 皮肤下载成功:" & FileName) End If WriteIni("cache\skin\SkinName", UUID, FileName) Return PATH & "PCL\cache\skin\" & FileName & ".png" Catch ex As Exception ExShow(ex, "下载皮肤失败(" & UUID & ")", ErrorLevel.Slient) Return "" End Try End Function ''' ''' 获取UUID对应的离线皮肤,返回“Steve”或“Alex”。 ''' ''' ''' ''' Public Function GetSkinTypeFromUUID(ByVal UUID As String) As String If Not UUID.Length = 32 Then Return "Steve" Dim a = Integer.Parse(UUID(7), Globalization.NumberStyles.AllowHexSpecifier) Dim b = Integer.Parse(UUID(15), Globalization.NumberStyles.AllowHexSpecifier) Dim c = Integer.Parse(UUID(23), Globalization.NumberStyles.AllowHexSpecifier) Dim d = Integer.Parse(UUID(31), Globalization.NumberStyles.AllowHexSpecifier) Return If(((a Xor b) Xor (c Xor d)) Mod 2, "Alex", "Steve") End Function #End Region Public Delegate Sub RefreshThemeInvoke() ''' 刷新主题。 ''' Public Sub RefreshTheme() Try '读取数据 Dim LeftbarUri As String = PATH_IMAGE & "LeftBar-BG_Blue.png" frmMain.imgMainBg.Source = Nothing If File.Exists(PATH & "PCL\top.png") Then LeftbarUri = PATH & "PCL\top.png" If File.Exists(PATH & "PCL\back.png") Then frmMain.imgMainBg.Source = New MyBitmap(PATH & "PCL\back.png") SetupRefreshBackground() End If Select Case Val(ReadIni("setup", "UiTheme", "0")) Case 0 Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(213, 233, 255)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(106, 177, 255)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(0, 121, 255)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(0, 81, 170)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(0, 40, 85)) log("[System] 主题配色:蓝色") Case 1 LeftbarUri = PATH_IMAGE & "LeftBar-BG_Black.png" Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(212, 212, 212)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(148, 148, 148)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(127, 127, 127)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(90, 90, 90)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(42, 42, 42)) log("[System] 主题配色:黑色") Case 2 LeftbarUri = PATH_IMAGE & "LeftBar-BG_Orange.png" Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(255, 232, 213)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(255, 168, 96)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(255, 126, 21)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(191, 86, 0)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(106, 48, 0)) log("[System] 主题配色:橙色") Case 3 LeftbarUri = PATH_IMAGE & "LeftBar-BG_Green.png" Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(213, 255, 217)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(97, 233, 111)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(43, 213, 60)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(29, 141, 40)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(15, 70, 20)) log("[System] 主题配色:绿色") Case 4 Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(ReadIni("setup", "UiThemeColorR1", Application.Current.Resources("ColorE1").R), ReadIni("setup", "UiThemeColorG1", Application.Current.Resources("ColorE1").G), ReadIni("setup", "UiThemeColorB1", Application.Current.Resources("ColorE1").B))) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(ReadIni("setup", "UiThemeColorR2", Application.Current.Resources("ColorE2").R), ReadIni("setup", "UiThemeColorG2", Application.Current.Resources("ColorE2").G), ReadIni("setup", "UiThemeColorB2", Application.Current.Resources("ColorE2").B))) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(ReadIni("setup", "UiThemeColorR3", Application.Current.Resources("ColorE3").R), ReadIni("setup", "UiThemeColorG3", Application.Current.Resources("ColorE3").G), ReadIni("setup", "UiThemeColorB3", Application.Current.Resources("ColorE3").B))) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(ReadIni("setup", "UiThemeColorR4", Application.Current.Resources("ColorE4").R), ReadIni("setup", "UiThemeColorG4", Application.Current.Resources("ColorE4").G), ReadIni("setup", "UiThemeColorB4", Application.Current.Resources("ColorE4").B))) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(ReadIni("setup", "UiThemeColorR5", Application.Current.Resources("ColorE5").R), ReadIni("setup", "UiThemeColorG5", Application.Current.Resources("ColorE5").G), ReadIni("setup", "UiThemeColorB5", Application.Current.Resources("ColorE5").B))) log("[System] 主题配色:自定义") Case 100 LeftbarUri = PATH_IMAGE & "LeftBar-BG_Hunluan.png" Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(248, 231, 191)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(244, 208, 125)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(228, 165, 16)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(182, 130, 20)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(102, 71, 0)) log("[System] 主题配色:混乱") Case 101 LeftbarUri = PATH_IMAGE & "LeftBar-BG_DeathBlue.png" Application.Current.Resources("Color1") = New SolidColorBrush(Color.FromRgb(209, 234, 254)) Application.Current.Resources("Color2") = New SolidColorBrush(Color.FromRgb(71, 171, 252)) Application.Current.Resources("Color3") = New SolidColorBrush(Color.FromRgb(3, 134, 241)) Application.Current.Resources("Color4") = New SolidColorBrush(Color.FromRgb(0, 108, 194)) Application.Current.Resources("Color5") = New SolidColorBrush(Color.FromRgb(0, 73, 45)) log("[System] 主题配色:死机蓝") End Select Application.Current.Resources("ColorE1") = Application.Current.Resources("Color1").Color Application.Current.Resources("ColorE2") = Application.Current.Resources("Color2").Color Application.Current.Resources("ColorE3") = Application.Current.Resources("Color3").Color Application.Current.Resources("ColorE4") = Application.Current.Resources("Color4").Color Application.Current.Resources("ColorE5") = Application.Current.Resources("Color5").Color '改变控件图片 If Not LeftbarUri = "" Then CType(frmMain.panTop.Background, Object).ImageSource = New MyBitmap(LeftbarUri) CType(frmMain.panTop.Background, Object).Stretch = Stretch.None CType(frmMain.panTop.Background, Object).TileMode = TileMode.Tile CType(CType(frmMain.panTop.Background, Object), ImageBrush).Viewport = (New RectConverter()).ConvertFromString("0,0," & CType(CType(frmMain.panTop.Background, Object).ImageSource, ImageSource).Width & "," & CType(CType(frmMain.panTop.Background, Object).ImageSource, ImageSource).Height) '改变已有控件 'Dim E2NoAlpha As Color = Application.Current.Resources("ColorE2") 'E2NoAlpha.A = 1 'If Not IsNothing(frmHomeRight.SwapOldLab) Then CType(frmHomeRight.SwapOldLab.Background, SolidColorBrush).SetCurrentValue(SolidColorBrush.ColorProperty, E2NoAlpha) 'If Not IsNothing(frmHomeRight.SwapVersionLab) Then CType(frmHomeRight.SwapVersionLab.Background, SolidColorBrush).SetCurrentValue(SolidColorBrush.ColorProperty, E2NoAlpha) Catch ex As Exception ExShow(ex, "刷新主题失败", ErrorLevel.Barrier) End Try End Sub ''' ''' 以正常方式结束程序运行,这会输出log与执行动画。 ''' ''' Public Sub EndNormal() AniStart({ AaOpacity(frmMain, -1, 150), AaScaleTransform(frmMain.panAllBack, -0.05, 150,, New AniEaseJumpStart(0.4)), AaCode({"End"}, 250) }, "EndAll") log("[System] 收到正常关闭指令") End Sub ''' ''' 强制暴力结束程序执行。 ''' ''' Public Sub EndForce() On Error Resume Next Process.GetCurrentProcess.Kill() End My.Application.Shutdown() frmMain.Close() frmStart.Close() End Sub '动画事件 Public Sub AaCodeCall(ByVal type As String, ByVal data As Array) Select Case type Case "HideInfoBox" data(1).Hide() Case "PageChange" frmMain.PageChange(data(1)) End Select End Sub Private LogLock As New Object ''' ''' 输出Log。 ''' ''' Log文本。 ''' 是否为重要记录,如果为是则会给调试模式用户发送提示。 ''' Public Sub Log(ByVal LogText As String, Optional ByVal Notice As Boolean = False, Optional DeveloperNotice As Boolean = False) Try Dim OutputText = "[" & GetTime() & "] " & LogText Debug.WriteLine(OutputText) If Not File.Exists(PATH & "PCL\log.txt") Then File.Create(PATH & "PCL\log.txt").Dispose() End If SyncLock LogLock Using Writter As New StreamWriter(PATH & "PCL\log.txt", True) Writter.WriteLine(OutputText) Writter.Close() End Using End SyncLock If Notice And MODE_DEBUG Then ShowHint(New HintConverter("[调试模式] " & LogText.Replace("[Error] ", ""), HintState.Warn)) If DeveloperNotice And MODE_DEVELOPER Then ShowHint(New HintConverter("[开发者模式] " & LogText.Replace("[Error] ", ""), HintState.Info)) Catch End Try End Sub Public UpdateState As LoadState = LoadState.Waiting ''' ''' 下载PCL版本更新。 ''' ''' Public Sub DownloadUpdate() UpdateState = LoadState.Loading log("[System] 下载更新开始") SendStat("反馈", "升级", VERSION_NAME, VERSION_CODE) WebStart({New WebRequireFile With { .WebURLs = New ArrayList From { TX_SREVER_2 & If(ReadIni("setup", "SysUpdateTest", "False") = "True", "pcl_snapshot", "pcl_release") & ".zip", TX_SREVER_1 & If(ReadIni("setup", "SysUpdateTest", "False") = "True", "pcl_snapshot", "pcl_release") & ".zip" }, .LocalFolder = PATH & "PCL\", .LocalName = "pcl.zip", .KnownFileSize = 256 * 1024} }, "PCL 更新", Sub() '下载成功 Try If Directory.Exists(PATH & "PCL\Update") Then Directory.Delete(PATH & "PCL\Update", True) Directory.CreateDirectory(PATH & "PCL\Update") Using zip As New ZipFile(PATH & "PCL\pcl.zip", Encoding.Default) zip.ExtractAll(PATH & "PCL\Update", ExtractExistingFileAction.OverwriteSilently) End Using File.Delete(PATH & "PCL\pcl.zip") OutputFileInResource("PCLAM", PATH & "PCL\PCL Admin Manager.exe", False) Dim Arguments As String = "Auto Update:" & GetPathFromFullPath(Forms.Application.ExecutablePath) & "|" & Process.GetCurrentProcess.Id Shell(PATH & "PCL\PCL Admin Manager.exe", Arguments) UpdateState = LoadState.Loaded EndForce() Catch ex As Exception ShowHint(New HintConverter("更新失败:" & GetStringFromException(ex), HintState.Critical)) log("[System] 更新失败:" & GetStringFromException(ex, True), True) UpdateState = LoadState.Failed End Try End Sub, Sub() '下载失败 ShowHint(New HintConverter("下载更新失败", HintState.Critical)) UpdateState = LoadState.Failed End Sub, WebRequireSize.AtLeast) End Sub Private LastRefreshOffline As Boolean = False ''' ''' 刷新离线模式状态。 ''' ''' Public Sub RefreshOffline() '刷新状态 If My.Computer.Network.IsAvailable Then MODE_OFFLINE = ReadIni("setup", "SysOffline", "False") Else MODE_OFFLINE = True End If '检测是否变化 If LastRefreshOffline = MODE_OFFLINE Then Exit Sub '各种UI的变化 On Error Resume Next If Not IsNothing(frmMain.labTopVer) Then frmMain.labTopVer.Content = VERSION_NAME & If(MODE_DEVELOPER, " | 开发者模式", If(MODE_DEBUG, " | 调试模式", "")) & If(MODE_OFFLINE, " | 离线模式", "") If Not IsNothing(frmHomeLeft) Then frmHomeLeft.ChangeWidth() If MODE_OFFLINE And Not IsNothing(frmHomeRight) Then frmHomeRight.ChangeLoginMethod(LoginMethods.Legacy) '更新状态 LastRefreshOffline = MODE_OFFLINE End Sub ''' ''' 检查MC版本。 ''' ''' 至少包含完整路径的MCVersion类。 ''' 是否对依赖版本进行递归检查。 ''' Public Sub CheckMCVersion(ByRef Version As MCVersion, Optional ByVal CheckInherit As Boolean = False) '检查Json文件 If Not File.Exists(Version.Path & Version.Name & ".json") Then If File.Exists(Version.Path & Version.Name & ".jar") Then '存在Jar,但是不存在Json Version.VersionCheckResult = VersionCheckState.JSON_NOT_EXIST Else 'Jar与Json均不存在 Version.VersionCheckResult = VersionCheckState.EMPTY_FOLDER End If Exit Sub End If '尝试读取Json Try If Version.JsonText.Length = 0 Then End If Catch Version.VersionCheckResult = VersionCheckState.JSON_CANT_READ Exit Sub End Try '检查Assets与依赖版本 Try Version.InheritVersion = If(Version.Json("inheritsFrom"), "").ToString If IsNothing(Version.Json("assets")) Then If Version.InheritVersion = "" Then Version.Assets = "" Else Version.Assets = If(New MCVersion(Version.InheritVersion).Json("assets"), "").ToString End If Else Version.Assets = Version.Json("assets") End If Catch Version.Assets = "" End Try If Not Version.InheritVersion = "" Then '存在依赖版本 If Not Directory.Exists(PATH_MC & "versions\" & Version.InheritVersion) Then '依赖版本不存在 Version.VersionCheckResult = VersionCheckState.INHERITS_NOT_EXIST Exit Sub End If '检查依赖版本问题 If CheckInherit Then Dim InheritVersion As New MCVersion(Version.InheritVersion) CheckMCVersion(InheritVersion) If Not InheritVersion.VersionCheckResult = VersionCheckState.NO_PROBLEM Then '依赖版本存在问题 Version.VersionCheckResult = VersionCheckState.INHERITS_EXCEPTION Exit Sub End If End If End If '获取全部引用 Dim LibrariesName As New ArrayList Try For Each File As JToken In Version.Json("libraries") LibrariesName.Add(File("name").ToString) Next Catch Version.VersionCheckResult = VersionCheckState.JSON_CANT_READ Exit Sub End Try '获取主Jar路径 For Each Library As String In LibrariesName If ReadIni("setup", "LaunchMending", "True") Then '允许自动修复 If Library.Contains("net.minecraftforge") Or Library.Contains("optifine:OptiFine") Then Version.VersionCheckResult = VersionCheckState.NO_PROBLEM Exit Sub End If Else '不允许自动修复 If Library.Contains("net.minecraftforge") Or Library.Contains("optifine:OptiFine") Then '找到主Jar If File.Exists(GetPathFromLibrary(Library)) Then Version.VersionCheckResult = VersionCheckState.NO_PROBLEM Else Version.VersionCheckResult = VersionCheckState.JAR_NOT_EXIST End If Exit Sub End If End If Next '主Jar为原版 If File.Exists(Version.Path & Version.Name & ".jar") Then Version.VersionCheckResult = VersionCheckState.NO_PROBLEM Else Version.VersionCheckResult = VersionCheckState.JAR_NOT_EXIST End If End Sub ''' ''' 从Json文件中的Library名获取文件所在的完整路径。 ''' ''' ''' ''' Public Function GetPathFromLibrary(ByVal Library As String) As String Dim SourceArray = Library.Split(":") Return PATH_MC & "libraries\" & SourceArray(0).Replace(".", "\") & "\" & SourceArray(1) & "\" & SourceArray(2) & "\" & SourceArray(1) & "-" & SourceArray(2) & ".jar" End Function Private IsSettingJavaEnvironment As Boolean = False ''' ''' 配置Java运行环境。必须在非主线程调用。 ''' ''' Public Sub SetJavaEnvironment() If IsSettingJavaEnvironment Then Exit Sub IsSettingJavaEnvironment = True '处理环境变量字符串,包括去重、去空白 Dim Env As String = PathEnv Dim EnvironArray As Object() = ArrayNoDouble(Split(Env.Replace(";;", ";") & ";" & PATH_JAVA, ";")).ToArray Dim NewEnv As String = Join(EnvironArray, ";").Replace(";;", ";") Try log("[System] 设置环境变量:" & NewEnv) OutputFileInResource("PCLAM", PATH & "PCL\PCL Admin Manager.exe", False) Process.Start(PATH & "PCL\PCL Admin Manager.exe", "Set Environment Variable:" & NewEnv) PathEnv = NewEnv ShowHint(New HintConverter("正在配置游戏环境,如果有安全提示请允许!(如果反复出现本提示,重启电脑即可)", HintState.Warn)) Catch ex As Exception PATH_JAVA = "" ExShow(ex, "设置环境变量时出现异常") ShowHint(New HintConverter("配置游戏环境失败:" & GetStringFromException(ex) & "(尝试重启电脑或关闭杀毒软件)", HintState.Critical)) IsSettingJavaEnvironment = False Exit Sub End Try ''等待并检查 'For i As Integer = 1 To 10 ' Dim a = PathEnv ' Dim b = Mid(PATH_JAVA, 1, Len(PATH_JAVA) - 1) ' If PathEnv.Contains(Mid(PATH_JAVA, 1, Len(PATH_JAVA) - 1)) Then ' ShowHint(New HintConverter("配置游戏环境成功!", HintState.FINISH)) ' IsSettingJavaEnvironment = False ' Exit Sub ' Else ' Thread.Sleep(2000) ' End If 'Next i 'ShowHint(New HintConverter("配置游戏环境失败!请尝试重启PCL或关闭杀毒软件。", HintState.CRITICAL)) IsSettingJavaEnvironment = False End Sub End Module