ModCrash.vb 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  1. Public Class CrashAnalyzer
  2. '构造函数
  3. Private TempFolder As String
  4. Private TargetVersion As McVersion
  5. Public Sub New(Optional TargetVersion As McVersion = Nothing)
  6. Me.TargetVersion = TargetVersion
  7. '构建文件结构
  8. TempFolder = RequestTaskTempFolder()
  9. Directory.CreateDirectory(TempFolder & "Temp\")
  10. Directory.CreateDirectory(TempFolder & "Report\")
  11. Log("[Crash] 崩溃分析暂存文件夹:" & TempFolder)
  12. End Sub
  13. '1:准备用于分析的 Log 文件
  14. Private AnalyzeRawFiles As New List(Of KeyValuePair(Of String, String())) '暂存的日志文件:文件完整路径 -> 文件内容
  15. ''' <summary>
  16. ''' 将可用于分析的日志存储到 AnalyzeRawFiles。
  17. ''' </summary>
  18. ''' <param name="LatestLog">从 PCL 捕获到的最后 200 行程序输出。</param>
  19. Public Sub Collect(VersionPathIndie As String, Optional LatestLog As IList(Of String) = Nothing)
  20. Log("[Crash] 步骤 1:收集日志文件")
  21. '简单收集可能的日志文件路径
  22. Dim PossibleLogs As New List(Of String)
  23. Try
  24. Dim DirInfo As New DirectoryInfo(VersionPathIndie & "crash-reports\")
  25. If DirInfo.Exists Then
  26. For Each File In DirInfo.EnumerateFiles
  27. PossibleLogs.Add(File.FullName)
  28. Next
  29. End If
  30. Catch ex As Exception
  31. Log(ex, "收集 Minecraft 崩溃日志文件夹下的日志失败")
  32. End Try
  33. Try
  34. For Each File In New DirectoryInfo(VersionPathIndie).Parent.Parent.EnumerateFiles
  35. If If(File.Extension, "") <> ".log" Then Continue For
  36. PossibleLogs.Add(File.FullName)
  37. Next
  38. Catch ex As Exception
  39. Log(ex, "收集 Minecraft 主文件夹下的日志失败")
  40. End Try
  41. Try
  42. For Each File In New DirectoryInfo(VersionPathIndie).EnumerateFiles
  43. If If(File.Extension, "") <> ".log" Then Continue For
  44. PossibleLogs.Add(File.FullName)
  45. Next
  46. Catch ex As Exception
  47. Log(ex, "收集 Minecraft 隔离文件夹下的日志失败")
  48. End Try
  49. PossibleLogs.Add(VersionPathIndie & "logs\latest.log") 'Minecraft 日志
  50. PossibleLogs.Add(VersionPathIndie & "logs\debug.log") 'Minecraft Debug 日志
  51. PossibleLogs = PossibleLogs.Distinct.ToList
  52. '确定最新的日志文件
  53. Dim RightLogs As New List(Of String)
  54. For Each LogFile In PossibleLogs
  55. Try
  56. Dim Info As New FileInfo(LogFile)
  57. If Not Info.Exists Then Continue For
  58. Dim Time = Math.Abs((Info.LastWriteTime - Date.Now).TotalMinutes)
  59. If Time < 3 AndAlso Info.Length > 0 Then
  60. RightLogs.Add(LogFile)
  61. Log("[Crash] 可能可用的日志文件:" & LogFile & "(" & Math.Round(Time, 1) & " 分钟)")
  62. End If
  63. Catch ex As Exception
  64. Log(ex, "确认崩溃日志时间失败(" & LogFile & ")")
  65. End Try
  66. Next
  67. If Not RightLogs.Any() Then Log("[Crash] 未发现可能可用的日志文件")
  68. '将可能可用的日志文件导出
  69. For Each FilePath In RightLogs
  70. Try
  71. AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(FilePath, ReadFile(FilePath).Split(vbCrLf.ToCharArray)))
  72. Catch ex As Exception
  73. Log(ex, "读取可能的崩溃日志文件失败(" & FilePath & ")")
  74. End Try
  75. Next
  76. If LatestLog IsNot Nothing AndAlso LatestLog.Any Then
  77. Dim RawOutput As String = Join(LatestLog, vbCrLf)
  78. Log("[Crash] 以下为游戏输出的最后一段内容:" & vbCrLf & RawOutput)
  79. WriteFile(TempFolder & "RawOutput.log", RawOutput)
  80. AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TempFolder & "RawOutput.log", LatestLog.ToArray))
  81. LatestLog.Clear()
  82. End If
  83. Log("[Crash] 步骤 1:收集日志文件完成,收集到 " & AnalyzeRawFiles.Count & " 个文件")
  84. End Sub
  85. ''' <summary>
  86. ''' 从文件路径直接导入日志文件或崩溃报告压缩包。
  87. ''' </summary>
  88. Public Sub Import(FilePath As String)
  89. Log("[Crash] 步骤 1:自主导入日志文件")
  90. '尝试视作压缩包解压
  91. Try
  92. Dim Info As New FileInfo(FilePath)
  93. If Info.Exists AndAlso Info.Length > 0 AndAlso Not FilePath.EndsWithF(".jar", True) Then
  94. ExtractFile(FilePath, TempFolder & "Temp\")
  95. Log("[Crash] 已解压导入的日志文件:" & FilePath)
  96. GoTo Extracted
  97. End If
  98. Catch
  99. End Try
  100. '并非压缩包
  101. CopyFile(FilePath, TempFolder & "Temp\" & GetFileNameFromPath(FilePath))
  102. Log("[Crash] 已复制导入的日志文件:" & FilePath)
  103. Extracted:
  104. '导入其中的日志文件
  105. For Each TargetFile As FileInfo In New DirectoryInfo(TempFolder & "Temp\").EnumerateFiles.ToList()
  106. Try
  107. If Not TargetFile.Exists OrElse TargetFile.Length = 0 Then Continue For
  108. Dim Ext As String = TargetFile.Extension.ToLower
  109. If Ext = ".log" OrElse Ext = ".txt" Then
  110. AnalyzeRawFiles.Add(New KeyValuePair(Of String, String())(TargetFile.FullName, ReadFile(TargetFile.FullName).Split(vbCrLf.ToCharArray)))
  111. Else
  112. File.Delete(TargetFile.FullName)
  113. End If
  114. Catch ex As Exception
  115. Log(ex, "导入单个日志文件失败")
  116. End Try
  117. Next
  118. Log("[Crash] 步骤 1:自主导入日志文件,收集到 " & AnalyzeRawFiles.Count & " 个文件")
  119. End Sub
  120. '2:确认实际用于分析的 Log 文本
  121. Private Enum AnalyzeFileType
  122. HsErr
  123. MinecraftLog
  124. ExtraLogFile
  125. ExtraReportFile
  126. CrashReport
  127. End Enum
  128. Private DirectFile As KeyValuePair(Of String, String())? = Nothing '在弹窗中选择直接打开的文件
  129. ''' <summary>
  130. ''' 从 AnalyzeRawFiles 中提取实际有用的文本片段存储到 AnalyzeFiles,并整理可用于生成报告的文件。
  131. ''' 返回是否有足够信息可用于分析。
  132. ''' </summary>
  133. Public Function Prepare() As Boolean
  134. Log("[Crash] 步骤 2:准备日志文本")
  135. '对日志文件进行分类
  136. DirectFile = Nothing
  137. Dim AllFiles As New List(Of KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String())))
  138. For Each LogFile In AnalyzeRawFiles
  139. Dim MatchName As String = GetFileNameFromPath(LogFile.Key).ToLower
  140. Dim TargetType As AnalyzeFileType
  141. If MatchName.StartsWithF("hs_err") Then
  142. TargetType = AnalyzeFileType.HsErr
  143. DirectFile = LogFile
  144. ElseIf MatchName.StartsWithF("crash-") Then
  145. TargetType = AnalyzeFileType.CrashReport
  146. DirectFile = LogFile
  147. ElseIf MatchName = "latest.log" OrElse MatchName = "latest log.txt" OrElse
  148. MatchName = "debug.log" OrElse MatchName = "debug log.txt" OrElse
  149. MatchName = "游戏崩溃前的输出.txt" OrElse MatchName = "rawoutput.log" Then
  150. TargetType = AnalyzeFileType.MinecraftLog
  151. If DirectFile Is Nothing Then DirectFile = LogFile
  152. ElseIf MatchName = "启动器日志.txt" OrElse MatchName = "PCL2 启动器日志.txt" OrElse MatchName = "PCL 启动器日志.txt" OrElse MatchName = "log1.txt" Then
  153. If LogFile.Value.Any(Function(s) s.Contains("以下为游戏输出的最后一段内容")) Then
  154. TargetType = AnalyzeFileType.MinecraftLog
  155. If DirectFile Is Nothing Then DirectFile = LogFile
  156. Else
  157. TargetType = AnalyzeFileType.ExtraLogFile
  158. End If
  159. ElseIf MatchName.EndsWithF(".log", True) Then
  160. TargetType = AnalyzeFileType.ExtraLogFile
  161. ElseIf MatchName.EndsWithF(".txt", True) Then
  162. TargetType = AnalyzeFileType.ExtraReportFile
  163. Else
  164. Log("[Crash] " & MatchName & " 分类为 Ignore")
  165. Continue For
  166. End If
  167. If LogFile.Value.Any Then
  168. AllFiles.Add(New KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String()))(TargetType, LogFile))
  169. Log("[Crash] " & MatchName & " 分类为 " & GetStringFromEnum(TargetType))
  170. Else
  171. Log("[Crash] " & MatchName & " 由于内容为空跳过")
  172. End If
  173. Next
  174. '若只有额外日志,则将它们视作 Minecraft 日志
  175. If AllFiles.Any() AndAlso AllFiles.All(Function(p) p.Key = AnalyzeFileType.ExtraLogFile) Then
  176. Log("[Crash] 由于仅发现了额外日志,将它们视作 Minecraft 日志进行分析")
  177. AllFiles = AllFiles.Select(Function(p) New KeyValuePair(Of AnalyzeFileType, KeyValuePair(Of String, String()))(AnalyzeFileType.MinecraftLog, p.Value)).ToList
  178. End If
  179. '将分类后的文件分别写入
  180. For Each SelectType In {AnalyzeFileType.MinecraftLog, AnalyzeFileType.HsErr, AnalyzeFileType.ExtraLogFile, AnalyzeFileType.CrashReport}
  181. '获取该种类的所有文件 {文件路径 -> 文件内容行}
  182. Dim SelectedFiles As New List(Of KeyValuePair(Of String, String()))
  183. For Each File In AllFiles
  184. If SelectType = File.Key Then SelectedFiles.Add(File.Value)
  185. Next
  186. If Not SelectedFiles.Any() Then Continue For
  187. Try
  188. '根据文件类别判断
  189. Select Case SelectType
  190. Case AnalyzeFileType.HsErr, AnalyzeFileType.CrashReport
  191. '获取文件的修改日期
  192. Dim DatedFiles As New SortedList(Of Date, KeyValuePair(Of String, String()))()
  193. For Each File In SelectedFiles
  194. Try
  195. DatedFiles.Add(New FileInfo(File.Key).LastWriteTime, File)
  196. Catch ex As Exception
  197. Log(ex, "获取日志文件修改时间失败")
  198. DatedFiles.Add(New Date(1900, 1, 1), File)
  199. End Try
  200. Next
  201. '输出最新的文件
  202. Dim NewestFile As KeyValuePair(Of String, String()) = DatedFiles.Last.Value
  203. OutputFiles.Add(NewestFile.Key)
  204. If SelectType = AnalyzeFileType.HsErr Then
  205. LogHs = GetHeadTailLines(NewestFile.Value, 200, 100)
  206. Log("[Crash] 输出报告:" & NewestFile.Key & ",作为虚拟机错误信息")
  207. Log("[Crash] 导入分析:" & NewestFile.Key & ",作为虚拟机错误信息")
  208. Else
  209. LogCrash = GetHeadTailLines(NewestFile.Value, 300, 700)
  210. Log("[Crash] 输出报告:" & NewestFile.Key & ",作为 Minecraft 崩溃报告")
  211. Log("[Crash] 导入分析:" & NewestFile.Key & ",作为 Minecraft 崩溃报告")
  212. End If
  213. Case AnalyzeFileType.MinecraftLog
  214. LogMc = ""
  215. LogMcDebug = ""
  216. '创建文件名词典
  217. Dim FileNameDict As New Dictionary(Of String, KeyValuePair(Of String, String()))
  218. For Each SelectedFile In SelectedFiles
  219. FileNameDict(GetFileNameFromPath(SelectedFile.Key).ToLower) = SelectedFile
  220. OutputFiles.Add(SelectedFile.Key)
  221. Log("[Crash] 输出报告:" & SelectedFile.Key & ",作为 Minecraft 或启动器日志")
  222. Next
  223. '选择一份最佳的来自启动器的游戏日志
  224. For Each FileName As String In {"rawoutput.log", "启动器日志.txt", "log1.txt", "游戏崩溃前的输出.txt", "PCL2 启动器日志.txt", "PCL 启动器日志.txt"}
  225. If Not FileNameDict.ContainsKey(FileName) Then Continue For
  226. Dim CurrentLog = FileNameDict(FileName)
  227. '截取 “以下为游戏输出的最后一段内容” 后的内容
  228. Dim HasLauncherMark As Boolean = False
  229. For Each Line In CurrentLog.Value
  230. If HasLauncherMark Then
  231. LogMc += Line & vbLf
  232. ElseIf Line.Contains("以下为游戏输出的最后一段内容") Then
  233. HasLauncherMark = True
  234. Log("[Crash] 找到 PCL 输出的游戏实时日志头")
  235. End If
  236. Next
  237. '导入后 500 行
  238. If Not HasLauncherMark Then LogMc += GetHeadTailLines(CurrentLog.Value, 0, 500)
  239. LogMc = LogMc.TrimEnd(vbCrLf.ToCharArray)
  240. Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为启动器日志")
  241. Exit For
  242. Next
  243. '选择一份最佳的 Minecraft Log
  244. For Each FileName As String In {"latest.log", "latest log.txt", "debug.log", "debug log.txt"}
  245. If Not FileNameDict.ContainsKey(FileName) Then Continue For
  246. Dim CurrentLog = FileNameDict(FileName)
  247. LogMc += GetHeadTailLines(CurrentLog.Value, 1500, 500)
  248. Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft 日志")
  249. Exit For
  250. Next
  251. '查找 Debug Log
  252. For Each FileName As String In {"debug.log", "debug log.txt"}
  253. If Not FileNameDict.ContainsKey(FileName) Then Continue For
  254. Dim CurrentLog = FileNameDict(FileName)
  255. LogMcDebug += GetHeadTailLines(CurrentLog.Value, 1000, 0)
  256. Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为 Minecraft Debug 日志")
  257. Exit For
  258. Next
  259. '兜底
  260. If LogMc = "" Then
  261. If LogMcDebug <> "" Then '如果没有找到 Minecraft 日志,则使用 Debug 日志作为兜底
  262. LogMc = LogMcDebug
  263. ElseIf FileNameDict.Any() Then '如果都没有找到,则使用第一个文件
  264. Dim CurrentLog = FileNameDict.First.Value
  265. LogMc += GetHeadTailLines(CurrentLog.Value, 1500, 500)
  266. Log("[Crash] 导入分析:" & CurrentLog.Key & ",作为兜底日志")
  267. Else
  268. LogMc = Nothing
  269. Throw New Exception("无法找到匹配的 Minecraft Log")
  270. End If
  271. End If
  272. If LogMcDebug = "" Then LogMcDebug = Nothing
  273. Case AnalyzeFileType.ExtraLogFile, AnalyzeFileType.ExtraReportFile
  274. '全部丢过去
  275. For Each SelectedFile In SelectedFiles
  276. OutputFiles.Add(SelectedFile.Key)
  277. Log("[Crash] 输出报告:" & SelectedFile.Key & ",不用作分析")
  278. Next
  279. End Select
  280. Catch ex As Exception
  281. Log(ex, "分类处理日志文件时出错")
  282. End Try
  283. Next
  284. '结束
  285. Prepare = LogMc IsNot Nothing OrElse LogHs IsNot Nothing OrElse LogCrash IsNot Nothing
  286. If Prepare Then
  287. Log(("[Crash] 步骤 2:准备日志文本完成,找到" & If(LogMc Is Nothing, "", "游戏日志、") & If(LogMcDebug Is Nothing, "", "游戏 Debug 日志、") & If(LogHs Is Nothing, "", "虚拟机日志、") & If(LogCrash Is Nothing, "", "崩溃日志、")).TrimEnd("、") & "用作分析")
  288. Else
  289. Log("[Crash] 步骤 2:准备日志文本完成,没有任何可供分析的日志")
  290. End If
  291. End Function
  292. ''' <summary>
  293. ''' 输出字符串的前后某些行,并统一行尾为 vbLf (正则 \n)、删除空行和重复行。
  294. ''' </summary>
  295. Private Function GetHeadTailLines(Raw As String(), HeadLines As Integer, TailLines As Integer) As String
  296. If Raw.Length <= HeadLines + TailLines Then Return Join(Raw.Distinct, vbLf)
  297. Dim Lines As New List(Of String)
  298. Dim RealHeadLines As Integer = 0, ViewedLines As Integer
  299. For ViewedLines = 0 To Raw.Length - 1
  300. If Lines.Contains(Raw(ViewedLines)) Then Continue For
  301. RealHeadLines += 1
  302. Lines.Add(Raw(ViewedLines))
  303. If RealHeadLines >= HeadLines Then Exit For
  304. Next
  305. Dim RealTailLines = 0
  306. For i = Raw.Length - 1 To ViewedLines Step -1
  307. If Lines.Contains(Raw(i)) Then Continue For
  308. RealTailLines += 1
  309. Lines.Insert(RealHeadLines, Raw(i))
  310. If RealTailLines >= TailLines Then Exit For
  311. Next
  312. Dim Result As New StringBuilder
  313. For Each Line In Lines
  314. If Line = "" Then Continue For
  315. Result.Append(Line)
  316. Result.Append(vbLf)
  317. Next
  318. Return Result.ToString
  319. End Function
  320. '3:根据文本分析崩溃原因
  321. Private LogMc As String = Nothing, LogMcDebug As String = Nothing, LogHs As String = Nothing, LogCrash As String = Nothing
  322. Private LogAll As String
  323. '可能导致崩溃的原因与附加信息
  324. Private CrashReasons As New Dictionary(Of CrashReason, List(Of String))
  325. ''' <summary>
  326. ''' 导致崩溃的原因枚举。
  327. ''' </summary>
  328. Private Enum CrashReason
  329. Mod文件被解压
  330. MixinBootstrap缺失
  331. 内存不足
  332. 使用JDK
  333. 显卡不支持OpenGL
  334. 使用OpenJ9
  335. Java版本过高
  336. Java版本不兼容
  337. Mod名称包含特殊字符
  338. 显卡驱动不支持导致无法设置像素格式
  339. 极短的程序输出
  340. Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-32606
  341. AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION 'https://bugs.mojang.com/browse/MC-31618
  342. Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION
  343. 玩家手动触发调试崩溃
  344. 光影或资源包导致OpenGL1282错误
  345. 文件或内容校验失败
  346. 确定Mod导致游戏崩溃
  347. 怀疑Mod导致游戏崩溃
  348. Mod配置文件导致游戏崩溃
  349. ModMixin失败
  350. Mod加载器报错
  351. Mod初始化失败
  352. 堆栈分析发现关键字
  353. 堆栈分析发现Mod名称
  354. OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
  355. 特定方块导致崩溃
  356. 特定实体导致崩溃
  357. 材质过大或显卡配置不足
  358. 没有可用的分析文件
  359. 使用32位Java导致JVM无法分配足够多的内存
  360. Mod重复安装
  361. Mod互不兼容
  362. OptiFine与Forge不兼容
  363. Fabric报错
  364. Fabric报错并给出解决方案
  365. Forge报错
  366. 低版本Forge与高版本Java不兼容
  367. 版本Json中存在多个Forge
  368. Mod过多导致超出ID限制
  369. NightConfig的Bug
  370. ShadersMod与OptiFine同时安装
  371. Forge安装不完整
  372. Mod需要Java11
  373. Mod缺少前置或MC版本错误
  374. End Enum
  375. ''' <summary>
  376. ''' 根据 AnalyzeLogs 与可能的版本信息分析崩溃原因。
  377. ''' </summary>
  378. Public Sub Analyze(Optional Version As McVersion = Nothing)
  379. Log("[Crash] 步骤 3:分析崩溃原因")
  380. LogAll = If(LogMc, If(LogMcDebug, "")) & If(LogHs, "") & If(LogCrash, "")
  381. '1. 精准日志匹配,中/高优先级
  382. AnalyzeCrit1()
  383. If CrashReasons.Any Then GoTo Done
  384. AnalyzeCrit2()
  385. If CrashReasons.Any Then GoTo Done
  386. '2. 堆栈分析
  387. If LogAll.Contains("orge") OrElse LogAll.Contains("abric") OrElse LogAll.Contains("uilt") OrElse LogAll.Contains("iteloader") Then
  388. Dim Keywords As New List(Of String)
  389. '崩溃日志
  390. If LogCrash IsNot Nothing Then
  391. Log("[Crash] 开始进行崩溃日志堆栈分析")
  392. Keywords.AddRange(AnalyzeStackKeyword(LogCrash.BeforeFirst("System Details")))
  393. End If
  394. 'Minecraft 日志
  395. If LogMc IsNot Nothing Then
  396. Dim Fatals As List(Of String) = RegexSearch(LogMc, "/FATAL] .+?(?=[\n]+\[)")
  397. If LogMc.Contains("Unreported exception thrown!") Then Fatals.Add(LogMc.Between("Unreported exception thrown!", "at oolloo.jlw.Wrapper"))
  398. Log("[Crash] 开始进行 Minecraft 日志堆栈分析,发现 " & Fatals.Count & " 个报错项")
  399. For Each Fatal In Fatals
  400. Keywords.AddRange(AnalyzeStackKeyword(Fatal))
  401. Next
  402. End If
  403. '虚拟机日志
  404. If LogHs IsNot Nothing Then
  405. Log("[Crash] 开始进行虚拟机堆栈分析")
  406. Dim StackLogs As String = LogHs.Between("T H R E A D", "Registers:")
  407. Keywords.AddRange(AnalyzeStackKeyword(StackLogs))
  408. End If
  409. 'Mod 名称分析
  410. If Keywords.Any Then
  411. Dim Names = AnalyzeModName(Keywords)
  412. If Names Is Nothing Then
  413. AppendReason(CrashReason.堆栈分析发现关键字, Keywords)
  414. Else
  415. AppendReason(CrashReason.堆栈分析发现Mod名称, Names)
  416. End If
  417. GoTo Done
  418. End If
  419. Else
  420. Log("[Crash] 可能并未安装 Mod,不进行堆栈分析")
  421. End If
  422. '3. 精准日志匹配,低优先级
  423. AnalyzeCrit3()
  424. '输出到日志
  425. Done:
  426. If Not CrashReasons.Any() Then
  427. Log("[Crash] 步骤 3:分析崩溃原因完成,未找到可能的原因")
  428. Else
  429. Log("[Crash] 步骤 3:分析崩溃原因完成,找到 " & CrashReasons.Count & " 条可能的原因")
  430. For Each Reason In CrashReasons
  431. Log("[Crash] - " & GetStringFromEnum(Reason.Key) & If(Reason.Value.Any, "(" & Join(Reason.Value, ";") & ")", ""))
  432. Next
  433. End If
  434. End Sub
  435. ''' <summary>
  436. ''' 增加一个可能的崩溃原因。
  437. ''' </summary>
  438. Private Sub AppendReason(Reason As CrashReason, Optional Additional As ICollection(Of String) = Nothing)
  439. If CrashReasons.ContainsKey(Reason) Then
  440. If Additional IsNot Nothing Then
  441. CrashReasons(Reason).AddRange(Additional)
  442. CrashReasons(Reason) = CrashReasons(Reason).Distinct.ToList
  443. End If
  444. Else
  445. CrashReasons.Add(Reason, New List(Of String)(If(Additional, {})))
  446. End If
  447. Log("[Crash] 可能的崩溃原因:" & GetStringFromEnum(Reason) & If(Additional IsNot Nothing AndAlso Additional.Any, "(" & Join(Additional, ";") & ")", ""))
  448. End Sub
  449. Private Sub AppendReason(Reason As CrashReason, Additional As String)
  450. AppendReason(Reason, If(String.IsNullOrEmpty(Additional), Nothing, New List(Of String) From {Additional}))
  451. End Sub
  452. '具体的分析代码
  453. ''' <summary>
  454. ''' 进行精准日志匹配。匹配优先级高于堆栈分析的崩溃。
  455. ''' </summary>
  456. Private Sub AnalyzeCrit1()
  457. '空白分析
  458. If LogMc Is Nothing AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing Then
  459. AppendReason(CrashReason.没有可用的分析文件)
  460. Return
  461. End If
  462. '崩溃报告分析,高优先级
  463. If LogCrash IsNot Nothing Then
  464. If LogCrash.Contains("Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass") Then AppendReason(CrashReason.Java版本过高)
  465. End If
  466. '游戏日志分析
  467. If LogMc IsNot Nothing Then
  468. If LogMc.Contains("Found multiple arguments for option fml.forgeVersion, but you asked for only one") Then AppendReason(CrashReason.版本Json中存在多个Forge)
  469. If LogMc.Contains("The driver does not appear to support OpenGL") Then AppendReason(CrashReason.显卡不支持OpenGL)
  470. If LogMc.Contains("java.lang.ClassCastException: java.base/jdk") Then AppendReason(CrashReason.使用JDK)
  471. If LogMc.Contains("java.lang.ClassCastException: class jdk.") Then AppendReason(CrashReason.使用JDK)
  472. If LogMc.Contains("TRANSFORMER/net.optifine/net.optifine.reflect.Reflector.<clinit>(Reflector.java") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  473. If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.texture.SpriteContents.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  474. If LogMc.Contains("java.lang.NoSuchMethodError: 'java.lang.String com.mojang.blaze3d.systems.RenderSystem.getBackendDescription") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  475. If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.client.renderer.block.model.BakedQuad.<init>") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  476. If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraftforge.client.gui.overlay.ForgeGui.renderSelectedItemName") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  477. If LogMc.Contains("java.lang.NoSuchMethodError: 'void net.minecraft.server.level.DistanceManager") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  478. If LogMc.Contains("java.lang.NoSuchMethodError: 'net.minecraft.network.chat.FormattedText net.minecraft.client.gui.Font.ellipsize") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  479. If LogMc.Contains("Open J9 is not supported") OrElse LogMc.Contains("OpenJ9 is incompatible") OrElse LogMc.Contains(".J9VMInternals.") Then AppendReason(CrashReason.使用OpenJ9)
  480. If LogMc.Contains("java.lang.NoSuchFieldException: ucp") Then AppendReason(CrashReason.Java版本过高)
  481. If LogMc.Contains("because module java.base does not export") Then AppendReason(CrashReason.Java版本过高)
  482. If LogMc.Contains("java.lang.ClassNotFoundException: jdk.nashorn.api.scripting.NashornScriptEngineFactory") Then AppendReason(CrashReason.Java版本过高)
  483. If LogMc.Contains("java.lang.ClassNotFoundException: java.lang.invoke.LambdaMetafactory") Then AppendReason(CrashReason.Java版本过高)
  484. If LogMc.Contains("The directories below appear to be extracted jar files. Fix this before you continue.") Then AppendReason(CrashReason.Mod文件被解压)
  485. If LogMc.Contains("Extracted mod jars found, loading will NOT continue") Then AppendReason(CrashReason.Mod文件被解压)
  486. If LogMc.Contains("java.lang.ClassNotFoundException: org.spongepowered.asm.launch.MixinTweaker") Then AppendReason(CrashReason.MixinBootstrap缺失)
  487. If LogMc.Contains("Couldn't set pixel format") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式)
  488. If LogMc.Contains("java.lang.OutOfMemoryError") OrElse LogMc.Contains("an out of memory error") Then AppendReason(CrashReason.内存不足)
  489. If LogMc.Contains("java.lang.RuntimeException: Shaders Mod detected. Please remove it, OptiFine has built-in support for shaders.") Then AppendReason(CrashReason.ShadersMod与OptiFine同时安装)
  490. If LogMc.Contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier") OrElse LogMc.Contains("java.lang.NoSuchMethodError: 'void sun.security.util.ManifestEntryVerifier") Then AppendReason(CrashReason.低版本Forge与高版本Java不兼容)
  491. If LogMc.Contains("1282: Invalid operation") Then AppendReason(CrashReason.光影或资源包导致OpenGL1282错误)
  492. If LogMc.Contains("signer information does not match signer information of other classes in the same package") Then AppendReason(CrashReason.文件或内容校验失败, If(RegexSeek(LogMc, "(?<=class "")[^']+(?=""'s signer information)"), "").TrimEnd(vbCrLf))
  493. If LogMc.Contains("Maybe try a lower resolution resourcepack?") Then AppendReason(CrashReason.材质过大或显卡配置不足)
  494. If LogMc.Contains("java.lang.NoSuchMethodError: net.minecraft.world.server.ChunkManager$ProxyTicketManager.shouldForceTicks(J)Z") AndAlso LogMc.Contains("OptiFine") Then AppendReason(CrashReason.OptiFine导致无法加载世界)
  495. If LogMc.Contains("Unsupported class file major version") Then AppendReason(CrashReason.Java版本不兼容)
  496. If LogMc.Contains("com.electronwill.nightconfig.core.io.ParsingException: Not enough data available") Then AppendReason(CrashReason.NightConfig的Bug)
  497. If LogMc.Contains("Cannot find launch target fmlclient, unable to launch") Then AppendReason(CrashReason.Forge安装不完整)
  498. If LogMc.Contains("Invalid paths argument, contained no existing paths") AndAlso LogMc.Contains("libraries\net\minecraftforge\fmlcore") Then AppendReason(CrashReason.Forge安装不完整)
  499. If LogMc.Contains("Invalid module name: '' is not a Java identifier") Then AppendReason(CrashReason.Mod名称包含特殊字符)
  500. If LogMc.Contains("has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to") Then AppendReason(CrashReason.Mod需要Java11)
  501. If LogMc.Contains("java.lang.RuntimeException: java.lang.NoSuchMethodException: no such method: sun.misc.Unsafe.defineAnonymousClass(Class,byte[],Object[])Class/invokeVirtual") Then AppendReason(CrashReason.Mod需要Java11)
  502. If LogMc.Contains("java.lang.IllegalArgumentException: The requested compatibility level JAVA_11 could not be set. Level is not supported by the active JRE or ASM version") Then AppendReason(CrashReason.Mod需要Java11)
  503. If LogMc.Contains("Unsupported major.minor version") Then AppendReason(CrashReason.Java版本不兼容)
  504. If LogMc.Contains("Invalid maximum heap size") Then AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
  505. If LogMc.Contains("Could not reserve enough space") Then
  506. If LogMc.Contains("for 1048576KB object heap") Then
  507. AppendReason(CrashReason.使用32位Java导致JVM无法分配足够多的内存)
  508. Else
  509. AppendReason(CrashReason.内存不足)
  510. End If
  511. End If
  512. '确定的 Mod 导致崩溃
  513. If LogMc.Contains("Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(RegexSeek(LogMc, "(?<=Caught exception from )[^\n]+?")?.TrimEnd((vbCrLf & " ").ToCharArray)))
  514. 'Mod 重复 / 前置问题
  515. If LogMc.Contains("DuplicateModsFoundException") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=\n\t[\w]+ : [A-Z]{1}:[^\n]+(/|\\))[^/\\\n]+?.jar", RegularExpressions.RegexOptions.IgnoreCase))
  516. If LogMc.Contains("Found a duplicate mod") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "Found a duplicate mod[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
  517. If LogMc.Contains("Found duplicate mods") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(LogMc, "(?<=Mod ID: ')\w+?(?=' from mod files:)").Distinct.ToList)
  518. If LogMc.Contains("ModResolutionException: Duplicate") Then AppendReason(CrashReason.Mod重复安装, RegexSearch(If(RegexSeek(LogMc, "ModResolutionException: Duplicate[^\n]+"), ""), "[^\\/]+.jar", RegularExpressions.RegexOptions.IgnoreCase))
  519. If LogMc.Contains("Incompatible mods found!") Then '#5006
  520. AppendReason(CrashReason.Mod互不兼容, If(RegexSeek(LogMc, "(?<=Incompatible mods found![\s\S]+: )[\s\S]+?(?=\tat )"), ""))
  521. End If
  522. If LogMc.Contains("Missing or unsupported mandatory dependencies:") Then
  523. AppendReason(CrashReason.Mod缺少前置或MC版本错误,
  524. RegexSearch(LogMc, "(?<=Missing or unsupported mandatory dependencies:)([\n\r]+\t(.*))+", RegularExpressions.RegexOptions.IgnoreCase).
  525. Select(Function(s) s.Trim((vbCrLf & vbTab & " ").ToCharArray)).Distinct().ToList())
  526. End If
  527. End If
  528. '虚拟机日志分析
  529. If LogHs IsNot Nothing Then
  530. If LogHs.Contains("The system is out of physical RAM or swap space") Then AppendReason(CrashReason.内存不足)
  531. If LogHs.Contains("Out of Memory Error") Then AppendReason(CrashReason.内存不足)
  532. If LogHs.Contains("EXCEPTION_ACCESS_VIOLATION") Then
  533. If LogHs.Contains("# C [ig") Then AppendReason(CrashReason.Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION)
  534. If LogHs.Contains("# C [atio") Then AppendReason(CrashReason.AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION)
  535. If LogHs.Contains("# C [nvoglv") Then AppendReason(CrashReason.Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION)
  536. End If
  537. End If
  538. '崩溃报告分析
  539. If LogCrash IsNot Nothing Then
  540. If LogCrash.Contains("maximum id range exceeded") Then AppendReason(CrashReason.Mod过多导致超出ID限制)
  541. If LogCrash.Contains("java.lang.OutOfMemoryError") Then AppendReason(CrashReason.内存不足)
  542. If LogCrash.Contains("Pixel format not accelerated") Then AppendReason(CrashReason.显卡驱动不支持导致无法设置像素格式)
  543. If LogCrash.Contains("Manually triggered debug crash") Then AppendReason(CrashReason.玩家手动触发调试崩溃)
  544. If LogCrash.Contains("has mods that were not found") AndAlso RegexCheck(LogCrash, "The Mod File [^\n]+optifine\\OptiFine[^\n]+ has mods that were not found") Then AppendReason(CrashReason.OptiFine与Forge不兼容)
  545. 'Mod 导致的崩溃
  546. If LogCrash.Contains("-- MOD ") Then
  547. Dim LogCrashMod As String = LogCrash.Between("-- MOD ", "Failure message:")
  548. If LogCrashMod.ContainsF(".jar", True) Then
  549. AppendReason(CrashReason.确定Mod导致游戏崩溃, If(RegexSeek(LogCrashMod, "(?<=Mod File: ).+"), "").TrimEnd((vbCrLf & " ").ToCharArray))
  550. Else
  551. AppendReason(CrashReason.Mod加载器报错, If(RegexSeek(LogCrash, "(?<=Failure message: )[\w\W]+?(?=\tMod)"), "").Replace(vbTab, " ").TrimEnd((vbCrLf & " ").ToCharArray))
  552. End If
  553. End If
  554. If LogCrash.Contains("Multiple entries with same key: ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=Multiple entries with same key: )[^=]+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
  555. If LogCrash.Contains("LoaderExceptionModCrash: Caught exception from ") Then AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=LoaderExceptionModCrash: Caught exception from )[^\n]+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
  556. If LogCrash.Contains("Failed loading config file ") Then AppendReason(CrashReason.Mod配置文件导致游戏崩溃, {TryAnalyzeModName(If(RegexSeek(LogCrash, "(?<=Failed loading config file .+ for modid )[^\n]+"), "").TrimEnd(vbCrLf)).First, If(RegexSeek(LogCrash, "(?<=Failed loading config file ).+(?= of type)"), "").TrimEnd(vbCrLf)})
  557. End If
  558. End Sub
  559. ''' <summary>
  560. ''' 进行精准日志匹配。匹配优先级高于堆栈分析的崩溃,但低于上面的。
  561. ''' 如果第一步已经找到了原因则不执行该检测。
  562. ''' </summary>
  563. Private Sub AnalyzeCrit2()
  564. 'Mixin 分析
  565. Dim MixinAnalyze =
  566. Function(LogText As String) As Boolean
  567. Dim IsMixin As Boolean =
  568. LogText.Contains("Mixin prepare failed ") OrElse LogText.Contains("Mixin apply failed ") OrElse
  569. LogText.Contains("MixinApplyError") OrElse LogText.Contains("MixinTransformerError") OrElse
  570. LogText.Contains("mixin.injection.throwables.") OrElse LogText.Contains(".json] FAILED during )")
  571. If Not IsMixin Then Return False
  572. 'Mod 名称匹配
  573. Dim ModName As String = RegexSeek(LogText, "(?<=from mod )[^.\/ ]+(?=\] from)")
  574. If ModName Is Nothing Then ModName = RegexSeek(LogText, "(?<=for mod )[^.\/ ]+(?= failed)")
  575. If ModName IsNot Nothing Then
  576. AppendReason(CrashReason.ModMixin失败, TryAnalyzeModName(ModName.TrimEnd((vbCrLf & " ").ToCharArray)))
  577. Return True
  578. End If
  579. 'JSON 名称匹配
  580. For Each JsonName In RegexSearch(LogText, "(?<=^[^\t]+[ \[{(]{1})[^ \[{(]+\.[^ ]+(?=\.json)", RegularExpressions.RegexOptions.Multiline)
  581. AppendReason(CrashReason.ModMixin失败,
  582. TryAnalyzeModName(JsonName.Replace("mixins", "mixin").Replace(".mixin", "").Replace("mixin.", "")))
  583. Return True
  584. Next
  585. '没有明确匹配
  586. AppendReason(CrashReason.ModMixin失败)
  587. Return True
  588. End Function
  589. '游戏日志分析
  590. If LogMc IsNot Nothing Then
  591. 'Mixin 崩溃
  592. Dim IsMixin As Boolean = MixinAnalyze(LogMc)
  593. '常规信息
  594. If LogMc.Contains("An exception was thrown, the game will display an error screen and halt.") Then AppendReason(CrashReason.Forge报错, RegexSeek(LogMc, "(?<=the game will display an error screen and halt.[\n\r]+[^\n]+?Exception: )[\s\S]+?(?=\n\tat)")?.Trim(vbCrLf))
  595. If LogMc.Contains("A potential solution has been determined:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined:\n)(\s+ - [^\n]+\n)+"), ""), "(?<=\s+)[^\n]+"), vbLf))
  596. If LogMc.Contains("A potential solution has been determined, this may resolve your problem:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=A potential solution has been determined, this may resolve your problem:\n)(\s+ - [^\n]+\n)+"), ""), "(?<=\s+)[^\n]+"), vbLf))
  597. If LogMc.Contains("确定了一种可能的解决方法,这样做可能会解决你的问题:") Then AppendReason(CrashReason.Fabric报错并给出解决方案, Join(RegexSearch(If(RegexSeek(LogMc, "(?<=确定了一种可能的解决方法,这样做可能会解决你的问题:\n)(\s+ - [^\n]+\n)+"), ""), "(?<=\s+)[^\n]+"), vbLf))
  598. If Not IsMixin AndAlso LogMc.Contains("due to errors, provided by ") Then '在 #3104 的情况下,这一句导致 OptiFabric 的 Mixin 失败错判为 Fabric Loader 加载失败
  599. AppendReason(CrashReason.确定Mod导致游戏崩溃, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=due to errors, provided by ')[^']+"), "").TrimEnd((vbCrLf & " ").ToCharArray)))
  600. End If
  601. End If
  602. '崩溃报告分析
  603. If LogCrash IsNot Nothing Then
  604. 'Mixin 崩溃
  605. MixinAnalyze(LogCrash)
  606. '常规信息
  607. If LogCrash.Contains("Suspected Mod") Then
  608. Dim SuspectsRaw As String = LogCrash.Between("Suspected Mod", "Stacktrace")
  609. If Not SuspectsRaw.StartsWithF("s: None") Then 'Suspected Mods: None
  610. Dim Suspects = RegexSearch(SuspectsRaw, "(?<=\n\t[^(\t]+\()[^)\n]+")
  611. If Suspects.Any Then AppendReason(CrashReason.怀疑Mod导致游戏崩溃, TryAnalyzeModName(Suspects))
  612. End If
  613. End If
  614. End If
  615. End Sub
  616. ''' <summary>
  617. ''' 进行精准日志匹配。匹配优先级低于堆栈分析的崩溃。
  618. ''' </summary>
  619. Private Sub AnalyzeCrit3()
  620. '游戏日志分析
  621. If LogMc IsNot Nothing Then
  622. '极短的程序输出
  623. If Not (LogMc.Contains("at net.") OrElse LogMc.Contains("INFO]")) AndAlso LogHs Is Nothing AndAlso LogCrash Is Nothing AndAlso LogMc.Length < 100 Then
  624. AppendReason(CrashReason.极短的程序输出, LogMc)
  625. End If
  626. 'Mod 解析错误(常见于 Fabric 前置校验失败)
  627. If LogMc.Contains("Mod resolution failed") Then AppendReason(CrashReason.Mod加载器报错)
  628. 'Mixin 失败可以导致大量 Mod 实例创建失败
  629. If LogMc.Contains("Failed to create mod instance.") Then AppendReason(CrashReason.Mod初始化失败, TryAnalyzeModName(If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModID: )[^,]+"), If(RegexSeek(LogMc, "(?<=Failed to create mod instance. ModId )[^\n]+(?= for )"), "")).TrimEnd(vbCrLf)))
  630. '注意:Fabric 的 Warnings were found! 不一定是崩溃原因,它可能是单纯的警报
  631. End If
  632. '崩溃报告分析
  633. If LogCrash IsNot Nothing Then
  634. If LogCrash.Contains(vbTab & "Block location: World: ") Then AppendReason(CrashReason.特定方块导致崩溃, If(RegexSeek(LogCrash, "(?<=\tBlock: Block\{)[^\}]+"), "") & " " & If(RegexSeek(LogCrash, "(?<=\tBlock location: World: )\([^\)]+\)"), ""))
  635. If LogCrash.Contains(vbTab & "Entity's Exact location: ") Then AppendReason(CrashReason.特定实体导致崩溃, If(RegexSeek(LogCrash, "(?<=\tEntity Type: )[^\n]+(?= \()"), "") & " (" & If(RegexSeek(LogCrash, "(?<=\tEntity's Exact location: )[^\n]+"), "").TrimEnd(vbCrLf.ToCharArray) & ")")
  636. End If
  637. End Sub
  638. ''' <summary>
  639. ''' 从堆栈中提取 Mod ID 关键字。若失败则返回空列表。
  640. ''' </summary>
  641. Private Function AnalyzeStackKeyword(ErrorStack As String) As List(Of String)
  642. ErrorStack = vbLf & If(ErrorStack, "") & vbLf
  643. '进行正则匹配
  644. Dim StackSearchResults As New List(Of String)
  645. StackSearchResults.AddRange(RegexSearch(ErrorStack, "(?<=\n[^{]+)[a-zA-Z_]+\w+\.[a-zA-Z_]+[\w\.]+(?=\.[\w\.$]+\.)"))
  646. StackSearchResults.AddRange(RegexSearch(ErrorStack, "(?<=at [^(]+?\.\w+\$\w+\$)[\w\$]+?(?=\$\w+\()").Select(Function(s) s.Replace("$", "."))) 'Mixin 堆栈:xxx.xxx.xxxx$xxxx$xxx
  647. StackSearchResults = StackSearchResults.Distinct.ToList
  648. '检查堆栈开头
  649. Dim PossibleStacks As New List(Of String)
  650. For Each Stack As String In StackSearchResults
  651. 'If Not Stack.Contains(".") Then Continue For
  652. For Each IgnoreStack In {
  653. "java", "sun", "javax", "jdk", "oolloo",
  654. "org.lwjgl", "com.sun", "net.minecraftforge", "paulscode.sound", "com.mojang", "net.minecraft", "cpw.mods", "com.google", "org.apache", "org.spongepowered", "net.fabricmc", "com.mumfrey",
  655. "com.electronwill.nightconfig", "it.unimi.dsi",
  656. "MojangTricksIntelDriversForPerformance_javaw"}
  657. If Stack.StartsWithF(IgnoreStack) Then GoTo NextStack
  658. Next
  659. PossibleStacks.Add(Stack.Trim)
  660. NextStack:
  661. Next
  662. PossibleStacks = PossibleStacks.Distinct.ToList
  663. Log("[Crash] 找到 " & PossibleStacks.Count & " 条可能的堆栈信息")
  664. If Not PossibleStacks.Any() Then Return New List(Of String)
  665. For Each Stack As String In PossibleStacks
  666. Log("[Crash] - " & Stack)
  667. Next
  668. '检查堆栈关键词
  669. Dim PossibleWords As New List(Of String)
  670. For Each Stack As String In PossibleStacks
  671. Dim Splited = Stack.Split(".")
  672. For i = 0 To Math.Min(3, Splited.Count - 1) '最多取前 4 节
  673. Dim Word As String = Splited(i)
  674. If Word.Length <= 2 OrElse Word.StartsWithF("func_") Then Continue For
  675. If {"com", "org", "net", "asm", "fml", "mod", "jar", "sun", "lib", "map", "gui", "dev", "nio", "api", "dsi", "top", "mcp",
  676. "core", "init", "mods", "main", "file", "game", "load", "read", "done", "util", "tile", "item", "base", "oshi", "impl", "data", "pool", "task",
  677. "forge", "setup", "block", "model", "mixin", "event", "unimi", "netty", "world", "lwjgl",
  678. "gitlab", "common", "server", "config", "mixins", "compat", "loader", "launch", "entity", "assist", "client", "plugin", "modapi", "mojang", "shader", "events", "github", "recipe", "render", "packet", "events",
  679. "preinit", "preload", "machine", "reflect", "channel", "general", "handler", "content", "systems", "modules", "service",
  680. "fastutil", "optifine", "internal", "platform", "override", "fabricmc", "neoforge",
  681. "injection", "listeners", "scheduler", "minecraft", "universal", "multipart", "neoforged", "microsoft",
  682. "transformer", "transformers", "minecraftforge", "blockentity", "spongepowered", "electronwill"
  683. }.Contains(Word.ToLower) Then Continue For
  684. PossibleWords.Add(Word.Trim)
  685. Next
  686. Next
  687. PossibleWords = PossibleWords.Distinct.ToList
  688. Log("[Crash] 从堆栈信息中找到 " & PossibleWords.Count & " 个可能的 Mod ID 关键词")
  689. If PossibleWords.Any Then Log("[Crash] - " & Join(PossibleWords, ", "))
  690. If PossibleWords.Count > 10 Then
  691. Log("[Crash] 关键词过多,考虑匹配出错,不纳入考虑")
  692. Return New List(Of String)
  693. Else
  694. Return PossibleWords
  695. End If
  696. End Function
  697. ''' <summary>
  698. ''' 根据 Mod 关键词尝试获取实际的 Mod 名称。
  699. ''' 若失败则返回 Nothing。
  700. ''' </summary>
  701. Private Function AnalyzeModName(Keywords As List(Of String)) As List(Of String)
  702. Dim ModFileNames As New List(Of String)
  703. '预处理关键词(分割括号)
  704. Dim RealKeywords As New List(Of String)
  705. For Each Keyword In Keywords
  706. For Each SubKeyword In Keyword.Split("(")
  707. RealKeywords.Add(SubKeyword.Trim(" )".ToCharArray))
  708. Next
  709. Next
  710. Keywords = RealKeywords
  711. '从崩溃报告获取 Mod 信息
  712. If LogCrash IsNot Nothing AndAlso LogCrash.Contains("A detailed walkthrough of the error") Then
  713. Dim Details As String = LogCrash.Replace("A detailed walkthrough of the error", "¨")
  714. Dim IsFabricDetail As Boolean = Details.Contains("Fabric Mods") '是否为 Fabric 信息格式
  715. If IsFabricDetail Then
  716. Details = Details.Replace("Fabric Mods", "¨")
  717. Log("[Crash] 崩溃报告中检测到 Fabric Mod 信息格式")
  718. End If
  719. Details = Details.AfterLast("¨")
  720. '[Forge] 获取所有包含 .jar 的行
  721. '[Fabric] 获取所有包含 Mod 信息的行
  722. Dim ModNameLines As New List(Of String)
  723. For Each Line In Details.Split(vbLf)
  724. If (Line.ContainsF(".jar", True) AndAlso Line.Length - Line.Replace(".jar", "").Length = 4) OrElse '只有一个 .jar
  725. (IsFabricDetail AndAlso Line.StartsWithF(vbTab & vbTab) AndAlso Not RegexCheck(Line, "\t\tfabric[\w-]*: Fabric")) Then ModNameLines.Add(Line)
  726. Next
  727. Log("[Crash] 崩溃报告中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
  728. '获取 Mod ID 与关键词的匹配行
  729. Dim HintLines As New List(Of String)
  730. For Each KeyWord As String In Keywords
  731. For Each ModString As String In ModNameLines
  732. Dim RealModString As String = ModString.ToLower.Replace("_", "")
  733. If Not RealModString.Contains(KeyWord.ToLower.Replace("_", "")) Then Continue For
  734. If RealModString.Contains("minecraft.jar") OrElse RealModString.Contains(" forge-") OrElse RealModString.Contains(" mixin-") Then Continue For
  735. HintLines.Add(ModString.Trim(vbCrLf.ToCharArray))
  736. Exit For
  737. Next
  738. Next
  739. HintLines = HintLines.Distinct.ToList
  740. Log("[Crash] 崩溃报告中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
  741. For Each ModLine As String In HintLines
  742. Log("[Crash] - " & ModLine)
  743. Next
  744. '从 Mod 匹配行中提取 .jar 文件的名称
  745. For Each Line As String In HintLines
  746. Dim Name As String
  747. If IsFabricDetail Then
  748. Name = RegexSeek(Line, "(?<=: )[^\n]+(?= [^\n]+)")
  749. Else
  750. Name = RegexSeek(Line, "(?<=\()[^\t]+.jar(?=\))|(?<=(\t\t)|(\| ))[^\t\|]+.jar", RegularExpressions.RegexOptions.IgnoreCase)
  751. End If
  752. If Name IsNot Nothing Then ModFileNames.Add(Name)
  753. Next
  754. End If
  755. '从 debug.log 获取 Mod 信息
  756. If LogMcDebug IsNot Nothing Then
  757. 'Forge: Found valid mod file YungsBetterStrongholds-1.20-Forge-4.0.1.jar with {betterstrongholds} mods - versions {1.20-Forge-4.0.1}
  758. Dim ModNameLines As List(Of String) = RegexSearch(LogMcDebug, "(?<=valid mod file ).*", RegularExpressions.RegexOptions.Multiline)
  759. Log("[Crash] Debug 信息中找到 " & ModNameLines.Count & " 个可能的 Mod 项目行")
  760. '获取 Mod ID 与关键词的匹配行
  761. Dim HintLines As New List(Of String)
  762. For Each KeyWord As String In Keywords
  763. For Each ModString As String In ModNameLines
  764. If ModString.Contains($"{{{KeyWord}}}") Then HintLines.Add(ModString)
  765. Next
  766. Next
  767. HintLines = HintLines.Distinct.ToList
  768. Log("[Crash] Debug 信息中找到 " & HintLines.Count & " 个可能的崩溃 Mod 匹配行")
  769. For Each ModLine As String In HintLines
  770. Log("[Crash] - " & ModLine)
  771. Next
  772. '从 Mod 匹配行中提取 .jar 文件的名称
  773. For Each Line As String In HintLines
  774. Dim Name As String
  775. Name = RegexSeek(Line, ".*(?= with)")
  776. If Name IsNot Nothing Then ModFileNames.Add(Name)
  777. Next
  778. End If
  779. '输出
  780. ModFileNames = ModFileNames.Distinct.ToList
  781. If Not ModFileNames.Any() Then
  782. Return Nothing
  783. Else
  784. Log("[Crash] 找到 " & ModFileNames.Count & " 个可能的崩溃 Mod 文件名")
  785. For Each ModFileName As String In ModFileNames
  786. Log("[Crash] - " & ModFileName)
  787. Next
  788. Return ModFileNames
  789. End If
  790. End Function
  791. ''' <summary>
  792. ''' 尝试从关键字获取 Mod 名称,若失败则返回原关键字。
  793. ''' </summary>
  794. Private Function TryAnalyzeModName(Keyword As String) As List(Of String)
  795. Dim RawList As New List(Of String) From {If(Keyword, "")}
  796. If String.IsNullOrEmpty(Keyword) Then Return RawList
  797. Return If(AnalyzeModName(RawList), RawList)
  798. End Function
  799. ''' <summary>
  800. ''' 尝试从关键字获取 Mod 名称,若失败则返回原关键字。
  801. ''' </summary>
  802. Private Function TryAnalyzeModName(Keywords As List(Of String)) As List(Of String)
  803. If Not Keywords.Any Then Return Keywords
  804. Return If(AnalyzeModName(Keywords), Keywords)
  805. End Function
  806. '4:根据原因输出信息
  807. Private OutputFiles As New List(Of String)
  808. ''' <summary>
  809. ''' 弹出崩溃弹窗,并指导导出崩溃报告。
  810. ''' </summary>
  811. Public Sub Output(IsManualAnalyze As Boolean, Optional ExtraFiles As List(Of String) = Nothing)
  812. '弹窗提示
  813. FrmMain.ShowWindowToTop()
  814. Dim Detail As String = GetAnalyzeResult(IsManualAnalyze)
  815. Select Case MyMsgBox(Detail, If(IsManualAnalyze, "错误报告分析结果", "Minecraft 出现错误"),
  816. "确定", If(IsManualAnalyze OrElse DirectFile Is Nothing, "", "查看日志"), If(IsManualAnalyze, "", "导出错误报告"),
  817. Button2Action:=If(IsManualAnalyze OrElse DirectFile Is Nothing, Nothing,
  818. Sub()
  819. '弹窗选择:查看日志
  820. If File.Exists(DirectFile.Value.Key) Then
  821. ShellOnly(DirectFile.Value.Key)
  822. Else
  823. Dim FilePath As String = PathTemp & "Crash.txt"
  824. WriteFile(FilePath, Join(DirectFile.Value.Value, vbCrLf))
  825. ShellOnly(FilePath)
  826. End If
  827. End Sub))
  828. Case 3
  829. '弹窗选择:导出错误报告
  830. Dim FileAddress As String = Nothing
  831. Try
  832. '获取文件路径
  833. RunInUiWait(Sub() FileAddress = SelectSaveFile("选择保存位置", "错误报告-" & Date.Now.ToString("G").Replace("/", "-").Replace(":", ".").Replace(" ", "_") & ".zip", "Minecraft 错误报告(*.zip)|*.zip"))
  834. If String.IsNullOrEmpty(FileAddress) Then Return
  835. Directory.CreateDirectory(GetPathFromFullPath(FileAddress))
  836. If File.Exists(FileAddress) Then File.Delete(FileAddress)
  837. '输出诊断信息
  838. FeedbackInfo()
  839. LogFlush()
  840. '复制文件
  841. If ExtraFiles IsNot Nothing Then OutputFiles.AddRange(ExtraFiles)
  842. For Each OutputFile In OutputFiles
  843. Dim FileName As String = GetFileNameFromPath(OutputFile)
  844. Dim FileEncoding As Encoding = Nothing
  845. Select Case FileName
  846. Case "LatestLaunch.bat"
  847. FileName = "启动脚本.bat"
  848. Case "Log1.txt"
  849. FileName = "PCL 启动器日志.txt"
  850. FileEncoding = Encoding.UTF8
  851. Case "RawOutput.log"
  852. FileName = "游戏崩溃前的输出.txt"
  853. FileEncoding = Encoding.UTF8
  854. End Select
  855. If File.Exists(OutputFile) Then
  856. If FileEncoding Is Nothing Then FileEncoding = GetEncoding(ReadFileBytes(OutputFile))
  857. Dim FileContent As String = ReadFile(OutputFile, FileEncoding)
  858. FileContent = FilterAccessToken(FileContent, If(FileName = "启动脚本.bat", "F", "*"))
  859. FileContent = FilterUserName(FileContent, "*")
  860. WriteFile(TempFolder & "Report\" & FileName, FileContent, Encoding:=FileEncoding)
  861. Log($"[Crash] 导出文件:{FileName},编码:{FileEncoding.HeaderName}")
  862. End If
  863. Next
  864. '导出报告
  865. Compression.ZipFile.CreateFromDirectory(TempFolder & "Report\", FileAddress)
  866. DeleteDirectory(TempFolder & "Report\")
  867. Hint("错误报告已导出!", HintType.Finish)
  868. Catch ex As Exception
  869. Log(ex, "导出错误报告失败", LogLevel.Feedback)
  870. Return
  871. End Try
  872. OpenExplorer(FileAddress)
  873. End Select
  874. '上报
  875. If IsManualAnalyze Then Return
  876. Telemetry("Minecraft 崩溃",
  877. "Version", If(TargetVersion Is Nothing, "null", TargetVersion.Version.ToString),
  878. "Detail", FilterUserName(Detail, "*"))
  879. End Sub
  880. ''' <summary>
  881. ''' 获取崩溃分析的结果描述。
  882. ''' </summary>
  883. Private Function GetAnalyzeResult(IsHandAnalyze As Boolean) As String
  884. '没有结果的处理
  885. If Not CrashReasons.Any() Then
  886. If IsHandAnalyze Then
  887. Return "很抱歉,PCL 无法确定错误原因。"
  888. Else
  889. Return $"很抱歉,你的游戏出现了一些问题……{vbCrLf}如果要寻求帮助,请把错误报告文件发给对方,而不是发送这个窗口的照片或者截图。"
  890. End If
  891. End If
  892. '根据不同原因判断
  893. Dim Results As New List(Of String)
  894. For Each Reason In CrashReasons
  895. Dim Additional As List(Of String) = Reason.Value
  896. Select Case Reason.Key
  897. Case CrashReason.Mod文件被解压
  898. Results.Add("由于 Mod 文件被解压了,导致游戏无法继续运行。\n直接把整个 Mod 文件放进 Mod 文件夹中即可,若解压就会导致游戏出错。\n\n请删除 Mod 文件夹中已被解压的 Mod,然后再启动游戏。")
  899. Case CrashReason.内存不足
  900. Results.Add("Minecraft 内存不足,导致其无法继续运行。\n这很可能是因为电脑内存不足、游戏分配的内存不足,或是配置要求过高。\n\n你可以尝试在 更多 → 百宝箱 中选择 内存优化,然后再启动游戏。\n如果还是不行,请在启动设置中增加为游戏分配的内存,并删除配置要求较高的材质、Mod、光影。\n如果依然不奏效,请在开始游戏前尽量关闭其他软件,或者……换台电脑?\h")
  901. Case CrashReason.使用OpenJ9
  902. Results.Add("游戏因为使用 OpenJ9 而崩溃了。\n请在启动设置的 Java 选择一项中改用非 OpenJ9 的 Java,然后再启动游戏。")
  903. Case CrashReason.使用JDK
  904. Results.Add("游戏似乎因为使用 JDK,或 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用 JRE 8(Java 8),然后再启动游戏。\n如果你没有安装 JRE 8,你可以从网络中下载、安装一个。")
  905. Case CrashReason.Java版本过高
  906. Results.Add("游戏似乎因为你所使用的 Java 版本过高而崩溃了。\n请在启动设置的 Java 选择一项中改用较低版本的 Java,然后再启动游戏。\n如果没有,可以从网络中下载、安装一个。")
  907. Case CrashReason.Java版本不兼容
  908. Results.Add("游戏不兼容你当前使用的 Java。\n如果没有合适的 Java,可以从网络中下载、安装一个。")
  909. Case CrashReason.Mod名称包含特殊字符
  910. Results.Add("由于有 Mod 的名称包含特殊字符,导致游戏崩溃。\n请尝试修改 Mod 文件名,让它只包含英文字母、数字、减号(-)、下划线(_)和小数点,然后再启动游戏。")
  911. Case CrashReason.MixinBootstrap缺失
  912. Results.Add("由于缺失 MixinBootstrap,导致游戏崩溃。\n请尝试安装 MixinBootstrap。若安装后依然崩溃,可以尝试在文件名前添加英文感叹号。")
  913. Case CrashReason.使用32位Java导致JVM无法分配足够多的内存
  914. If Environment.Is64BitOperatingSystem Then
  915. Results.Add("你似乎正在使用 32 位 Java,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n请在启动设置的 Java 选择一项中改用 64 位的 Java 再启动游戏,然后再启动游戏。\n如果你没有安装 64 位的 Java,你可以从网络中下载、安装一个。")
  916. Else
  917. Results.Add("你正在使用 32 位的操作系统,这会导致 Minecraft 无法使用所需的内存,进而造成崩溃。\n\n你或许只能重装 64 位的操作系统来解决此问题。\n如果你的电脑内存在 2GB 以内,那或许只能换台电脑了……\h")
  918. End If
  919. Case CrashReason.Mod缺少前置或MC版本错误
  920. If Additional.Any Then
  921. Results.Add("由于未安装正确的前置 Mod,导致游戏退出。\n缺失的依赖项:\n - " & Join(Additional, "\n - ") & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  922. Else
  923. Results.Add("由于未安装正确的前置 Mod,导致游戏退出。\n请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  924. End If
  925. Case CrashReason.堆栈分析发现关键字
  926. If Additional.Count = 1 Then
  927. Results.Add("你的游戏遇到了一些问题,PCL 为此找到了一个可疑的关键词:" & Additional.First & "。\n\n如果你知道某个关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
  928. Else
  929. Results.Add("你的游戏遇到了一些问题,PCL 为此找到了以下可疑的关键词:\n - " & Join(Additional, ", ") & "\n\n如果你知道某个关键词对应的 Mod,那么有可能就是它引起的错误,你也可以查看错误报告获取详情。\h")
  930. End If
  931. Case CrashReason.堆栈分析发现Mod名称, CrashReason.怀疑Mod导致游戏崩溃
  932. If Additional.Count = 1 Then
  933. Results.Add("PCL 怀疑名为 " & Additional.First & " 的 Mod 导致了游戏出错,但不能完全确定。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  934. Else
  935. Results.Add("PCL 怀疑以下 Mod 导致了游戏出错,但不能完全确定:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  936. End If
  937. Case CrashReason.确定Mod导致游戏崩溃
  938. If Additional.Count = 1 Then
  939. Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  940. Else
  941. Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  942. End If
  943. Case CrashReason.ModMixin失败
  944. If Additional.Count = 0 Then
  945. Results.Add("部分 Mod 注入失败,导致游戏出错。\n这一般代表着部分 Mod 与其他 Mod 或当前环境不兼容,或是它存在 Bug。\n你可以尝试逐步禁用 Mod,然后观察游戏是否还会崩溃,以此定位导致崩溃的 Mod。\n\e\h")
  946. ElseIf Additional.Count = 1 Then
  947. Results.Add("名为 " & Additional.First & " 的 Mod 注入失败,导致游戏出错。\n这一般代表着它与其他 Mod 或当前环境不兼容,或是它存在 Bug。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  948. Else
  949. Results.Add("以下 Mod 导致了游戏出错:\n - " & Join(Additional, "\n - ") & "\n这一般代表着它们与其他 Mod 或当前环境不兼容,或是它存在 Bug。\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  950. End If
  951. Case CrashReason.Mod配置文件导致游戏崩溃
  952. If Additional(1) Is Nothing Then
  953. Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错。\n\e\h")
  954. Else
  955. Results.Add("名为 " & Additional.First & " 的 Mod 导致了游戏出错:\n其配置文件 " & Additional(1) & " 存在异常,无法读取。")
  956. End If
  957. Case CrashReason.Mod初始化失败
  958. If Additional.Count = 1 Then
  959. Results.Add("名为 " & Additional.First & " 的 Mod 初始化失败,导致游戏无法继续加载。\n你可以尝试禁用此 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  960. Else
  961. Results.Add("以下 Mod 初始化失败,导致游戏出错:\n - " & Join(Additional, "\n - ") & "\n\n你可以尝试依次禁用上述 Mod,然后观察游戏是否还会崩溃。\n\e\h")
  962. End If
  963. Case CrashReason.特定方块导致崩溃
  964. If Additional.Count = 1 Then
  965. Results.Add("游戏似乎因为方块 " & Additional.First & " 出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是该方块导致出错,你或许需要使用一些方式删除此方块。\n - 若仍然出错,问题就可能来自其他原因……\h")
  966. Else
  967. Results.Add("游戏似乎因为世界中的某些方块出现了问题。\n\n你可以创建一个新世界,并观察游戏的运行情况:\n - 若正常运行,则是某些方块导致出错,你或许需要删除该世界。\n - 若仍然出错,问题就可能来自其他原因……\h")
  968. End If
  969. Case CrashReason.Mod重复安装
  970. If Additional.Count >= 2 Then
  971. Results.Add("你重复安装了多个相同的 Mod:\n - " & Join(Additional, "\n - ") & "\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。")
  972. Else
  973. Results.Add("你可能重复安装了多个相同的 Mod,导致游戏出错。\n\n每个 Mod 只能出现一次,请删除重复的 Mod,然后再启动游戏。\e\h")
  974. End If
  975. Case CrashReason.特定实体导致崩溃
  976. If Additional.Count = 1 Then
  977. Dim entityInfo As String = Additional.First
  978. If entityInfo.StartsWith("minecraft:player") Then
  979. Results.Add("游戏似乎因为玩家实体 " & entityInfo & " 出现了问题。\n\n这通常是因为玩家数据损坏或世界存档问题导致的。\n请尝试以下解决方案:\n - 创建一个新世界,观察游戏是否正常运行\n - 如果新世界正常,可能需要删除或修复当前世界的玩家数据文件\n - 也可能是某个 Mod 与玩家实体处理存在冲突,尝试逐个禁用近期安装的 Mod\n - 如果问题持续存在,建议备份重要数据后重新创建世界\h")
  980. Else
  981. Results.Add("游戏似乎因为实体 " & entityInfo & " 出现了问题。\n\n请尝试以下解决方案:\n - 创建一个新世界,并尝试生成该类型的实体,观察游戏运行情况\n - 如果新世界中该实体正常,问题可能在于当前世界的数据损坏\n - 如果新世界中仍然崩溃,可能是某个 Mod 导致了实体处理异常\n - 尝试逐个禁用可能影响实体的 Mod(如生物类、AI 类 Mod)\n - 也可以尝试使用指令删除该坐标附近的实体\h")
  982. End If
  983. Else
  984. Results.Add("游戏似乎因为世界中的某些实体出现了问题。\n\n请尝试以下解决方案:\n - 创建一个新世界,观察游戏的运行情况\n - 如果新世界正常运行,则是当前世界的实体数据导致出错\n - 可以尝试使用指令清理异常实体\n - 如果问题持续存在,可能是某个处理实体的 Mod 存在问题\n - 建议逐个禁用近期安装的 Mod,特别是涉及生物、AI 或实体处理的 Mod\h")
  985. End If
  986. Case CrashReason.OptiFine与Forge不兼容
  987. Results.Add("由于 OptiFine 与当前版本的 Forge 不兼容,导致了游戏崩溃。\n\n请前往 OptiFine 官网(https://optifine.net/downloads)查看 OptiFine 所兼容的 Forge 版本,并严格按照对应版本重新安装游戏。")
  988. Case CrashReason.ShadersMod与OptiFine同时安装
  989. Results.Add("无需同时安装 OptiFine 和 Shaders Mod,OptiFine 已经集成了 Shaders Mod 的功能。\n在删除 Shaders Mod 后,游戏即可正常运行。")
  990. Case CrashReason.低版本Forge与高版本Java不兼容
  991. Results.Add("由于低版本 Forge 与当前 Java 不兼容,导致了游戏崩溃。\n\n请尝试以下解决方案:\n - 更新 Forge 到 36.2.26 或更高版本\n - 换用版本低于 1.8.0.320 的 Java")
  992. Case CrashReason.版本Json中存在多个Forge
  993. Results.Add("可能由于其他启动器修改了 Forge 版本,当前版本的文件存在异常,导致了游戏崩溃。\n请尝试重新全新安装 Forge,而非使用其他启动器修改 Forge 版本。")
  994. Case CrashReason.玩家手动触发调试崩溃
  995. Results.Add("* 事实上,你的游戏没有任何问题,这是你自己触发的崩溃。\n* 你难道没有更重要的事要做吗?")
  996. Case CrashReason.Mod需要Java11
  997. Results.Add("你所安装的部分 Mod 似乎需要使用 Java 11 启动。\n请在启动设置的 Java 选择一项中改用 Java 11,然后再启动游戏。\n如果你没有安装 Java 11,你可以从网络中下载、安装一个。")
  998. Case CrashReason.极短的程序输出
  999. Results.Add($"程序返回了以下信息:\n{Additional.First}\n\h")
  1000. Case CrashReason.OptiFine导致无法加载世界 'https://www.minecraftforum.net/forums/support/java-edition-support/3051132-exception-ticking-world
  1001. Results.Add("你所使用的 OptiFine 可能导致了你的游戏出现问题。\n\n该问题只在特定 OptiFine 版本中出现,你可以尝试更换 OptiFine 的版本。\h")
  1002. Case CrashReason.显卡驱动不支持导致无法设置像素格式, CrashReason.Intel驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.AMD驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.Nvidia驱动不兼容导致EXCEPTION_ACCESS_VIOLATION, CrashReason.显卡不支持OpenGL
  1003. If LogAll.Contains("hd graphics ") Then
  1004. Results.Add("你的显卡驱动存在问题,或未使用独立显卡,导致游戏无法正常运行。\n\n如果你的电脑存在独立显卡,请使用独立显卡而非 Intel 核显启动 PCL 与 Minecraft。\n如果问题依然存在,请尝试升级你的显卡驱动到最新版本,或回退到出厂版本。\n如果还是不行,还可以尝试使用 8.0.51 或更低版本的 Java。\h")
  1005. Else
  1006. Results.Add("你的显卡驱动存在问题,导致游戏无法正常运行。\n\n请尝试升级你的显卡驱动到最新版本,或回退到出厂版本,然后再启动游戏。\n如果还是不行,可以尝试使用 8.0.51 或更低版本的 Java。\n如果问题依然存在,那么你可能需要换个更好的显卡……\h")
  1007. End If
  1008. Case CrashReason.材质过大或显卡配置不足
  1009. Results.Add("你所使用的材质分辨率过高,或显卡配置不足,导致游戏无法继续运行。\n\n如果你正在使用高清材质,请将它移除。\n如果你没有使用材质,那么你可能需要更新显卡驱动,或者换个更好的显卡……\h")
  1010. Case CrashReason.NightConfig的Bug
  1011. Results.Add("由于 Night Config 存在问题,导致了游戏崩溃。\n你可以尝试安装 Night Config Fixes 模组,这或许能解决此问题。\h")
  1012. Case CrashReason.光影或资源包导致OpenGL1282错误
  1013. Results.Add("你所使用的光影或材质导致游戏出现了一些问题……\n\n请尝试删除你所添加的这些额外资源。\h")
  1014. Case CrashReason.Mod过多导致超出ID限制
  1015. Results.Add("你所安装的 Mod 过多,超出了游戏的 ID 限制,导致了游戏崩溃。\n请尝试安装 JEID 等修复 Mod,或删除部分大型 Mod。")
  1016. Case CrashReason.文件或内容校验失败
  1017. Results.Add("部分文件或内容校验失败,导致游戏出现了问题。\n\n请尝试删除游戏(包括 Mod)并重新下载,或尝试在重新下载时使用 VPN。\h")
  1018. Case CrashReason.Forge安装不完整
  1019. Results.Add("由于安装的 Forge 文件丢失,导致游戏无法正常运行。\n请重新安装一次相同版本的 Forge,然后再启动游戏。\n在打包游戏时删除 libraries 文件夹可能导致此错误。\h")
  1020. Case CrashReason.Fabric报错
  1021. If Additional.Count = 1 Then
  1022. Results.Add("Fabric 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  1023. Else
  1024. Results.Add("Fabric 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  1025. End If
  1026. Case CrashReason.Mod互不兼容
  1027. If Additional.Count = 1 Then
  1028. Results.Add("你所安装的 Mod 不兼容:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  1029. Else
  1030. Results.Add("你所安装的 Mod 不兼容,Mod 加载器可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  1031. End If
  1032. Case CrashReason.Mod加载器报错
  1033. If Additional.Count = 1 Then
  1034. Results.Add("Mod 加载器提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  1035. Else
  1036. Results.Add("Mod 加载器可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  1037. End If
  1038. Case CrashReason.Fabric报错并给出解决方案
  1039. If Additional.Count = 1 Then
  1040. Results.Add("Fabric 提供了以下解决方案:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  1041. Else
  1042. Results.Add("Fabric 可能已经提供了解决方案,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  1043. End If
  1044. Case CrashReason.Forge报错
  1045. If Additional.Count = 1 Then
  1046. Results.Add("Forge 提供了以下错误信息:\n" & Additional.First & "\n\n请根据上述信息进行对应处理,如果看不懂英文可以使用翻译软件。")
  1047. Else
  1048. Results.Add("Forge 可能已经提供了错误信息,请根据错误报告中的日志信息进行对应处理,如果看不懂英文可以使用翻译软件。\h")
  1049. End If
  1050. Case CrashReason.没有可用的分析文件
  1051. Results.Add("你的游戏出现了一些问题,但 PCL 未能找到相关记录文件,因此无法进行分析。\h")
  1052. Case Else
  1053. Results.Add("PCL 获取到了没有详细信息的错误原因(" & CrashReasons.First.Key & "),请向 PCL 作者提交反馈以获取详情。\h")
  1054. End Select
  1055. Next
  1056. Return Join(Results, "\n\n此外,").
  1057. Replace("\n", vbCrLf).
  1058. Replace("\h", "").
  1059. Replace("\e", If(IsHandAnalyze, "", vbCrLf & "你可以查看错误报告了解错误具体是如何发生的。")).
  1060. Replace(vbCrLf, vbCr).Replace(vbLf, vbCr).Replace(vbCr, vbCrLf).
  1061. Trim(vbCrLf.ToCharArray) &
  1062. If(Not Results.Any(Function(r) r.EndsWithF("\h")) OrElse IsHandAnalyze, "",
  1063. vbCrLf & "如果要寻求帮助,请把错误报告文件发给对方,而不是发送这个窗口的照片或者截图。" &
  1064. If(If(PageSetupSystem.IsLauncherNewest(), True), "",
  1065. vbCrLf & vbCrLf & "此外,你正在使用老版本 PCL,更新 PCL 或许也能解决这个问题。" & vbCrLf & "你可以点击 [设置 → 其他 → 启动器 → 检查更新] 更新 PCL。"))
  1066. End Function
  1067. End Class