modStart.vb 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381
  1. Imports Ionic.Zip
  2. Module modStart
  3. #Region "开始"
  4. Public Class GameStartInfo
  5. ''' <summary>
  6. ''' 登录方式。
  7. ''' </summary>
  8. Public LoginMethod As LoginMethods
  9. ''' <summary>
  10. ''' 玩家名。正版登录时给出返回的名称,离线登录时给出输入的名称。
  11. ''' </summary>
  12. Public UserName As String
  13. ''' <summary>
  14. ''' 启动的 Minecraft 版本。
  15. ''' </summary>
  16. Public SelectVersion As MCVersion
  17. End Class
  18. Public GameWindowState As LoadState = LoadState.Waiting
  19. ''' <summary>
  20. ''' 开始游戏。
  21. ''' </summary>
  22. ''' <remarks></remarks>
  23. Public Sub GameStart(Info As GameStartInfo)
  24. Try
  25. '初始化进度条(设置为 0 即为初始化)
  26. frmHomeRight.StartProcess = 0
  27. '检查输入信息、Java、内存。如果有问题会直接以 Exception 抛出
  28. frmHomeRight.StartProcess = 0.03
  29. log("[Launch] 正在进行启动前检查")
  30. frmHomeRight.StartButtomSet("检查基础信息中")
  31. GameCheck(Info)
  32. '检查 Lib 文件,有可能转入下载
  33. frmHomeRight.StartProcess = 0.06
  34. log("[Launch] 正在检查支持库")
  35. frmHomeRight.StartButtomSet("检查支持库中")
  36. Try
  37. GameLibStartCheck(Info)
  38. Catch ex As Exception
  39. If ex.Message.Contains("版本文件缺失") Then Throw ex
  40. Select Case MyMsgbox("支持库下载失败,是否要继续启动游戏?游戏很可能会启动失败。" & vbCrLf & "这通常是因为网络不稳定,或国内下载源尚未更新刚发布的 Minecraft 的文件。你可以选择取消启动,并且稍后重新尝试下载。", "支持库下载失败", "确定", "取消", , True)
  41. Case 1 '确定
  42. log("[Launch] 支持库下载失败,但是被强行启动")
  43. Case 2 '取消
  44. Throw New Exception("")
  45. End Select
  46. End Try
  47. '检查 Assets 文件,有可能转入下载
  48. frmHomeRight.StartProcess = 0.36
  49. If ReadIni("setup", "DownMinecraftAssetsHint", "True") = "True" Then
  50. log("[Launch] 正在检查资源文件")
  51. frmHomeRight.StartButtomSet("检查资源文件中")
  52. Try
  53. GameAssetsStartCheck(Info)
  54. Catch ex As Exception
  55. Select Case MyMsgbox("资源文件下载失败,是否要继续启动游戏?游戏可能没有中文,或是缺失部分声音。" & vbCrLf & "这通常是因为网络不稳定,或国内下载源尚未更新刚发布的 Minecraft 的文件。你可以选择取消启动,并且稍后重新尝试下载。", "资源文件下载失败", "确定", "取消", , True)
  56. Case 1 '确定
  57. log("[Launch] 资源文件下载失败,但是被强行启动")
  58. Case 2 '取消
  59. Throw New Exception("")
  60. End Select
  61. End Try
  62. End If
  63. '加载启动信息
  64. frmHomeRight.StartProcess = 0.72
  65. frmHomeRight.StartButtomSet("加载启动信息中")
  66. Dim LaunchCMD As String = ""
  67. Select Case Info.LoginMethod
  68. Case LoginMethods.Mojang
  69. '正版登录
  70. log("[Launch] 获取正版登录启动参数")
  71. LaunchCMD = GameArgumentsMojang(Info.SelectVersion)
  72. Case LoginMethods.Legacy
  73. '离线登录
  74. log("[Launch] 获取离线登录启动参数")
  75. LaunchCMD = GameArgumentsLegacy(Info.SelectVersion, Info.UserName)
  76. End Select
  77. '启动游戏进程与日志监视窗口
  78. frmHomeRight.StartProcess = 0.82
  79. log("[Launch] 正在启动游戏进程")
  80. frmHomeRight.StartButtomSet("启动游戏进程中")
  81. GameThreadStart(LaunchCMD, Info)
  82. '等待游戏窗口
  83. frmHomeRight.StartProcess = 0.92
  84. log("[Launch] 正在等待游戏窗口")
  85. frmHomeRight.StartButtomSet("等待游戏窗口中")
  86. Do While GameWindowState = LoadState.Loading
  87. Thread.Sleep(15)
  88. Loop
  89. If GameWindowState = LoadState.Failed Then Throw New Exception("Minecraft 已崩溃")
  90. GameWindowState = LoadState.Waiting
  91. '启动后处理
  92. frmHomeRight.StartProcess = 0.96
  93. log("[Launch] 正在进行启动后处理")
  94. frmHomeRight.StartButtomSet("进行启动后处理中")
  95. GameThreadEnding()
  96. '启动结束,由动画触发结束事件
  97. frmHomeRight.StartProcess = 1
  98. log("[Launch] 启动结束")
  99. Catch ex As Exception
  100. If ex.Message = "" Then
  101. '空异常代表已知的取消
  102. frmHomeRight.StartProcess = -1
  103. Else
  104. '非空异常代表未知的启动失败
  105. frmHomeRight.StartProcess = -2
  106. If ex.Message.Contains("!") Or ex.Message.Contains("Minecraft 已崩溃") Then
  107. '带有感叹号的是我写的错误信息
  108. ShowHint(New HintConverter("游戏启动失败:" & ex.Message, HintState.Critical))
  109. Else
  110. '不带有感叹号的是系统错误信息,直接利用 ExShow 返回
  111. ExShow(ex, "游戏启动失败", ErrorLevel.MsgboxWithoutFeedback)
  112. End If
  113. End If
  114. End Try
  115. End Sub
  116. #End Region
  117. #Region "0.03-0.06 检查"
  118. Private Sub GameCheck(Info As GameStartInfo)
  119. '游戏名检测
  120. If Info.UserName = "" Or Info.UserName = "游戏名" Then
  121. Throw New FormatException("游戏名不能为空!")
  122. End If
  123. If Info.UserName.Contains("""") Then
  124. Throw New FormatException("游戏名不能包含引号!")
  125. End If
  126. 'Java 检测
  127. If Not File.Exists(PATH_JAVA & "\javaw.exe") Then
  128. Select Case MyMsgbox("未找到 Java,请下载或人工查找!", "提示", "下载 Java", "人工查找", "取消")
  129. Case 1
  130. If MODE_OFFLINE Then
  131. '无网络连接
  132. ShowHint(New HintConverter("没有网络连接,无法下载", HintState.Warn))
  133. Else
  134. '有网络连接
  135. Process.Start("https://www.java.com/winoffline_installer/")
  136. MyMsgbox("请在下载安装结束后重启电脑,再开始游戏。", "下载提示")
  137. End If
  138. Case 2
  139. '选择文件
  140. Using fileDialog As New Forms.OpenFileDialog
  141. fileDialog.AddExtension = True
  142. fileDialog.AutoUpgradeEnabled = True
  143. fileDialog.CheckFileExists = True
  144. fileDialog.Filter = "JavaW|javaw.exe"
  145. fileDialog.Multiselect = False
  146. fileDialog.Title = "选择 javaw.exe"
  147. fileDialog.ShowDialog()
  148. '选择文件结束
  149. If Not fileDialog.FileName = "" Then
  150. PATH_JAVA = Mid(fileDialog.FileName, 1, fileDialog.FileName.LastIndexOf("\"))
  151. GoTo JavaOK
  152. End If
  153. End Using
  154. WriteReg("SetupJavaPath", PATH_JAVA)
  155. log("[Launch] 人工指定的新的Java路径:" & PATH_JAVA)
  156. Dim th As New Thread(AddressOf SetJavaEnvironment)
  157. th.Priority = ThreadPriority.AboveNormal
  158. th.Start()
  159. Thread.Sleep(2000)
  160. End Select
  161. Throw New Exception("")
  162. Else
  163. JavaOK:
  164. log("[Launch] Java路径:" & PATH_JAVA)
  165. End If
  166. 'Java 环境变量检测
  167. If Not PathEnv.Contains(PATH_JAVA) Then
  168. frmHomeRight.StartButtomSet("设置环境变量中")
  169. SetJavaEnvironment()
  170. Thread.Sleep(2000)
  171. frmHomeRight.StartButtomSet("检查基础信息中")
  172. End If
  173. 'Java 版本检测
  174. If Val(Mid(Info.SelectVersion.ReleaseTime, 1, 4)) >= 2017 Then
  175. Dim JavaVersion As String = GetFileVersion(PATH_JAVA & "\javaw.exe")
  176. log("[Launch] Java 版本:" & JavaVersion)
  177. '版本不足
  178. If Val(Mid(JavaVersion, 1, 1)) < 8 Then
  179. If MyMsgbox("自 Minecraft 1.12 起,Minecraft 必须要 Java 8 或者更高版本才能启动,请在官网下载安装最新版的 Java!", "提示", "下载Java", "取消") = 1 Then
  180. If MODE_OFFLINE Then
  181. '无网络连接
  182. ShowHint(New HintConverter("没有网络连接,无法下载", HintState.Warn))
  183. Else
  184. '有网络连接
  185. Process.Start("https://www.java.com/winoffline_installer/")
  186. MyMsgbox("请在下载安装结束后重启电脑,然后在 启动设置 的 Java 路径 中填写新版 Java 的路径,再开始游戏。", "下载提示")
  187. End If
  188. End If
  189. Throw New Exception("") '抛出空异常时不会弹窗
  190. End If
  191. Else
  192. log("[Launch] 不需要强制使用 Java 8 启动,跳过版本检查")
  193. End If
  194. End Sub
  195. #End Region
  196. #Region "0.06-0.36 支持库检查"
  197. Private Structure GameLibFile
  198. ''' <summary>
  199. ''' 文件的本地完整路径。
  200. ''' </summary>
  201. ''' <remarks></remarks>
  202. Public LocalPath As String
  203. ''' <summary>
  204. ''' 文件大小。
  205. ''' </summary>
  206. ''' <remarks></remarks>
  207. Public Size As FileSize
  208. ''' <summary>
  209. ''' 是否为Natives文件。
  210. ''' </summary>
  211. ''' <remarks></remarks>
  212. Public IsNatives As Boolean
  213. Public Overrides Function ToString() As String
  214. Return If(IsNatives, "[Native] ", "") & Size.ToString & " | " & LocalPath
  215. End Function
  216. End Structure
  217. Private Sub GameLibStartCheck(Info As GameStartInfo)
  218. Dim FileList As New ArrayList
  219. If Not GameLibCheck(Info.SelectVersion, FileList) Then
  220. 'FileList 在调用 GameLibCheck 时用 ByRef 获取
  221. frmHomeRight.StartButtomSet("下载支持库中")
  222. GameLibDownload(Info.SelectVersion.Name, FileList)
  223. End If
  224. End Sub
  225. ''' <summary>
  226. ''' 获取 Minecraft 某一版本的完整支持库列表。(包含引用版本等)
  227. ''' </summary>
  228. Private Function GameLibListGet(Version As MCVersion) As ArrayList
  229. '初步获取引用表
  230. log("[Launch] 开始获取引用表")
  231. GameLibListGet = GameLibListGetByJson(Version.Json("libraries"))
  232. '处理继承版本
  233. If Version.InheritVersion = "" Then
  234. '没有继承,添加当前版本jar作为引用
  235. log("[Launch] 没有继承版本")
  236. GameLibListGet.Add(New GameLibFile With {.LocalPath = Version.Path & Version.Name & ".jar", .Size = GetFileSize(Version.Path & Version.Name & ".jar"), .IsNatives = False})
  237. Else
  238. '读取Json
  239. log("[Launch] 读取继承版本Json:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  240. Dim InheritVersionJson As JObject = Nothing
  241. For Each Encode As Encoding In {Encoding.Default, New UTF8Encoding(False), Encoding.Unicode, Encoding.ASCII}
  242. Try
  243. InheritVersionJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(ReadFileToEnd(PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json", Encode)), JObject)
  244. GoTo InheritJsonReadFinish
  245. Catch
  246. '编码错误
  247. InheritVersionJson = Nothing
  248. End Try
  249. Next
  250. '各种编码都没戏了
  251. Throw New FileFormatException("Json文件读取失败:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  252. InheritJsonReadFinish:
  253. log("[Launch] 获取继承版本引用表")
  254. '获取继承版本中的Json文件
  255. GameLibListGet.AddRange(GameLibListGetByJson(InheritVersionJson("libraries")))
  256. '添加继承的Jar
  257. GameLibListGet.Add(New GameLibFile With {.LocalPath = PATH_MC & "versions\" & Version.Json("inheritsFrom").ToString & "\" & Version.Json("inheritsFrom").ToString & ".jar",
  258. .Size = GetFileSize(PATH_MC & "versions\" & Version.Json("inheritsFrom").ToString & "\" & Version.Json("inheritsFrom").ToString & ".jar"), .IsNatives = False})
  259. End If
  260. End Function
  261. ''' <summary>
  262. ''' 获取 Minecraft 某一版本的支持库列表。
  263. ''' </summary>
  264. ''' <param name="LibJson">版本 json 中的 libraries 项。</param>
  265. ''' <returns></returns>
  266. Private Function GameLibListGetByJson(ByVal LibJson As JToken) As ArrayList
  267. GameLibListGetByJson = New ArrayList
  268. For Each Library As JToken In LibJson
  269. '检查是否需要(Rules)
  270. If Not GameRuleCheck(Library("rules")) Then GoTo NextFile
  271. '获取名称(Name)
  272. Dim SourceArray() As String
  273. SourceArray = Library("name").ToString.Split(":")
  274. '根据是否本地化处理(Natives)
  275. If IsNothing(Library("natives")) Then
  276. '没有Natives
  277. Dim Size As Integer = 0
  278. Try
  279. If IsNothing(Library("downloads")) Then
  280. Size = 0
  281. Else
  282. Size = Val(Library("downloads")("artifact")("size").ToString)
  283. End If
  284. Catch
  285. Size = 0
  286. End Try
  287. '添加
  288. GameLibListGetByJson.Add(New GameLibFile With {.LocalPath =
  289. PATH_MC & "libraries\" &
  290. SourceArray(0).Replace(".", "\") & "\" &
  291. SourceArray(1) & "\" &
  292. SourceArray(2) & "\" &
  293. SourceArray(1) & "-" & SourceArray(2) & ".jar", .Size = Size, .IsNatives = False})
  294. Else
  295. '有natives
  296. If Not IsNothing(Library("natives")("windows")) Then
  297. Dim Size As Integer = 0
  298. Try
  299. If IsNothing(Library("downloads")) Then
  300. Size = 0
  301. Else
  302. Size = Val(Library("downloads")("classifiers")("natives-windows")("size").ToString)
  303. End If
  304. Catch
  305. Size = 0
  306. End Try
  307. GameLibListGetByJson.Add(New GameLibFile With {.LocalPath =
  308. (PATH_MC & "libraries\" &
  309. SourceArray(0).Replace(".", "\") & "\" &
  310. SourceArray(1) & "\" &
  311. SourceArray(2) & "\" &
  312. SourceArray(1) & "-" & SourceArray(2) & "-" & Library("natives")("windows").ToString & ".jar").Replace("${arch}", If(Environment.Is64BitOperatingSystem, "64", "32")), .Size = Size, .IsNatives = True})
  313. End If
  314. End If
  315. NextFile:
  316. Next
  317. End Function
  318. ''' <summary>
  319. ''' 检查 Minecraft 的某一版本的支持库是否存在。
  320. ''' </summary>
  321. Private Function GameLibCheck(ByVal Version As MCVersion, ByRef FileList As ArrayList)
  322. Try
  323. '检查每个文件的大小是否合乎要求
  324. FileList = GameLibListGet(Version)
  325. For i As Integer = FileList.Count - 1 To 0 Step -1
  326. If FileList(i).Size = 0 Then
  327. '不包含大小信息
  328. If Not File.Exists(FileList(i).LocalPath) Then
  329. log("[Launch] 支持库有误:" & FileList(i).LocalPath)
  330. If FileList(i).LocalPath.Contains(PATH_MC & "versions\") Then
  331. Throw New Exception("版本文件缺失:" & FileList(i).LocalPath & ",请尝试重新下载安装该版本。")
  332. End If
  333. Return False
  334. End If
  335. Else
  336. '包含大小信息
  337. If Not GetFileSize(FileList(i).LocalPath) = FileList(i).Size Then
  338. log("[Launch] 支持库有误:" & FileList(i).LocalPath)
  339. If FileList(i).LocalPath.Contains(PATH_MC & "versions\") Then
  340. Throw New Exception("版本文件缺失:" & FileList(i).LocalPath & ",请尝试重新下载安装该版本。")
  341. End If
  342. Return False
  343. End If
  344. End If
  345. Next
  346. Catch ex As Exception
  347. If ex.Message.Contains("版本文件缺失") Then Throw ex
  348. ExShow(ex, "检查支持库完整性出错", ErrorLevel.Slient)
  349. Return False
  350. End Try
  351. '没有出错就返回 True
  352. Return True
  353. End Function
  354. Private GameLibIndexDownloading As LoadState = LoadState.Waiting
  355. ''' <summary>
  356. ''' 下载 Minecraft 的支持库。
  357. ''' </summary>
  358. ''' <remarks></remarks>
  359. Public Sub GameLibDownload(ByVal VersionName As String, FileList As ArrayList)
  360. log("[Launch] 下载支持库:" & VersionName)
  361. GameLibIndexDownloading = LoadState.Loading
  362. Dim Source As New ArrayList
  363. For Each File As GameLibFile In FileList
  364. If File.LocalPath.Contains("minecraftforge") Then
  365. If ReadIni("setup", "DownForge", "1") = "0" Then '官方源优先
  366. Source.Add(New WebRequireFile With {
  367. .WebURLs = New ArrayList From {
  368. "http://files.minecraftforge.net/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/").Replace(".jar", "-universal.jar"),
  369. "http://bmclapi2.bangbang93.com/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/").Replace(".jar", "-universal.jar"),
  370. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/")
  371. },
  372. .LocalFolder = GetPathFromFullPath(File.LocalPath), .LocalName = GetFileNameFromPath(File.LocalPath),
  373. .KnownFileSize = File.Size
  374. })
  375. Else
  376. 'BMCLAPI 优先
  377. Source.Add(New WebRequireFile With {
  378. .WebURLs = New ArrayList From {
  379. "http://bmclapi2.bangbang93.com/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/").Replace(".jar", "-universal.jar"),
  380. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  381. "http://files.minecraftforge.net/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/").Replace(".jar", "-universal.jar")
  382. },
  383. .LocalFolder = GetPathFromFullPath(File.LocalPath), .LocalName = GetFileNameFromPath(File.LocalPath),
  384. .KnownFileSize = File.Size
  385. })
  386. End If
  387. ElseIf File.LocalPath.Contains("optifine\OptiFine") Then
  388. Source.Add(New WebRequireFile With {
  389. .WebURLs = New ArrayList From {
  390. "http://bmclapi2.bangbang93.com/maven/com/optifine/" & File.LocalPath.Replace(PATH_MC & "libraries\optifine\OptiFine\", "").Split("_")(0) & "/" & GetFileNameFromPath(File.LocalPath).Replace("-", "_")
  391. },
  392. .LocalFolder = GetPathFromFullPath(File.LocalPath), .LocalName = GetFileNameFromPath(File.LocalPath),
  393. .KnownFileSize = File.Size
  394. })
  395. Else
  396. If ReadIni("setup", "DownMinecraft", "1") = "0" Then
  397. '官方源优先
  398. Source.Add(New WebRequireFile With {
  399. .WebURLs = New ArrayList From {
  400. "https://libraries.minecraft.net" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  401. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  402. "https://libraries.minecraft.net" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  403. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  404. "http://bmclapi2.bangbang93.com/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/")
  405. },
  406. .LocalFolder = GetPathFromFullPath(File.LocalPath), .LocalName = GetFileNameFromPath(File.LocalPath),
  407. .KnownFileSize = File.Size
  408. })
  409. Else
  410. 'BMCLAPI 优先
  411. Source.Add(New WebRequireFile With {
  412. .WebURLs = New ArrayList From {
  413. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  414. "https://libraries.minecraft.net" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  415. "http://bmclapi2.bangbang93.com/libraries" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  416. "https://libraries.minecraft.net" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/"),
  417. "http://bmclapi2.bangbang93.com/maven" & File.LocalPath.Replace(PATH_MC & "libraries", "").Replace("\", "/")
  418. },
  419. .LocalFolder = GetPathFromFullPath(File.LocalPath), .LocalName = GetFileNameFromPath(File.LocalPath),
  420. .KnownFileSize = File.Size
  421. })
  422. End If
  423. End If
  424. Next
  425. WebStart(ArrayConventer(Of WebRequireFile, ArrayList)(Source),
  426. Name:=VersionName & " 支持库",
  427. OnSuccess:=Sub() GameLibIndexDownloading = LoadState.Loaded,
  428. OnFail:=Sub() GameLibIndexDownloading = LoadState.Failed,
  429. RequireSize:=WebRequireSize.AtLeast)
  430. '等待下载结束
  431. Do While GameLibIndexDownloading = LoadState.Loading
  432. Try
  433. Thread.Sleep(250)
  434. If WebGroups.Keys.Contains(VersionName & " 支持库") Then
  435. Dim Percent As Double = WebGroups(VersionName & " 支持库").Percent
  436. frmHomeRight.StartButtomSet("下载支持库中 " & Math.Round(Percent * 100, 1) & "%")
  437. frmHomeRight.StartProcess = 0.09 + MathRange(Percent, 0, 1) * 0.27
  438. End If
  439. Catch
  440. End Try
  441. Loop
  442. '如果下载失败则返回
  443. If GameLibIndexDownloading = LoadState.Failed Then Throw New Exception("下载支持库失败!")
  444. End Sub
  445. #End Region
  446. #Region "0.36-0.72 资源文件检查"
  447. Private Structure GameAssetsFile
  448. Public IsVirtual As Boolean
  449. ''' <summary>
  450. ''' 本地文件的完整路径,包含文件名。
  451. ''' </summary>
  452. Public LocalPath As String
  453. Public Hash As String
  454. ''' <summary>
  455. ''' Json 中书写的源路径。例如 minecraft/sounds/mob/stray/death2.ogg 。
  456. ''' </summary>
  457. Public SourcePath As String
  458. Public Size As Integer
  459. End Structure
  460. Public Sub GameAssetsStartCheck(Info As GameStartInfo)
  461. If Info.SelectVersion.Assets = "" Then
  462. log("[Launch] 不存在资源文件项,可能是由于 Json 不标准")
  463. Else
  464. log("[Launch] 资源文件:" & Info.SelectVersion.Assets)
  465. If Not GameAssetsCheck(Info.SelectVersion.Assets) Then
  466. If ReadIni("setup", "DownMinecraftAssetsAuto", "True") = "True" Then
  467. GoTo StartDownload
  468. ElseIf MyMsgbox("Minecraft 资源文件(" & Info.SelectVersion.Assets & ")未下载。" & vbCrLf & "若不下载,游戏可能没有中文与声音,是否立即下载?" & vbCrLf & "你可以在设置中关闭此提示。", "缺少资源文件", "下载", "取消") = 1 Then
  469. StartDownload:
  470. frmHomeRight.StartProcess = 0.39
  471. frmHomeRight.StartButtomSet("下载资源文件中")
  472. GameAssetsDownload(Info.SelectVersion)
  473. End If
  474. End If
  475. End If
  476. End Sub
  477. ''' <summary>
  478. ''' 获取 Minecraft 的资源文件列表。
  479. ''' </summary>
  480. ''' <param name="Name">版本的资源名称。</param>
  481. ''' <returns></returns>
  482. ''' <remarks></remarks>
  483. Public Function GameAssetsListGet(ByVal Name As String) As ArrayList
  484. Try
  485. '初始化
  486. If Not File.Exists(PATH_MC & "assets\indexes\" & Name & ".json") Then Throw New FileNotFoundException("索引文件未找到", PATH_MC & "assets\indexes\" & Name & ".json")
  487. GameAssetsListGet = New ArrayList
  488. Dim Json = ReadJson(ReadFileToEnd(PATH_MC & "assets\indexes\" & Name & ".json"))
  489. '确认Virtual
  490. Dim IsVirtual As Boolean = False
  491. If Not IsNothing(Json("virtual")) Then IsVirtual = Json("virtual").ToString
  492. '加载列表
  493. If IsVirtual Then
  494. For Each File As JProperty In Json("objects").Children
  495. GameAssetsListGet.Add(New GameAssetsFile With {
  496. .IsVirtual = True,
  497. .LocalPath = PATH_MC & "assets\virtual\" & Name & "\" & File.Name.Replace("/", "\"),
  498. .SourcePath = File.Name,
  499. .Hash = File.Value("hash").ToString,
  500. .Size = File.Value("size").ToString
  501. })
  502. Next
  503. Else
  504. For Each File As JProperty In Json("objects").Children
  505. GameAssetsListGet.Add(New GameAssetsFile With {
  506. .IsVirtual = False,
  507. .LocalPath = PATH_MC & "assets\objects\" & Left(File.Value("hash").ToString, 2) & "\" & File.Value("hash").ToString,
  508. .SourcePath = File.Name,
  509. .Hash = File.Value("hash").ToString,
  510. .Size = File.Value("size").ToString
  511. })
  512. Next
  513. End If
  514. Catch ex As Exception
  515. ExShow(ex, "获取资源文件列表失败:" & Name)
  516. Throw
  517. End Try
  518. End Function
  519. ''' <summary>
  520. ''' 检查 Minecraft 的某一版本的资源文件是否存在。
  521. ''' </summary>
  522. ''' <param name="Name">版本的资源名称。</param>
  523. ''' <returns></returns>
  524. ''' <remarks></remarks>
  525. Public Function GameAssetsCheck(ByVal Name As String)
  526. Try
  527. '检查 Json 是否存在
  528. If Not File.Exists(PATH_MC & "assets\indexes\" & Name & ".json") Then Return False
  529. '检查每个文件的大小是否合乎要求
  530. Dim FileList As ArrayList = GameAssetsListGet(Name)
  531. For i As Integer = FileList.Count - 1 To 0 Step -1
  532. If Not GetFileSize(FileList(i).LocalPath) = FileList(i).Size Then
  533. log("[System] 资源文件有误:" & FileList(i).LocalPath)
  534. Return False
  535. End If
  536. Next
  537. Catch ex As Exception
  538. ExShow(ex, "检查资源文件完整性出错:" & Name, ErrorLevel.Slient)
  539. Return False
  540. End Try
  541. '没有出错就返回 True
  542. Return True
  543. End Function
  544. Private GameAssetsIndexDownloading As LoadState = LoadState.Waiting
  545. ''' <summary>
  546. ''' 下载 Minecraft 的资源文件。
  547. ''' </summary>
  548. ''' <param name="Version">版本信息。</param>
  549. ''' <remarks></remarks>
  550. Public Sub GameAssetsDownload(ByVal Version As MCVersion)
  551. log("[Launch] 下载资源文件:" & Version.Assets)
  552. Directory.CreateDirectory(PATH_MC & "assets\indexes\")
  553. '获取资源文件索引下载地址及文件大小,输出为 AssetsAddress 与 AssetsSize
  554. Dim AssetsAddress As String = ""
  555. Dim AssetsSize As Integer = 0
  556. Try
  557. '直接获取
  558. Dim URLKey As JToken = Version.Json("assetIndex")
  559. If Not IsNothing(URLKey) Then
  560. AssetsAddress = URLKey("url").ToString
  561. AssetsSize = URLKey("size").ToString
  562. GoTo FinishJson
  563. End If
  564. '从继承版本获取
  565. If Not Version.InheritVersion = "" Then
  566. Dim InheritVersion = New MCVersion(Version.InheritVersion)
  567. URLKey = InheritVersion.Json("assetIndex")
  568. If Not IsNothing(URLKey) Then
  569. AssetsAddress = URLKey("url").ToString
  570. AssetsSize = URLKey("size").ToString
  571. GoTo FinishJson
  572. End If
  573. End If
  574. Catch ex As Exception
  575. log("[Launch] 获取资源文件索引下载地址失败:" & GetStringFromException(ex))
  576. End Try
  577. FinishJson:
  578. If AssetsAddress = "" Then
  579. AssetsAddress = "http://s3.anazonaws.com/Minecraft.Download/indexes/" & Version.Assets & ".json"
  580. log("[Launch] 无法获取资源文件索引下载地址,使用旧版本下载地址:" & AssetsAddress)
  581. Else
  582. log("[Launch] 资源文件索引下载地址:" & AssetsAddress)
  583. End If
  584. '下载资源文件索引
  585. GameAssetsIndexDownloading = LoadState.Loading
  586. WebStart({New WebRequireFile With {
  587. .WebURLs = New ArrayList From {
  588. "http://bmclapi2.bangbang93.com/indexes/" & Version.Assets & ".json",
  589. AssetsAddress.Replace("https://launchermeta.mojang.com", "http://bmclapi2.bangbang93.com"),
  590. AssetsAddress,
  591. "http://s3.anazonaws.com/Minecraft.Download/indexes/" & Version.Assets & ".json"},
  592. .LocalFolder = PATH_MC & "assets\indexes\", .LocalName = Version.Assets & ".json",
  593. .KnownFileSize = AssetsSize
  594. }},
  595. Name:=Version.Assets & " 资源文件信息",
  596. OnSuccess:=Sub() GameAssetsIndexDownloading = LoadState.Loaded,
  597. OnFail:=Sub() GameAssetsIndexDownloading = LoadState.Failed,
  598. RequireSize:=If(AssetsSize = 0, WebRequireSize.Require, WebRequireSize.Known))
  599. '等待下载结束
  600. Do While GameAssetsIndexDownloading = LoadState.Loading
  601. Thread.Sleep(50)
  602. Loop
  603. '如果下载失败则返回
  604. If GameAssetsIndexDownloading = LoadState.Failed Then Throw New Exception("下载资源文件索引失败!")
  605. '下载资源文件
  606. GameAssetsIndexDownloading = LoadState.Loading
  607. Dim FileList As ArrayList = GameAssetsListGet(Version.Assets)
  608. Dim KVList As New ArrayList
  609. For Each AssetFile As GameAssetsFile In FileList
  610. If ReadIni("setup", "DownAssets", "0") = "0" Then
  611. KVList.Add(New WebRequireFile With {
  612. .LocalFolder = GetPathFromFullPath(AssetFile.LocalPath), .LocalName = GetFileNameFromPath(AssetFile.LocalPath),
  613. .WebURLs = New ArrayList From {
  614. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  615. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  616. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  617. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  618. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  619. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  620. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  621. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  622. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  623. "http://bmclapi2.bangbang93.com/resources/" & AssetFile.SourcePath},
  624. .KnownFileSize = AssetFile.Size
  625. })
  626. Else
  627. KVList.Add(New WebRequireFile With {
  628. .LocalFolder = GetPathFromFullPath(AssetFile.LocalPath), .LocalName = GetFileNameFromPath(AssetFile.LocalPath),
  629. .WebURLs = New ArrayList From {
  630. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  631. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  632. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  633. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  634. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  635. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  636. "http://bmclapi2.bangbang93.com/assets/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  637. "http://resources.download.minecraft.net/" & Left(AssetFile.Hash, 2) & "/" & AssetFile.Hash,
  638. "http://bmclapi2.bangbang93.com/resources/" & AssetFile.SourcePath},
  639. .KnownFileSize = AssetFile.Size
  640. })
  641. End If
  642. Next
  643. WebStart(ArrayConventer(Of WebRequireFile, ArrayList)(KVList),
  644. Name:=Version.Assets & " 资源文件",
  645. OnSuccess:=Sub() GameAssetsIndexDownloading = LoadState.Loaded,
  646. OnFail:=Sub() GameAssetsIndexDownloading = LoadState.Failed,
  647. RequireSize:=WebRequireSize.Known)
  648. '等待下载结束
  649. Do While GameAssetsIndexDownloading = LoadState.Loading
  650. Try
  651. Thread.Sleep(250)
  652. If WebGroups.Keys.Contains(Version.Assets & " 资源文件") Then
  653. Dim Percent As Double = WebGroups(Version.Assets & " 资源文件").Percent
  654. frmHomeRight.StartButtomSet("下载资源文件中 " & Math.Round(Percent * 100, 1) & "%")
  655. frmHomeRight.StartProcess = 0.42 + MathRange(Percent, 0, 1) * 0.3
  656. End If
  657. Catch
  658. End Try
  659. Loop
  660. '如果下载失败则返回
  661. If GameAssetsIndexDownloading = LoadState.Failed Then Throw New Exception("下载资源文件失败!")
  662. End Sub
  663. #End Region
  664. #Region "0.72-0.82 基础参数"
  665. Dim GameArguments As New Dictionary(Of String, String)
  666. ''' <summary>
  667. ''' 获取 Minecraft 启动所需的 JVM 字符串(第一段)。这个函数会抛出 Exception。
  668. ''' </summary>
  669. Private Function GameArgumentsJVM(ByRef Version As MCVersion) As String
  670. If IsNothing(Version.Json("arguments")) Then
  671. '老版
  672. Return GameArgumentsJVMOld(Version)
  673. Else
  674. '新版
  675. Return GameArgumentsJVMNew(Version)
  676. End If
  677. End Function
  678. ''' <summary>
  679. ''' 获取老版 Minecraft JVM 启动参数。
  680. ''' </summary>
  681. Private Function GameArgumentsJVMOld(ByRef Version As MCVersion) As String
  682. '存储以空格为间隔的启动参数列表
  683. Dim DataList As New ArrayList
  684. '输出固定参数
  685. Dim JVMArguments As String = ReadIni("setup", "LaunchJVM", "-XX:+UseG1GC -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow -Dfml.ignoreInvalidMinecraftCertificates=True -Dfml.ignorePatchDiscrepancies=True")
  686. DataList.Add("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump")
  687. DataList.Add(JVMArguments)
  688. DataList.Add("-Xmn128m")
  689. DataList.Add("-Xmx" & ReadReg("LaunchMaxRam", "1024") & "m")
  690. DataList.Add("""-Djava.library.path=" & Version.Path & Version.Name & "-natives""")
  691. '获取支持库列表
  692. Dim LibList As ArrayList = GameLibListGet(Version)
  693. Dim CPString As String = "-cp """
  694. For Each Library As GameLibFile In LibList
  695. If Not Library.IsNatives Then CPString = CPString & Library.LocalPath & ";"
  696. Next
  697. '把支持库添加进启动参数表
  698. DataList.Add(Mid(CPString, 1, Len(CPString) - 1) & """")
  699. '解压Natives
  700. frmHomeRight.StartButtomSet("解压文件中")
  701. frmHomeRight.StartProcess = 0.74
  702. '删除原文件
  703. If ReadIni("setup", "LaunchNatives", "True") = "False" Then
  704. log("[Launch] Natives解压根据设置跳过")
  705. GoTo NoUnrar
  706. End If
  707. log("[Launch] 删除原Natives")
  708. Directory.CreateDirectory(Version.Path & Version.Name & "-natives")
  709. For Each FileName As String In IO.Directory.GetFiles(Version.Path & Version.Name & "-natives")
  710. Try
  711. File.Delete(FileName)
  712. Catch ex As Exception
  713. log("[System] 删除原dll访问被拒绝,这通常代表有一个MC正在运行,跳过解压步骤")
  714. log("[System] 实际的错误信息:" & GetStringFromException(ex))
  715. GoTo NoUnrar
  716. End Try
  717. Next
  718. frmHomeRight.StartProcess = 0.77
  719. '解压文件
  720. log("[Launch] 解压新Natives")
  721. For Each Native As GameLibFile In LibList
  722. If Not Native.IsNatives Then GoTo NextFile
  723. Try
  724. Using zip As New ZipFile(Native.LocalPath)
  725. For Each File As ZipEntry In zip
  726. If File.FileName.Contains("dll") Then
  727. IO.File.Delete(Version.Path & Version.Name & "-natives\" & File.FileName)
  728. zip.ExtractSelectedEntries(File.FileName, "", Version.Path & Version.Name & "-natives")
  729. End If
  730. Next
  731. log("[Launch] 解压的Native文件:" & Native.LocalPath)
  732. End Using
  733. Catch ex As Exception
  734. ExShow(ex, "解压文件失败")
  735. If MyMsgbox("解压文件失败,是否要继续启动游戏?" & vbCrLf & vbCrLf & "详细的错误信息:" & GetStringFromException(ex, True), "启动游戏异常", "确定", "取消", , True) = 2 Then Throw
  736. End Try
  737. NextFile:
  738. Next
  739. NoUnrar:
  740. '添加 MainClass
  741. DataList.Add(Version.Json("mainClass").ToString)
  742. Return Join(DataList.ToArray)
  743. End Function
  744. ''' <summary>
  745. ''' 获取新版 Minecraft JVM 启动参数。
  746. ''' </summary>
  747. Private Function GameArgumentsJVMNew(ByRef Version As MCVersion) As String
  748. '加载可用参数
  749. GameArguments.Add("${natives_directory}", Version.Path & Version.Name & "-natives")
  750. GameArguments.Add("${launcher_name}", APPLICATION_FULL_NAME)
  751. GameArguments.Add("${launcher_version}", VERSION_CODE)
  752. '获取支持库列表
  753. Dim LibList As ArrayList = GameLibListGet(Version)
  754. Dim CPString As String = ""
  755. For Each Library As GameLibFile In LibList
  756. If Not Library.IsNatives Then CPString = CPString & Library.LocalPath & ";"
  757. Next
  758. GameArguments.Add("${classpath}", Mid(CPString, 1, Len(CPString) - 1))
  759. '解压Natives
  760. frmHomeRight.StartButtomSet("解压文件中")
  761. frmHomeRight.StartProcess = 0.74
  762. '删除原文件
  763. If ReadIni("setup", "LaunchNatives", "True") = "False" Then
  764. log("[Launch] Natives解压根据设置跳过")
  765. GoTo NoUnrar
  766. End If
  767. log("[Launch] 删除原Natives")
  768. Directory.CreateDirectory(Version.Path & Version.Name & "-natives")
  769. For Each FileName As String In IO.Directory.GetFiles(Version.Path & Version.Name & "-natives")
  770. Try
  771. File.Delete(FileName)
  772. Catch ex As Exception
  773. log("[System] 删除原dll访问被拒绝,这通常代表有一个MC正在运行,跳过解压步骤")
  774. log("[System] 实际的错误信息:" & GetStringFromException(ex))
  775. GoTo NoUnrar
  776. End Try
  777. Next
  778. frmHomeRight.StartProcess = 0.77
  779. '解压文件
  780. log("[Launch] 解压新Natives")
  781. For Each Native As GameLibFile In LibList
  782. If Not Native.IsNatives Then GoTo NextFile
  783. Try
  784. Using zip As New ZipFile(Native.LocalPath)
  785. For Each File As ZipEntry In zip
  786. If File.FileName.Contains("dll") Then
  787. IO.File.Delete(Version.Path & Version.Name & "-natives\" & File.FileName)
  788. zip.ExtractSelectedEntries(File.FileName, "", Version.Path & Version.Name & "-natives")
  789. End If
  790. Next
  791. log("[Launch] 解压的Native文件:" & Native.LocalPath)
  792. End Using
  793. Catch ex As Exception
  794. ExShow(ex, "解压文件失败")
  795. If MyMsgbox("解压文件失败,是否要继续启动游戏?" & vbCrLf & vbCrLf & "详细的错误信息:" & GetStringFromException(ex, True), "启动游戏异常", "确定", "取消", , True) = 2 Then Throw
  796. End Try
  797. NextFile:
  798. Next
  799. NoUnrar:
  800. '处理 Json
  801. Dim DataList As New ArrayList
  802. If Version.Json("arguments")("jvm") IsNot Nothing Then
  803. For Each SubJson As JToken In Version.Json("arguments")("jvm")
  804. If SubJson.Type = JTokenType.String Then
  805. '字符串类型
  806. DataList.Add(SubJson.ToString)
  807. Else
  808. '非字符串类型
  809. If GameRuleCheck(SubJson("rules")) Then
  810. '满足准则
  811. If SubJson("value").Type = JTokenType.String Then
  812. DataList.Add(SubJson("value").ToString)
  813. Else
  814. For Each value As JToken In SubJson("value")
  815. DataList.Add(value.ToString)
  816. Next
  817. End If
  818. End If
  819. End If
  820. Next
  821. End If
  822. If Version.InheritVersion <> "" Then
  823. '读取Json
  824. Log("[Launch] 读取继承版本Json:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  825. Dim InheritVersionJson As JObject = Nothing
  826. For Each Encode As Encoding In {Encoding.Default, New UTF8Encoding(False), Encoding.Unicode, Encoding.ASCII}
  827. Try
  828. InheritVersionJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(ReadFileToEnd(PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json", Encode)), JObject)
  829. GoTo InheritJsonReadFinish
  830. Catch
  831. '编码错误
  832. InheritVersionJson = Nothing
  833. End Try
  834. Next
  835. '各种编码都没戏了
  836. Throw New FileFormatException("Json文件读取失败:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  837. InheritJsonReadFinish:
  838. If InheritVersionJson("arguments")("jvm") IsNot Nothing Then
  839. For Each SubJson As JToken In InheritVersionJson("arguments")("jvm")
  840. If SubJson.Type = JTokenType.String Then
  841. '字符串类型
  842. DataList.Add(SubJson.ToString)
  843. Else
  844. '非字符串类型
  845. If GameRuleCheck(SubJson("rules")) Then
  846. '满足准则
  847. If SubJson("value").Type = JTokenType.String Then
  848. DataList.Add(SubJson("value").ToString)
  849. Else
  850. For Each value As JToken In SubJson("value")
  851. DataList.Add(value.ToString)
  852. Next
  853. End If
  854. End If
  855. End If
  856. Next
  857. End If
  858. End If
  859. '输出固定参数
  860. DataList.Add(ReadIni("setup", "LaunchJVM", "-XX:+UseG1GC -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow -Dfml.ignoreInvalidMinecraftCertificates=True -Dfml.ignorePatchDiscrepancies=True"))
  861. DataList.Add("-Xmn128m")
  862. DataList.Add("-Xmx" & ReadReg("LaunchMaxRam", "1024") & "m")
  863. '添加 MainClass
  864. DataList.Add(Version.Json("mainClass").ToString)
  865. Return Join(DataList.ToArray)
  866. End Function
  867. ''' <summary>
  868. ''' 获取 Minecraft 启动所需的 Game 字符串(第二段)。若是盗版,才传入 UserName。
  869. ''' </summary>
  870. ''' <param name="Version"></param>
  871. ''' <returns></returns>
  872. ''' <remarks></remarks>
  873. Private Function GameArgumentsGame(ByRef Version As MCVersion) As String
  874. If IsNothing(Version.Json("arguments")) Then
  875. '老版
  876. Return GameArgumentsGameOld(Version)
  877. Else
  878. '新版
  879. Return GameArgumentsGameNew(Version)
  880. End If
  881. End Function
  882. ''' <summary>
  883. ''' 获取老版 Minecraft Game 启动参数。
  884. ''' </summary>
  885. ''' <param name="Version"></param>
  886. ''' <returns></returns>
  887. ''' <remarks></remarks>
  888. Private Function GameArgumentsGameOld(ByRef Version As MCVersion) As String
  889. Dim DataList = New ArrayList
  890. '本地化 Minecraft 启动信息
  891. log("[Launch] 处理基准参数字符串")
  892. Dim BasicString As String = Version.Json("minecraftArguments").ToString & " --height ${resolution_height} --width ${resolution_width}"
  893. DataList.Add(BasicString)
  894. '添加服务器信息
  895. Dim Server As String = ReadIni("setup", "LaunchServer", "")
  896. If Server.Length > 0 Then
  897. If Server.Contains(":") Then
  898. '包含端口号
  899. DataList.Add("--server " & Server.Split(":")(0))
  900. DataList.Add("--port " & Server.Split(":")(1))
  901. Else
  902. '不包含端口号
  903. DataList.Add("--server " & Server)
  904. DataList.Add("--port 25565")
  905. End If
  906. End If
  907. '添加全屏
  908. If ReadIni("setup", "LaunchMode", "0") = "1" Then DataList.Add("--fullscreen")
  909. Return Join(DataList.ToArray)
  910. End Function
  911. ''' <summary>
  912. ''' 获取新版 Minecraft Game 启动参数。
  913. ''' </summary>
  914. ''' <param name="Version"></param>
  915. ''' <returns></returns>
  916. ''' <remarks></remarks>
  917. Private Function GameArgumentsGameNew(ByRef Version As MCVersion) As String
  918. Dim DataList = New ArrayList
  919. '处理 Json
  920. For Each SubJson As JToken In Version.Json("arguments")("game")
  921. If SubJson.Type = JTokenType.String Then
  922. '字符串类型
  923. DataList.Add(SubJson.ToString)
  924. Else
  925. '非字符串类型
  926. If GameRuleCheck(SubJson("rules")) Then
  927. '满足准则
  928. If SubJson("value").Type = JTokenType.String Then
  929. DataList.Add(SubJson("value").ToString)
  930. Else
  931. For Each value As JToken In SubJson("value")
  932. DataList.Add(value.ToString)
  933. Next
  934. End If
  935. End If
  936. End If
  937. Next
  938. If Version.InheritVersion <> "" Then
  939. '读取Json
  940. Log("[Launch] 读取继承版本Json:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  941. Dim InheritVersionJson As JObject = Nothing
  942. For Each Encode As Encoding In {Encoding.Default, New UTF8Encoding(False), Encoding.Unicode, Encoding.ASCII}
  943. Try
  944. InheritVersionJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(ReadFileToEnd(PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json", Encode)), JObject)
  945. GoTo InheritJsonReadFinish
  946. Catch
  947. '编码错误
  948. InheritVersionJson = Nothing
  949. End Try
  950. Next
  951. '各种编码都没戏了
  952. Throw New FileFormatException("Json文件读取失败:" & PATH_MC & "versions\" & Version.InheritVersion & "\" & Version.InheritVersion & ".json")
  953. InheritJsonReadFinish:
  954. If InheritVersionJson("arguments")("game") IsNot Nothing Then
  955. For Each SubJson As JToken In InheritVersionJson("arguments")("game")
  956. If SubJson.Type = JTokenType.String Then
  957. '字符串类型
  958. DataList.Add(SubJson.ToString)
  959. Else
  960. '非字符串类型
  961. If GameRuleCheck(SubJson("rules")) Then
  962. '满足准则
  963. If SubJson("value").Type = JTokenType.String Then
  964. DataList.Add(SubJson("value").ToString)
  965. Else
  966. For Each value As JToken In SubJson("value")
  967. DataList.Add(value.ToString)
  968. Next
  969. End If
  970. End If
  971. End If
  972. Next
  973. End If
  974. End If
  975. '添加服务器信息
  976. Dim Server As String = ReadIni("setup", "LaunchServer", "")
  977. If Server.Length > 0 Then
  978. If Server.Contains(":") Then
  979. '包含端口号
  980. DataList.Add("--server " & Server.Split(":")(0))
  981. DataList.Add("--port " & Server.Split(":")(1))
  982. Else
  983. '不包含端口号
  984. DataList.Add("--server " & Server)
  985. DataList.Add("--port 25565")
  986. End If
  987. End If
  988. '添加全屏
  989. If ReadIni("setup", "LaunchMode", "0") = "1" Then DataList.Add("--fullscreen")
  990. Return Join(DataList.ToArray)
  991. End Function
  992. ''' <summary>
  993. ''' 获取正版登录参数。
  994. ''' </summary>
  995. ''' <param name="Version">要启动的Minecraft版本。</param>
  996. ''' <returns></returns>
  997. ''' <remarks></remarks>
  998. Private Function GameArgumentsMojang(ByVal Version As MCVersion) As String
  999. '初始化参数
  1000. GameArguments = New Dictionary(Of String, String)
  1001. Dim LoginJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(LoginResult), JObject)
  1002. GameArguments.Add("${version_name}", Version.Name)
  1003. GameArguments.Add("${version_type}", ReadIni("setup", "LaunchShow", APPLICATION_FULL_NAME))
  1004. GameArguments.Add("${game_directory}", Mid(GetVersionFolder(Version), 1, Len(GetVersionFolder(Version)) - 1))
  1005. GameArguments.Add("${assets_root}", PATH_MC & "assets")
  1006. GameArguments.Add("${game_assets}", PATH_MC & "assets\virtual\" & If(Version.Assets = "", "legacy", Version.Assets))
  1007. GameArguments.Add("${assets_index_name}", Version.Assets)
  1008. GameArguments.Add("${user_properties}", "{}")
  1009. GameArguments.Add("${auth_player_name}", LoginJson("selectedProfile")("name").ToString)
  1010. GameArguments.Add("${auth_uuid}", LoginJson("selectedProfile")("id").ToString)
  1011. GameArguments.Add("${auth_access_token}", LoginJson("accessToken").ToString)
  1012. GameArguments.Add("${access_token}", LoginJson("accessToken").ToString)
  1013. GameArguments.Add("${auth_session}", LoginJson("accessToken").ToString)
  1014. GameArguments.Add("${user_type}", "mojang")
  1015. GameArguments.Add("${resolution_width}", ReadIni("setup", "LaunchModeWindowWidth", "854"))
  1016. GameArguments.Add("${resolution_height}", ReadIni("setup", "LaunchModeWindowHeight", "480"))
  1017. '获取字符串
  1018. Dim Arguments As String = GameArgumentsJVM(Version) & " " & GameArgumentsGame(Version)
  1019. '替换参数
  1020. For Each entry As KeyValuePair(Of String, String) In GameArguments
  1021. Arguments = Arguments.Replace(entry.Key, If(entry.Value.Contains(" ") Or entry.Value.Contains("-"), """" & entry.Value & """", entry.Value))
  1022. Next
  1023. Return Arguments
  1024. End Function
  1025. ''' <summary>
  1026. ''' 获取离线登录参数。
  1027. ''' </summary>
  1028. ''' <param name="Version">要启动的Minecraft版本</param>
  1029. ''' <param name="UserName">用户名</param>
  1030. ''' <returns></returns>
  1031. ''' <remarks></remarks>
  1032. Private Function GameArgumentsLegacy(ByVal Version As MCVersion, ByVal UserName As String) As String
  1033. '初始化参数
  1034. GameArguments = New Dictionary(Of String, String)
  1035. Dim UUID As String = GetLegacyUUID(UserName, True)
  1036. Dim AccessToken As String = SerRemove(ReadReg("AccessToken", ""))
  1037. If Not Len(AccessToken) = 32 Then AccessToken = UUID
  1038. log("[Launch] UUID:" & UUID)
  1039. GameArguments.Add("${version_name}", Version.Name)
  1040. GameArguments.Add("${version_type}", ReadIni("setup", "LaunchShow", APPLICATION_FULL_NAME))
  1041. GameArguments.Add("${game_directory}", Mid(GetVersionFolder(Version), 1, Len(GetVersionFolder(Version)) - 1))
  1042. GameArguments.Add("${assets_root}", PATH_MC & "assets")
  1043. GameArguments.Add("${game_assets}", PATH_MC & "assets\virtual\" & If(Version.Assets = "", "legacy", Version.Assets))
  1044. GameArguments.Add("${assets_index_name}", Version.Assets)
  1045. GameArguments.Add("${user_properties}", "{}")
  1046. GameArguments.Add("${auth_player_name}", UserName.Replace("""", ""))
  1047. GameArguments.Add("${auth_uuid}", UUID)
  1048. GameArguments.Add("${auth_access_token}", AccessToken)
  1049. GameArguments.Add("${access_token}", AccessToken)
  1050. GameArguments.Add("${auth_session}", AccessToken)
  1051. GameArguments.Add("${user_type}", "Legacy")
  1052. GameArguments.Add("${resolution_width}", ReadIni("setup", "LaunchModeWindowWidth", "854"))
  1053. GameArguments.Add("${resolution_height}", ReadIni("setup", "LaunchModeWindowHeight", "480"))
  1054. '获取字符串
  1055. Dim Arguments As String = GameArgumentsJVM(Version) & " " & GameArgumentsGame(Version)
  1056. '替换参数
  1057. For Each entry As KeyValuePair(Of String, String) In GameArguments
  1058. Arguments = Arguments.Replace(entry.Key, If(entry.Value.Contains(" ") Or entry.Value.Contains("-"), """" & entry.Value & """", entry.Value))
  1059. Next
  1060. Return Arguments
  1061. End Function
  1062. ''' <summary>
  1063. ''' 由皮肤设置与用户名获取离线UUID。
  1064. ''' </summary>
  1065. Public Function GetLegacyUUID(ByVal UserName As String, ByVal TryMojangSource As Boolean) As String
  1066. Select Case Val(ReadReg("LaunchSkin", "3"))
  1067. Case 0
  1068. '默认
  1069. Return FillLength(SerAdd(UserName), "0", 32) '从用户名生成
  1070. Case 1
  1071. 'Steve
  1072. Dim UUID As String = FillLength(SerAdd(UserName), "0", 32) '从用户名生成
  1073. Do Until GetSkinTypeFromUUID(UUID) = "Steve"
  1074. If UUID.EndsWith("FFFFF") Then UUID = Mid(UUID, 1, 32 - 5) & "00000"
  1075. UUID = Mid(UUID, 1, 32 - 5) & (Long.Parse(Right(UUID, 5), Globalization.NumberStyles.AllowHexSpecifier) + 1).ToString("X")
  1076. Loop
  1077. Return UUID
  1078. Case 2
  1079. 'Alex
  1080. Dim UUID As String = FillLength(SerAdd(UserName), "0", 32) '从用户名生成
  1081. Do Until GetSkinTypeFromUUID(UUID) = "Alex"
  1082. If UUID.EndsWith("FFFFF") Then UUID = Mid(UUID, 1, 32 - 5) & "00000"
  1083. UUID = Mid(UUID, 1, 32 - 5) & (Long.Parse(Right(UUID, 5), Globalization.NumberStyles.AllowHexSpecifier) + 1).ToString("X")
  1084. Loop
  1085. Return UUID
  1086. Case Else
  1087. '使用正版用户名
  1088. UserName = If(ReadReg("LaunchSkinName") = "", UserName, ReadReg("LaunchSkinName"))
  1089. Dim UUID As String = GetUUIDFromUserName(UserName, TryMojangSource)
  1090. If UUID = "" Then
  1091. Return FillLength(SerAdd(UserName), "0", 32)
  1092. Else
  1093. Return UUID
  1094. End If
  1095. End Select
  1096. End Function
  1097. ''' <summary>
  1098. ''' 从官方玩家用户名获取官方 UUID。获取失败会返回空字符串。
  1099. ''' </summary>
  1100. Public Function GetUUIDFromUserName(ByVal UserName As String, ByVal TryOnline As Boolean) As String
  1101. Try
  1102. '尝试从列表获取
  1103. GetUUIDFromUserName = ReadIni("cache\skin\UUID", UserName, "")
  1104. If Len(GetUUIDFromUserName) = 32 Then Exit Function
  1105. '尝试从 Mojang 官方获取
  1106. If Not TryOnline Then Return ""
  1107. If UserName = "" Or UserName = "游戏名" Then Return ""
  1108. Dim GotJson = CType(Newtonsoft.Json.JsonConvert.DeserializeObject(GetWebsiteCode("https://api.mojang.com/users/profiles/minecraft/" & UserName, Encoding.Default)), JObject)
  1109. If IsNothing(GotJson) Then Return ""
  1110. GetUUIDFromUserName = If(GotJson("id"), "")
  1111. If Len(GetUUIDFromUserName) = 32 Then
  1112. WriteIni("cache\skin\UUID", UserName, GetUUIDFromUserName)
  1113. Else
  1114. Return ""
  1115. End If
  1116. Catch ex As Exception
  1117. ExShow(ex, "获取 UUID 失败(" & UserName & ")", ErrorLevel.Slient)
  1118. Return ""
  1119. End Try
  1120. End Function
  1121. ''' <summary>
  1122. ''' 是否符合 Rule。输入为 "rule" 的 Json 项目,返回是否符合。
  1123. ''' </summary>
  1124. ''' <param name="RuleObj"></param>
  1125. ''' <returns></returns>
  1126. ''' <remarks></remarks>
  1127. Public Function GameRuleCheck(ByVal RuleObj As JToken) As Boolean
  1128. If IsNothing(RuleObj) Then Return True
  1129. '初始化
  1130. Dim Required As Boolean = False
  1131. For Each Rule As JToken In RuleObj
  1132. '单条条件验证
  1133. Dim IsRightRule As Boolean? = Nothing '是否为正确的规则
  1134. 'Rule: 操作系统
  1135. If Not IsNothing(Rule("os")) Then
  1136. If Rule("os")("name") = "windows" Then
  1137. '是 Windows
  1138. If IsNothing(Rule("os")("version")) Then
  1139. IsRightRule = True
  1140. Else
  1141. '要求操作系统版本检查
  1142. Dim Version As String = My.Computer.Info.OSVersion
  1143. Dim Cr As String = Rule("os")("version").ToString
  1144. IsRightRule = RegexSearch(Version, Cr).Count > 0
  1145. End If
  1146. Else
  1147. IsRightRule = False
  1148. End If
  1149. End If
  1150. 'Rule: 标签
  1151. If Not IsNothing(Rule("features")) Then
  1152. '只反选“is_demo_user”
  1153. IsRightRule = IsNothing(Rule("features")("is_demo_user"))
  1154. End If
  1155. '反选确认
  1156. If Rule("action").ToString = "allow" Then
  1157. 'allow
  1158. If If(IsRightRule, True) Then
  1159. Required = True
  1160. End If
  1161. Else
  1162. 'disallow
  1163. If If(IsRightRule, True) Then
  1164. Required = False
  1165. End If
  1166. End If
  1167. Next
  1168. Return Required
  1169. End Function
  1170. #End Region
  1171. #Region "0.82-1.00 启动游戏"
  1172. Private Sub GameThreadStart(ByVal CMD As String, ByVal Info As GameStartInfo)
  1173. 'CMD = "set appdata=" & PATH_MC & "&&cd /D %appdata%&&""" & PATH_JAVA & "\javaw.exe"" " & CMD
  1174. 'Dim myProcess As New Process()
  1175. 'Dim myProcessStartInfo As New ProcessStartInfo("cmd.exe")
  1176. 'myProcessStartInfo.UseShellExecute = False
  1177. 'myProcessStartInfo.RedirectStandardOutput = True
  1178. 'myProcessStartInfo.RedirectStandardError = True
  1179. 'myProcessStartInfo.RedirectStandardInput = True
  1180. 'myProcessStartInfo.CreateNoWindow = True
  1181. 'myProcessStartInfo.Arguments = "/c " & CMD
  1182. 'myProcess.StartInfo = myProcessStartInfo
  1183. '启动信息
  1184. Dim GameProcess = New Process()
  1185. Dim StartInfo As New ProcessStartInfo(PATH_JAVA & "\javaw.exe")
  1186. log("[Launch] Java 路径:" & PATH_JAVA & "\javaw.exe(" & GetFileVersion(PATH_JAVA & "\javaw.exe") & ")")
  1187. StartInfo.EnvironmentVariables("appdata") = PATH_MC
  1188. StartInfo.WorkingDirectory = GetVersionFolder(Info.SelectVersion)
  1189. log("[Launch] 工作目录:" & GetVersionFolder(Info.SelectVersion))
  1190. StartInfo.UseShellExecute = False
  1191. StartInfo.RedirectStandardOutput = True
  1192. StartInfo.RedirectStandardError = True
  1193. StartInfo.CreateNoWindow = False
  1194. StartInfo.Arguments = CMD
  1195. GameProcess.StartInfo = StartInfo
  1196. '开始进程
  1197. log("[Launch] 启动 Minecraft,版本:" & Info.SelectVersion.Name)
  1198. SendStat("启动", GetStringFromEnum(Info.LoginMethod), Info.SelectVersion.Name, Info.SelectVersion.MainVersionCode)
  1199. GameProcess.Start()
  1200. GameProcess.BeginOutputReadLine()
  1201. GameProcess.BeginErrorReadLine()
  1202. '设置进程优先级
  1203. Select Case Val(ReadReg("LaunchLevel", "1"))
  1204. Case 0
  1205. GameProcess.PriorityClass = ProcessPriorityClass.AboveNormal
  1206. Process.GetCurrentProcess.PriorityClass = ProcessPriorityClass.High
  1207. Case 1
  1208. '设置 MC 的进程优先级,默认为 Normal 所以不设置
  1209. Process.GetCurrentProcess.PriorityClass = ProcessPriorityClass.AboveNormal
  1210. Case 2
  1211. GameProcess.PriorityClass = ProcessPriorityClass.BelowNormal
  1212. '将 PCL 的进程优先级设置得比 MC 高一级,这样就不会造成互锁卡顿。由于这里正好是 Normal 所以不设置
  1213. End Select
  1214. '输出脚本
  1215. CMD = "title Minecraft " & Info.SelectVersion.Name.Replace("""", "").Replace("&", "") & "&&set appdata=" & PATH_MC & "&&cd /D %appdata%&&""" & PATH_JAVA & "\javaw.exe"" " & CMD
  1216. log("[Launch] 启动脚本(保存为 .bat 文件即可使用):" & vbCrLf & CMD)
  1217. '显示日志窗口
  1218. GameWindowState = LoadState.Loading
  1219. frmMain.Dispatcher.Invoke(Sub()
  1220. Dim wind As New formMinecraft(GameProcess, Info.SelectVersion, ReadIni("setup", "LaunchLog", "False") = "False")
  1221. End Sub)
  1222. End Sub
  1223. ''' <summary>
  1224. ''' 启动后处理。
  1225. ''' </summary>
  1226. Private Sub GameThreadEnding()
  1227. '剩余的内存(MB)
  1228. Dim RamLeft As Integer = (New Microsoft.VisualBasic.Devices.ComputerInfo).AvailablePhysicalMemory / 1024 / 1024
  1229. log("[Launch] Minecraft 启动成功,剩余内存:" & RamLeft & "MB")
  1230. frmMain.Dispatcher.Invoke(Sub()
  1231. Select Case Val(ReadIni("setup", "LaunchVisibility", "3"))
  1232. Case 0 '关闭
  1233. EndNormal()
  1234. Case 3 '两分钟后关闭
  1235. AniStart({
  1236. AaOpacity(frmMain, -1, 300),
  1237. AaCode({"Visible", frmMain, False}, 400),
  1238. AaCode({"ShowInTaskbar", frmMain, False}, 400),
  1239. AaCode({"End"}, 120000)
  1240. }, "EndAll")
  1241. If IsPlayingMusic Then frmMain.ChangeBgm()
  1242. log("[Launch] 启动器已隐藏,将在两分钟后关闭")
  1243. Case 1 '隐藏
  1244. AniStart({
  1245. AaOpacity(frmMain, -1, 300),
  1246. AaCode({"Visible", frmMain, False}, 400),
  1247. AaCode({"ShowInTaskbar", frmMain, False}, 400)
  1248. }, "EndAll")
  1249. If IsPlayingMusic Then frmMain.ChangeBgm()
  1250. log("[Launch] 启动器已隐藏")
  1251. Case 2 '保留
  1252. If RamLeft < 600 Then ShowHint(New HintConverter("内存仅剩 " & RamLeft & " MB,Minecraft 有可能崩溃", HintState.Warn))
  1253. Case 4 '最小化
  1254. frmMain.WindowState = WindowState.Minimized
  1255. End Select
  1256. End Sub)
  1257. End Sub
  1258. #End Region
  1259. End Module