PageDownloadModify.vb 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. ''' <summary>
  2. ''' 版本修改页:完整还原已安装组件后,复用安装页的选择交互。
  3. ''' </summary>
  4. Public Class PageDownloadModify
  5. Inherits PageDownloadInstall
  6. Private InitLoader As LoaderTask(Of McInstance, Object)
  7. Private ForgeVersionLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry))
  8. Private InitialTarget As McComponents '进入时的组件快照
  9. ''' <summary>
  10. ''' 原版的版本号是否可靠(不是通过 json 模糊分析)。
  11. ''' </summary>
  12. Private IsVanillaVersionReliable As Boolean
  13. Private OriginalName As String
  14. Private CustomLogo As String
  15. ''' <summary>
  16. ''' 开始修改 MC 版本时,记录当前选择的组件,以在修改 MC 版本后尝试自动选中相同组件。
  17. ''' </summary>
  18. Private ReadOnly PendingComponents As New Dictionary(Of MyLoading, String)
  19. Private IsRestoringComponents As Boolean
  20. Public Overrides Sub LoaderInit() '父级 Handles Me.Initialized
  21. ControlInit()
  22. InitLoader = New LoaderTask(Of McInstance, Object)("初始化版本修改", AddressOf LoadInstance)
  23. PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, InitLoader, AddressOf LoadInstanceFinished, Function() PageInstanceLeft.Instance)
  24. End Sub
  25. Public Overrides Sub Init() '父级 Handles Me.Loaded
  26. PanBack.ScrollToHome()
  27. Static IsLoad As Boolean = False
  28. If IsLoad Then PageLoaderRestart()
  29. IsLoad = True
  30. End Sub
  31. Private Sub PageUnloaded() Handles Me.Unloaded
  32. InitLoader.Cancel()
  33. End Sub
  34. ''' <summary>
  35. ''' 读取或初始化版本的安装清单。
  36. ''' </summary>
  37. Private Sub LoadInstance(Task As LoaderTask(Of McInstance, Object))
  38. '旧状态清理
  39. RunInUiWait(
  40. Sub()
  41. AniStop("FrmDownloadInstall SelectPageSwitch")
  42. IsSelectNameEdited = False
  43. ClearSelected()
  44. PendingComponents.Clear()
  45. InitialTarget = Nothing
  46. IsInSelectPage = False
  47. PanAllBack.IsHitTestVisible = False
  48. End Sub)
  49. Task.Input.Config.DiscardCache()
  50. '获取 Components
  51. Dim Manifest = Configs.McInstallManifest.Get(Task.Input.Config)
  52. If Manifest IsNot Nothing AndAlso Manifest.SchemaVersion > Versions.McInstallManifestVersion Then
  53. Logger.Warn($"安装清单由更新版本的 PCL 创建(清单版本:{Manifest.SchemaVersion},当前版本:{Versions.McInstallManifestVersion})")
  54. Throw New Exception($"更新 PCL 后才能修改它!点击【设置 → 其他 → 检查更新】以更新。")
  55. End If
  56. If Manifest IsNot Nothing Then
  57. Dim LatestManifest = Manifest.RefreshModComponents(Task.Input.PathIndie)
  58. If LatestManifest IsNot Manifest Then
  59. Manifest = LatestManifest
  60. Configs.McInstallManifest.Set(Manifest, Task.Input.Config)
  61. Task.Input.Config.Save()
  62. End If
  63. End If
  64. Dim Components As McComponents
  65. Dim OptiFineVersion As String = Nothing
  66. If Manifest IsNot Nothing Then
  67. '有清单:从清单读取
  68. Components = Manifest.Components
  69. If Components Is Nothing Then Throw New Exception("安装清单缺少组件信息,无法还原已安装组件。")
  70. IsVanillaVersionReliable = True
  71. Else
  72. '无清单:通过版本 JSON 获取
  73. Dim VersionInfo = New McInstance(Task.Input.PathVersion).Version
  74. If VersionInfo.VanillaName = "Unknown" Then Throw New Exception("无法确定 Minecraft 版本,无法修改")
  75. Dim UnknownLoaders = VersionInfo.ModLoaderVersions.
  76. Where(Function(Loader) String.IsNullOrWhiteSpace(Loader.Value) OrElse Loader.Value = "未知版本").
  77. Select(Function(Loader) Loader.Key.ToString()).ToList()
  78. If UnknownLoaders.Any() Then Throw New Exception($"无法确定 {UnknownLoaders.Join("、")} 的版本,无法修改")
  79. Components = New McComponents(VersionInfo.VanillaName, VersionInfo.ModLoaderVersions)
  80. OptiFineVersion = VersionInfo.OptiFine
  81. IsVanillaVersionReliable = VersionInfo.Reliable
  82. End If
  83. Dim MinecraftVersion = Components.MinecraftVersion
  84. If String.IsNullOrWhiteSpace(MinecraftVersion) Then Throw New Exception("无法确定当前 Minecraft 版本,请检查版本文件")
  85. '等待子列表加载器完成获取
  86. Dim LoaderList As New Dictionary(Of String, LoaderBase) From {
  87. {"Minecraft", DlClientListLoader}, {"OptiFine", DlOptiFineListLoader}, {"LiteLoader", DlLiteLoaderListLoader},
  88. {"NeoForge", DlNeoForgeListLoader}, {"Fabric", DlFabricListLoader}, {"Fabric API", DlFabricApiLoader}, {"OptiFabric", DlOptiFabricLoader}}
  89. Dim ForgeLoader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = Nothing
  90. If McVersion.IsStandardFormat(MinecraftVersion) Then
  91. ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & MinecraftVersion, AddressOf DlForgeVersionMain)
  92. LoaderList.Add("Forge", ForgeLoader)
  93. End If
  94. For Each Loader In LoaderList.Values
  95. Loader.Start(If(Loader Is ForgeLoader, MinecraftVersion, Nothing), Task.IsForceRestarting)
  96. Next
  97. Do
  98. If Task.IsCanceled Then Return
  99. For Each Pair In LoaderList
  100. Dim Loader = Pair.Value
  101. If Loader.State = LoadState.Failed Then
  102. If Loader Is ForgeLoader AndAlso Loader.Error.Message = "无" Then Continue For 'Forge 无版本时不报错
  103. Throw New Exception($"获取 {Pair.Key} 版本列表失败,请稍后再试,或使用 VPN 改善网络环境", Loader.Error)
  104. ElseIf Loader.State = LoadState.Canceled Then
  105. Throw New Exception($"获取 {Pair.Key} 版本列表被中断,请稍后再试,或使用 VPN 改善网络环境")
  106. End If
  107. Next
  108. If LoaderList.Values.All(Function(Loader) Loader.State = LoadState.Finished OrElse Loader.State = LoadState.Failed) Then Exit Do
  109. Task.Progress = 0.7 * LoaderList.Values.Average(Function(Loader) Loader.Progress)
  110. Thread.Sleep(10)
  111. Loop
  112. If Task.IsCanceled Then Return
  113. Task.Progress = 0.7
  114. 'Minecraft
  115. MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
  116. VanillaData = MinecraftVersions.OfType(Of JObject).FirstOrDefault(
  117. Function(v) v("id").ToString.Replace("∞", "infinite") = MinecraftVersion.Replace("∞", "infinite"))
  118. If VanillaData Is Nothing Then Throw New Exception($"Minecraft 列表中没有已安装的 {MinecraftVersion}")
  119. VanillaData("id") = MinecraftVersion
  120. 'OptiFine
  121. If Manifest IsNot Nothing Then
  122. SelectedOptiFine = Components.OptiFineEntry?.ToObject(Of DlOptiFineListEntry)()
  123. If SelectedOptiFine IsNot Nothing AndAlso Not DlOptiFineListLoader.Output.Value.Any(Function(v) v.FileName = SelectedOptiFine.FileName) Then
  124. '如果 OptiFine 是 pre 版,可能只在 BMCLAPI 下有,官方源删了。如果是这种缺失数据的情况,换用 BMCLAPI 源作为输出
  125. If DlOptiFineListBmclapiLoader.State <> LoadState.Finished Then DlOptiFineListBmclapiLoader.WaitForExit()
  126. DlOptiFineListLoader.Output = DlOptiFineListBmclapiLoader.Output
  127. End If
  128. ElseIf OptiFineVersion IsNot Nothing AndAlso OptiFineVersion <> "未知版本" Then
  129. SelectedOptiFine = DlOptiFineListLoader.Output.Value.FirstOrDefault(
  130. Function(v) v.Inherit = MinecraftVersion AndAlso
  131. v.InstanceName.AfterLast("-OptiFine_").Replace("HD_U_", "") = OptiFineVersion.Replace("HD_U_", ""))
  132. If SelectedOptiFine Is Nothing Then Throw New Exception($"OptiFine 列表中没有已安装的 {OptiFineVersion}")
  133. End If
  134. 'Forge
  135. If Components.ModLoaderVersions.ContainsKey(ModLoaders.Forge) Then
  136. SelectedForge = ForgeLoader?.Output?.FirstOrDefault(
  137. Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.Forge) AndAlso
  138. v.Category <> "universal" AndAlso v.Category <> "client" AndAlso (SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
  139. If SelectedForge Is Nothing Then Throw New Exception($"Forge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Forge)}")
  140. End If
  141. 'NeoForge
  142. If Components.ModLoaderVersions.ContainsKey(ModLoaders.NeoForge) Then
  143. SelectedNeoForge = DlNeoForgeListLoader.Output.Value.FirstOrDefault(
  144. Function(v) v.Inherit = MinecraftVersion AndAlso v.VersionName = Components.ModLoaderVersions(ModLoaders.NeoForge))
  145. If SelectedNeoForge Is Nothing Then Throw New Exception($"NeoForge 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.NeoForge)}")
  146. End If
  147. 'Fabric
  148. If Components.ModLoaderVersions.ContainsKey(ModLoaders.Fabric) Then
  149. Dim FabricEntry = DlFabricListLoader.Output.Value("loader").OfType(Of JObject).FirstOrDefault(
  150. Function(v) v("version").ToString = Components.ModLoaderVersions(ModLoaders.Fabric),
  151. Function(v) v("version").ToString.Replace("+build", "") = Components.ModLoaderVersions(ModLoaders.Fabric))
  152. If FabricEntry Is Nothing OrElse Not DlFabricListLoader.Output.Value("game").OfType(Of JObject).
  153. Any(Function(v) v("version").ToString = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")) Then
  154. Throw New Exception($"Fabric 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.Fabric)}")
  155. End If
  156. SelectedFabric = FabricEntry("version").ToString
  157. End If
  158. 'LiteLoader
  159. If Components.ModLoaderVersions.ContainsKey(ModLoaders.LiteLoader) Then
  160. SelectedLiteLoader = DlLiteLoaderListLoader.Output.Value.FirstOrDefault(
  161. Function(v) v.Inherit = MinecraftVersion AndAlso v.JsonToken("version").ToString = Components.ModLoaderVersions(ModLoaders.LiteLoader))
  162. If SelectedLiteLoader Is Nothing Then Throw New Exception($"LiteLoader 列表中没有已安装的 {Components.ModLoaderVersions(ModLoaders.LiteLoader)}")
  163. End If
  164. 'Fabric API
  165. If Components.FabricApiVersion IsNot Nothing Then
  166. SelectedFabricApi = DlFabricApiLoader.Output.FirstOrDefault(
  167. Function(v) (v.Version = Components.FabricApiVersion OrElse v.Id = Components.FabricApiVersion OrElse
  168. v.Display = Components.FabricApiVersion) AndAlso IsFabricApiCompatible(v, MinecraftVersion))
  169. If SelectedFabricApi Is Nothing Then Throw New Exception($"Fabric API 列表中没有已安装的 {Components.FabricApiVersion}")
  170. End If
  171. 'OptiFabric
  172. If Components.OptiFabricVersion IsNot Nothing Then
  173. SelectedOptiFabric = DlOptiFabricLoader.Output.FirstOrDefault(
  174. Function(v) (v.Version = Components.OptiFabricVersion OrElse v.Id = Components.OptiFabricVersion OrElse
  175. v.Display = Components.OptiFabricVersion) AndAlso IsOptiFabricCompatible(v, MinecraftVersion))
  176. If SelectedOptiFabric Is Nothing Then Throw New Exception($"OptiFabric 列表中没有已安装的 {Components.OptiFabricVersion}")
  177. End If
  178. '扫描尚未识别的 Mod 组件
  179. Dim ManagedFiles As New List(Of McInstallManifest.ManagedFile)
  180. If Manifest IsNot Nothing Then ManagedFiles.AddRange(Manifest.ManagedFiles)
  181. #Region "从 mods 文件夹中获取以 Mod 形式安装的组件信息"
  182. '获取 mods 文件夹下的 jar 文件与 SHA1
  183. Dim Mods As New Lazy(Of List(Of (File As FileInfo, Sha1 As String)))(
  184. Function()
  185. Dim ModsFolder = Task.Input.PathIndie & "mods\"
  186. Dim Files = DirectoryUtils.EnumerateFiles(ModsFolder, searchPattern:="*.jar").ToList()
  187. If SelectedLiteLoader IsNot Nothing Then 'LiteLoader 的 OptiFine 安装目录
  188. Files.AddRange(DirectoryUtils.EnumerateFiles(Path.Combine(ModsFolder, MinecraftVersion), searchPattern:="*.jar"))
  189. End If
  190. Dim Entries As New List(Of (File As FileInfo, Sha1 As String))
  191. For Each FilePath In Files
  192. If Task.IsCanceled Then Return Entries
  193. Dim File = FileUtils.GetInfo(FilePath)
  194. Entries.Add((File, New LocalResourceFile(File).ModrinthHash))
  195. Task.Progress = 0.7 + 0.28 * Entries.Count / Files.Count
  196. Next
  197. Return Entries
  198. End Function)
  199. 'Fabric API
  200. If SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing Then
  201. Dim FabricApiList = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, MinecraftVersion)).ToList()
  202. Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
  203. For Each Entry In Mods.Value
  204. Dim Match = FabricApiList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
  205. If Match Is Nothing Then Continue For
  206. Matches.Add((Entry.File, Entry.Sha1, Match))
  207. Next
  208. Dim SelectedFile = SelectMod("Fabric API", Matches)
  209. If SelectedFile IsNot Nothing Then
  210. SelectedFabricApi = SelectedFile.Value.Version
  211. ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "FabricApi"))
  212. End If
  213. End If
  214. If Task.IsCanceled Then Return
  215. 'OptiFabric
  216. If SelectedFabric IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then
  217. Dim OptiFabricList = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, MinecraftVersion)).ToList()
  218. Dim HasMd5 = OptiFabricList.Any(Function(v) If(v.Hash, "").Length = 32) 'CurseForge 的 OptiFabric 信息可能提供 SHA1 或 MD5
  219. Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As ResourceVersion))
  220. For Each Entry In Mods.Value
  221. Dim Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Entry.Sha1)
  222. If Match Is Nothing AndAlso HasMd5 Then
  223. Dim Md5 = CryptographyUtils.ComputeFileHash(Entry.File.FullName, CryptographyUtils.HashMethod.Md5)
  224. Match = OptiFabricList.FirstOrDefault(Function(v) v.Hash = Md5)
  225. End If
  226. If Match Is Nothing Then Continue For
  227. Matches.Add((Entry.File, Entry.Sha1, Match))
  228. Next
  229. Dim SelectedFile = SelectMod("OptiFabric", Matches)
  230. If SelectedFile IsNot Nothing Then
  231. SelectedOptiFabric = SelectedFile.Value.Version
  232. ManagedFiles.Add(New McInstallManifest.ManagedFile("mods/" & SelectedFile.Value.File.Name.Lower(), SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFabric"))
  233. End If
  234. End If
  235. If Task.IsCanceled Then Return
  236. 'OptiFine
  237. Dim OptiFineFiles As New List(Of (File As FileInfo, Sha1 As String, OriginalName As String))
  238. If SelectedOptiFine Is Nothing AndAlso (SelectedForge IsNot Nothing OrElse SelectedLiteLoader IsNot Nothing OrElse SelectedFabric IsNot Nothing) Then 'OptiFine 只在有 Mod 加载器时才看在不在 mods 文件夹中
  239. Static OptiFineHashes As JObject = FileUtils.ReadAsJson("Minecraft\OptiFineHashes.json", type:=GetType(Resource)) 'JSON 内容:{"SHA1": "原始文件名", ...}
  240. OptiFineFiles = Mods.Value.
  241. Select(Function(Entry) (Entry.File, Entry.Sha1, OriginalName:=OptiFineHashes(Entry.Sha1)?.ToString())). '用本地文件的 SHA1 匹配下载时的原始文件名,再根据原始文件名取得下载源信息
  242. Where(Function(Entry) Entry.OriginalName IsNot Nothing).ToList()
  243. Dim Matches As New List(Of (File As FileInfo, Sha1 As String, Version As DlOptiFineListEntry))
  244. For Each Entry In OptiFineFiles '遍历 hash 匹配到了的本地文件
  245. Dim Match = DlOptiFineListLoader.Output.Value.FirstOrDefault(
  246. Function(v) v.Inherit = MinecraftVersion AndAlso v.FileName = Entry.OriginalName AndAlso (SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
  247. If Match Is Nothing Then
  248. Hint($"无法从 OptiFine 列表还原 {Entry.File.Name} 对应的 {Entry.OriginalName},将跳过该文件。")
  249. Continue For
  250. End If
  251. Matches.Add((Entry.File, Entry.Sha1, Match))
  252. Next
  253. Dim SelectedFile = SelectMod("OptiFine", Matches)
  254. If SelectedFile IsNot Nothing Then
  255. SelectedOptiFine = SelectedFile.Value.Version
  256. Dim RelativePath = PathUtils.RemoveExtendedPrefix(SelectedFile.Value.File.FullName).Substring(Task.Input.PathIndie.Length).Replace("\", "/").Lower()
  257. ManagedFiles.Add(New McInstallManifest.ManagedFile(RelativePath, SelectedFile.Value.Sha1, SelectedFile.Value.File.LastWriteTimeUtc, "OptiFine"))
  258. End If
  259. End If
  260. If Task.IsCanceled Then Return
  261. #End Region
  262. '保存 Manifest
  263. If IsVanillaVersionReliable Then
  264. Components = New McComponents(MinecraftVersion, Components.ModLoaderVersions,
  265. If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
  266. If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
  267. If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)))
  268. If Manifest Is Nothing OrElse Not Components.IsEquivalentTo(Manifest.Components) OrElse Not ManagedFiles.SequenceEqual(Manifest.ManagedFiles) Then
  269. Configs.McInstallManifest.Set(New McInstallManifest(Versions.McInstallManifestVersion, Components, Date.UtcNow, ManagedFiles), Task.Input.Config)
  270. Task.Input.Config.Save()
  271. Logger.Info($"已为 {Task.Input.Name} 更新安装清单")
  272. End If
  273. End If
  274. If Task.IsCanceled Then Return
  275. ForgeVersionLoader = ForgeLoader
  276. End Sub
  277. Private Function SelectMod(Of T)(ComponentName As String, Matches As List(Of (File As FileInfo, Sha1 As String, Version As T))) As (File As FileInfo, Sha1 As String, Version As T)?
  278. If Matches.Count = 0 Then Return Nothing
  279. If Matches.Count = 1 Then Return Matches(0)
  280. Dim Choice As Integer? = Nothing
  281. RunInUiWait(Sub() Choice = MyMsgBoxSelect(Matches.Select(Function(Entry) CType(New MyRadioBox With {.Text = Entry.File.Name}, IMyRadio)), $"选择当前使用的 {ComponentName}"))
  282. If Not Choice.HasValue Then Throw New Exception($"发现了多个 {ComponentName}")
  283. Return Matches(Choice.Value)
  284. End Function
  285. ''' <summary>
  286. ''' 初始化修改页
  287. ''' </summary>
  288. Private Sub LoadInstanceFinished(Task As LoaderTask(Of McInstance, Object))
  289. '还原原版信息
  290. OriginalName = Task.Input.Name
  291. CustomLogo = Nothing
  292. If ReadIni(Task.Input.PathVersion & "PCL\Setup.ini", "LogoCustom", False) Then
  293. CustomLogo = If(Task.Input.Logo.EndsWithF("PCL\Logo.png"), Task.Input.PathVersion & "PCL\Logo.png", Task.Input.Logo)
  294. End If
  295. Dim Item = McDownloadListItem(VanillaData, Nothing, False).Init()
  296. VanillaName = Item.Title
  297. VanillaIcon = Item.Logo
  298. LabMinecraftVersion.Text = VanillaName
  299. TextSelectName.Text = OriginalName
  300. IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
  301. TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {
  302. New ValidateFolderName(McFolderSelected & "versions", IgnoreList:=New List(Of String) From {OriginalName})}
  303. '修改页 UI
  304. CardMinecraft.Visibility = If(RestrictMcDownload, Visibility.Collapsed, Visibility.Visible)
  305. BtnBack.Visibility = Visibility.Collapsed
  306. ImgLogo.Margin = New Thickness(15, 15, 0, 0)
  307. TextSelectName.Margin = New Thickness(56, 16, 16, 0)
  308. LabSelectName.Text = $"原名 :   {OriginalName}"
  309. UpdateOriginalNameVisibility()
  310. BtnStart.Text = "开始修改"
  311. BtnStart.Logo = FrmInstanceLeft.ItemModify.Logo
  312. AutoSelectedFabricApi = SelectedFabric IsNot Nothing
  313. AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
  314. If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
  315. PrepareSelectPage()
  316. ReloadComponentLists()
  317. '进入动画
  318. PanMinecraft.Visibility = Visibility.Collapsed
  319. PanMinecraft.Opacity = 0
  320. PanMinecraft.RenderTransform = New TranslateTransform(-50, 0)
  321. PanSelect.Opacity = 1
  322. PanSelect.RenderTransform = New TranslateTransform
  323. PanBack.IsHitTestVisible = True
  324. PanAllBack.IsHitTestVisible = True
  325. InitialTarget = GetSelectedComponents()
  326. '设置原始数据
  327. BtnMinecraftRestore.Tag = VanillaData
  328. BtnOptiFineRestore.Tag = SelectedOptiFine
  329. BtnForgeRestore.Tag = SelectedForge
  330. BtnNeoForgeRestore.Tag = SelectedNeoForge
  331. BtnFabricRestore.Tag = SelectedFabric
  332. BtnFabricApiRestore.Tag = SelectedFabricApi
  333. BtnOptiFabricRestore.Tag = SelectedOptiFabric
  334. BtnLiteLoaderRestore.Tag = SelectedLiteLoader
  335. ImgMinecraftOld.Source = VanillaIcon
  336. LabMinecraftOld.Text = LabMinecraftVersion.Text
  337. LabOptiFineOld.Text = If(SelectedOptiFine Is Nothing, "无", LabOptiFine.Text)
  338. LabForgeOld.Text = If(SelectedForge Is Nothing, "无", LabForge.Text)
  339. LabNeoForgeOld.Text = If(SelectedNeoForge Is Nothing, "无", LabNeoForge.Text)
  340. LabFabricOld.Text = If(SelectedFabric Is Nothing, "无", LabFabric.Text)
  341. LabLiteLoaderOld.Text = If(SelectedLiteLoader Is Nothing, "无", LabLiteLoader.Text)
  342. LabFabricApiOld.Text = If(SelectedFabricApi Is Nothing, "无", LabFabricApi.Text)
  343. LabOptiFabricOld.Text = If(SelectedOptiFabric Is Nothing, "无", LabOptiFabric.Text)
  344. RefreshComparisons()
  345. End Sub
  346. Public Overrides Function GetSelectLogo() As String
  347. Return If(CustomLogo, MyBase.GetSelectLogo())
  348. End Function
  349. Public Overrides Sub ShowMinecraftVersionList()
  350. If IsInSelectPage Then
  351. '把当前已选择的组件记录到 PendingComponents
  352. Dim Components = GetSelectedComponents()
  353. For Each Component In {
  354. (Loader:=LoadOptiFine, Version:=Components.OptiFineVersion),
  355. (Loader:=LoadFabricApi, Version:=Components.FabricApiVersion),
  356. (Loader:=LoadOptiFabric, Version:=Components.OptiFabricVersion),
  357. (Loader:=LoadForge, Version:=SelectedForge?.VersionName),
  358. (Loader:=LoadNeoForge, Version:=SelectedNeoForge?.VersionName),
  359. (Loader:=LoadFabric, Version:=SelectedFabric),
  360. (Loader:=LoadLiteLoader, Version:=SelectedLiteLoader?.JsonToken?("version").ToString())}
  361. '保留尚未加载完成的组件选择
  362. If Component.Version IsNot Nothing Then PendingComponents(Component.Loader) = Component.Version
  363. Next
  364. End If
  365. MyBase.ShowMinecraftVersionList()
  366. End Sub
  367. Public Overrides Sub ReloadSelected()
  368. If IsRestoringComponents Then Return
  369. If IsInSelectPage AndAlso VanillaName IsNot Nothing AndAlso PendingComponents.Any() Then
  370. IsRestoringComponents = True
  371. Try
  372. Dim HasProcessed As Boolean = False
  373. '优先恢复 Mod 加载器,再匹配兼容的附加组件
  374. For Each LoadingComponent In {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader, LoadOptiFine, LoadFabricApi, LoadOptiFabric}
  375. Dim Version As String = Nothing
  376. If Not PendingComponents.TryGetValue(LoadingComponent, Version) Then Continue For
  377. If Not LoadingComponent.State.IsLoader Then Continue For
  378. Dim Loader = DirectCast(LoadingComponent.State, LoaderBase)
  379. '检查前置组件
  380. If LoadingComponent Is LoadOptiFine Then
  381. If {LoadFabric, LoadForge, LoadNeoForge, LoadLiteLoader}.Any(Function(Component) PendingComponents.ContainsKey(Component)) Then Continue For
  382. ElseIf LoadingComponent Is LoadFabricApi Then
  383. If PendingComponents.ContainsKey(LoadFabric) Then Continue For
  384. ElseIf LoadingComponent Is LoadForge Then
  385. If Not McVersion.IsStandardFormat(VanillaName) Then
  386. PendingComponents.Remove(LoadingComponent)
  387. Continue For
  388. End If
  389. If DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Input <> VanillaName Then Continue For
  390. If Loader.State = LoadState.Failed AndAlso Loader.Error.Message = "无" Then
  391. PendingComponents.Remove(LoadingComponent)
  392. Continue For
  393. End If
  394. ElseIf LoadingComponent Is LoadOptiFabric Then
  395. If PendingComponents.ContainsKey(LoadFabric) OrElse PendingComponents.ContainsKey(LoadOptiFine) Then Continue For
  396. End If
  397. If Loader.State <> LoadState.Finished Then Continue For
  398. '移除已处理的组件
  399. PendingComponents.Remove(LoadingComponent)
  400. HasProcessed = True
  401. '匹配目标 MC 版本下兼容的组件
  402. If LoadingComponent Is LoadFabric Then
  403. If SelectedFabric Is Nothing AndAlso LoadFabricGetError() Is Nothing Then
  404. Dim Versions = DlFabricListLoader.Output.Value("loader").OfType(Of JObject)
  405. SelectedFabric = If(Versions.FirstOrDefault(Function(v) v("version").ToString = Version), Versions.FirstOrDefault())?("version").ToString()
  406. End If
  407. ElseIf LoadingComponent Is LoadOptiFine Then
  408. If SelectedOptiFine Is Nothing AndAlso LoadOptiFineGetError() Is Nothing Then
  409. Dim Versions = DlOptiFineListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName AndAlso
  410. (SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(v, SelectedForge)))
  411. SelectedOptiFine = If(Versions.FirstOrDefault(Function(v) v.InstanceName.AfterLast("-OptiFine_") = Version),
  412. Versions.OrderBy(Function(v) v.IsPreview).
  413. ThenByDescending(Function(v) v.DisplayName, Comparer(Of String).Create(AddressOf McUtils.CompareVersion)).FirstOrDefault())
  414. End If
  415. ElseIf LoadingComponent Is LoadForge Then
  416. If SelectedForge Is Nothing AndAlso LoadForgeGetError() Is Nothing Then
  417. Dim Versions = DirectCast(Loader, LoaderTask(Of String, List(Of DlForgeVersionEntry))).Output.Where(
  418. Function(v) v.Inherit = VanillaName AndAlso
  419. v.Category <> "universal" AndAlso v.Category <> "client" AndAlso
  420. (SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, v)))
  421. SelectedForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.OrderByDescending(Function(v) v).FirstOrDefault())
  422. End If
  423. ElseIf LoadingComponent Is LoadNeoForge Then
  424. If SelectedNeoForge Is Nothing AndAlso LoadNeoForgeGetError() Is Nothing Then
  425. Dim Versions = DlNeoForgeListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
  426. SelectedNeoForge = If(Versions.FirstOrDefault(Function(v) v.VersionName = Version), Versions.FirstOrDefault())
  427. End If
  428. ElseIf LoadingComponent Is LoadLiteLoader Then
  429. If SelectedLiteLoader Is Nothing AndAlso LoadLiteLoaderGetError() Is Nothing Then
  430. Dim Versions = DlLiteLoaderListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName)
  431. SelectedLiteLoader = If(Versions.FirstOrDefault(Function(v) v.JsonToken("version").ToString = Version), Versions.FirstOrDefault())
  432. End If
  433. ElseIf LoadingComponent Is LoadFabricApi Then
  434. If SelectedFabricApi Is Nothing AndAlso LoadFabricApiGetError() Is Nothing Then
  435. Dim Versions = DlFabricApiLoader.Output.Where(Function(v) IsFabricApiCompatible(v, VanillaName))
  436. SelectedFabricApi = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
  437. Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
  438. End If
  439. If SelectedFabricApi IsNot Nothing Then AutoSelectedFabricApi = True
  440. ElseIf LoadingComponent Is LoadOptiFabric Then
  441. If SelectedOptiFabric Is Nothing AndAlso LoadOptiFabricGetError() Is Nothing Then
  442. Dim Versions = DlOptiFabricLoader.Output.Where(Function(v) IsOptiFabricCompatible(v, VanillaName))
  443. SelectedOptiFabric = If(Versions.FirstOrDefault(Function(v) v.Version = Version OrElse v.Id = Version OrElse v.Display = Version),
  444. Versions.OrderByDescending(Function(v) v.ReleaseDate).FirstOrDefault())
  445. End If
  446. If SelectedOptiFabric IsNot Nothing Then AutoSelectedOptiFabric = True
  447. End If
  448. Next
  449. '更新组件列表
  450. If HasProcessed Then ReloadComponentLists()
  451. Finally
  452. IsRestoringComponents = False
  453. End Try
  454. End If
  455. '刷新当前选择与原始版本的对比
  456. MyBase.ReloadSelected()
  457. If InitialTarget IsNot Nothing Then RefreshComparisons()
  458. End Sub
  459. Private Sub RefreshComparisons()
  460. Dim Current = GetSelectedComponents()
  461. ImgMinecraftNew.Source = VanillaIcon
  462. 'Minecraft
  463. Dim IsMinecraftUnchanged = InitialTarget.MinecraftVersion = Current.MinecraftVersion
  464. ShowComparison(PanMinecraftCompare, PanMinecraftInfo, InitialTarget.MinecraftVersion, Current.MinecraftVersion,
  465. LabMinecraftVersion.Text, BtnMinecraftRestore, True)
  466. 'OptiFine
  467. Dim OriginalOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
  468. ShowComparison(PanOptiFineCompare, PanOptiFineInfo, InitialTarget.OptiFineVersion, Current.OptiFineVersion,
  469. LabOptiFine.Text, BtnOptiFineRestore, InitialTarget.OptiFineEntry Is Nothing OrElse
  470. IsMinecraftUnchanged AndAlso CardOptiFine.MainSwap.Visibility = Visibility.Visible AndAlso OriginalOptiFine IsNot Nothing AndAlso
  471. (SelectedForge Is Nothing OrElse IsOptiFineSuitForForge(OriginalOptiFine, SelectedForge)), BtnOptiFineClear)
  472. 'Fabric API
  473. ShowComparison(PanFabricApiCompare, PanFabricApiInfo, InitialTarget.FabricApiVersion, Current.FabricApiVersion,
  474. LabFabricApi.Text, BtnFabricApiRestore, InitialTarget.FabricApiVersion Is Nothing OrElse
  475. IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso CardFabricApi.MainSwap.Visibility = Visibility.Visible, BtnFabricApiClear)
  476. 'OptiFabric
  477. ShowComparison(PanOptiFabricCompare, PanOptiFabricInfo, InitialTarget.OptiFabricVersion, Current.OptiFabricVersion,
  478. LabOptiFabric.Text, BtnOptiFabricRestore, InitialTarget.OptiFabricVersion Is Nothing OrElse
  479. IsMinecraftUnchanged AndAlso SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso CardOptiFabric.MainSwap.Visibility = Visibility.Visible, BtnOptiFabricClear)
  480. 'Mod Loader
  481. For Each Loader In {
  482. (Kind:=ModLoaders.Forge, Card:=CardForge, ComparePanel:=PanForgeCompare, CurrentPanel:=PanForgeInfo, Label:=LabForge, RestoreButton:=BtnForgeRestore, ClearButton:=BtnForgeClear),
  483. (Kind:=ModLoaders.NeoForge, Card:=CardNeoForge, ComparePanel:=PanNeoForgeCompare, CurrentPanel:=PanNeoForgeInfo, Label:=LabNeoForge, RestoreButton:=BtnNeoForgeRestore, ClearButton:=BtnNeoForgeClear),
  484. (Kind:=ModLoaders.Fabric, Card:=CardFabric, ComparePanel:=PanFabricCompare, CurrentPanel:=PanFabricInfo, Label:=LabFabric, RestoreButton:=BtnFabricRestore, ClearButton:=BtnFabricClear),
  485. (Kind:=ModLoaders.LiteLoader, Card:=CardLiteLoader, ComparePanel:=PanLiteLoaderCompare, CurrentPanel:=PanLiteLoaderInfo, Label:=LabLiteLoader, RestoreButton:=BtnLiteLoaderRestore, ClearButton:=BtnLiteLoaderClear)}
  486. Dim OldVersion As String = Nothing, NewVersion As String = Nothing
  487. InitialTarget.ModLoaderVersions.TryGetValue(Loader.Kind, OldVersion)
  488. Current.ModLoaderVersions.TryGetValue(Loader.Kind, NewVersion)
  489. Dim CanRestore = OldVersion Is Nothing OrElse IsMinecraftUnchanged AndAlso
  490. Loader.Card.MainSwap.Visibility = Visibility.Visible AndAlso Loader.RestoreButton.Tag IsNot Nothing
  491. If Loader.Kind = ModLoaders.Forge AndAlso OldVersion IsNot Nothing AndAlso CanRestore Then
  492. CanRestore = SelectedOptiFine Is Nothing OrElse IsOptiFineSuitForForge(SelectedOptiFine, DirectCast(Loader.RestoreButton.Tag, DlForgeVersionEntry))
  493. End If
  494. If Loader.Kind = ModLoaders.Fabric AndAlso OldVersion IsNot Nothing Then
  495. Dim TargetVersion = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3")
  496. CanRestore = CardFabric.Visibility = Visibility.Visible AndAlso CardFabric.MainSwap.Visibility = Visibility.Visible AndAlso
  497. DlFabricListLoader.Output.Value("loader").OfType(Of JObject).Any(Function(v) v("version").ToString = OldVersion) AndAlso
  498. DlFabricListLoader.Output.Value("game").OfType(Of JObject).Any(Function(v) v("version").ToString = TargetVersion)
  499. End If
  500. ShowComparison(Loader.ComparePanel, Loader.CurrentPanel, OldVersion, NewVersion, Loader.Label.Text,
  501. Loader.RestoreButton, CanRestore, Loader.ClearButton)
  502. Next
  503. UpdateStartButton()
  504. End Sub
  505. Private Sub ShowComparison(ComparePanel As Grid, CurrentPanel As FrameworkElement, OldVersion As String, NewVersion As String, NewText As String,
  506. RestoreButton As MyIconButton, CanRestore As Boolean, Optional ClearButton As FrameworkElement = Nothing)
  507. '获取不可安装的原因
  508. Dim UnavailableReason As String = Nothing
  509. If NewVersion Is Nothing Then
  510. If RestoreButton Is BtnOptiFineRestore Then
  511. UnavailableReason = LoadOptiFineGetError()
  512. ElseIf RestoreButton Is BtnForgeRestore Then
  513. UnavailableReason = If(McVersion.IsStandardFormat(VanillaName), LoadForgeGetError(), "无")
  514. ElseIf RestoreButton Is BtnNeoForgeRestore Then
  515. UnavailableReason = If(CardNeoForge.Visibility = Visibility.Collapsed, "无", LoadNeoForgeGetError())
  516. ElseIf RestoreButton Is BtnFabricRestore Then
  517. UnavailableReason = If(CardFabric.Visibility = Visibility.Collapsed, "无", LoadFabricGetError())
  518. ElseIf RestoreButton Is BtnLiteLoaderRestore Then
  519. UnavailableReason = If(CardLiteLoader.Visibility = Visibility.Collapsed, "无", LoadLiteLoaderGetError())
  520. ElseIf RestoreButton Is BtnFabricApiRestore Then
  521. UnavailableReason = LoadFabricApiGetError()
  522. ElseIf RestoreButton Is BtnOptiFabricRestore Then
  523. UnavailableReason = LoadOptiFabricGetError()
  524. End If
  525. NewText = If(UnavailableReason, "可以添加")
  526. End If
  527. '修改对比
  528. Dim IsPending = NewVersion Is Nothing AndAlso (NewText = "获取中……" OrElse NewText.StartsWithF("获取失败:"))
  529. Dim IsChanged = OldVersion <> NewVersion AndAlso Not IsPending
  530. CanRestore = IsChanged AndAlso OldVersion IsNot Nothing AndAlso CanRestore
  531. ComparePanel.Visibility = If(IsChanged, Visibility.Visible, Visibility.Collapsed)
  532. CurrentPanel.Visibility = If(IsChanged, Visibility.Collapsed, Visibility.Visible)
  533. '还原与取消安装按钮
  534. RestoreButton.Visibility = If(CanRestore, Visibility.Visible, Visibility.Collapsed)
  535. If ClearButton IsNot Nothing Then ClearButton.ToolTip = If(OldVersion Is Nothing, "取消安装", "移除")
  536. Dim HasClearButton = ClearButton IsNot Nothing AndAlso ClearButton.Visibility = Visibility.Visible
  537. RestoreButton.Margin = New Thickness(0, 5, If(CanRestore AndAlso HasClearButton, 60, 32), 0)
  538. ComparePanel.Margin = New Thickness(ComparePanel.Margin.Left, ComparePanel.Margin.Top,
  539. If(CanRestore AndAlso HasClearButton, 110, 80), ComparePanel.Margin.Bottom)
  540. If Not IsChanged Then Return
  541. '显示修改前已安装组件的对比卡片
  542. If OldVersion IsNot Nothing Then
  543. Dim Card = CType(ComparePanel.Parent, MyCard)
  544. If Card.Visibility = Visibility.Collapsed Then
  545. Card.IsSwapped = True
  546. If Card.MainSwap IsNot Nothing Then Card.MainSwap.Visibility = Visibility.Collapsed
  547. Card.Visibility = Visibility.Visible
  548. End If
  549. End If
  550. Dim OldSide = CType(ComparePanel.Children(0), DockPanel)
  551. Dim NewSide = CType(ComparePanel.Children(2), DockPanel)
  552. OldSide.Children(0).Visibility = If(OldVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
  553. CType(OldSide.Children(1), TextBlock).Margin = If(OldVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
  554. NewSide.Children(0).Visibility = If(NewVersion Is Nothing, Visibility.Collapsed, Visibility.Visible)
  555. Dim NewLabel = CType(NewSide.Children(1), TextBlock)
  556. NewLabel.Margin = If(NewVersion Is Nothing, New Thickness(0), New Thickness(7, 0, 0, 0))
  557. NewLabel.Text = If(NewVersion Is Nothing, "移除", NewText)
  558. If NewVersion Is Nothing AndAlso UnavailableReason IsNot Nothing Then
  559. NewLabel.Inlines.Add(New Run($"({If(UnavailableReason = "无", "无可用版本", UnavailableReason)})") With {.Foreground = ColorGray4})
  560. End If
  561. End Sub
  562. Private Sub RestoreComponent(sender As Object, e As EventArgs) Handles BtnMinecraftRestore.Click, BtnOptiFineRestore.Click, BtnForgeRestore.Click,
  563. BtnNeoForgeRestore.Click, BtnFabricRestore.Click, BtnFabricApiRestore.Click, BtnOptiFabricRestore.Click, BtnLiteLoaderRestore.Click
  564. Dim Button = DirectCast(sender, MyIconButton)
  565. '恢复初始选择
  566. If Button Is BtnMinecraftRestore Then
  567. IsRestoringComponents = True
  568. Try
  569. PendingComponents.Clear()
  570. MinecraftSelected(McDownloadListItem(DirectCast(Button.Tag, JObject), Nothing, False).Init(), Nothing)
  571. If ForgeVersionLoader IsNot Nothing Then LoadForge.State = ForgeVersionLoader
  572. '恢复初始的组件
  573. SelectedOptiFine = TryCast(BtnOptiFineRestore.Tag, DlOptiFineListEntry)
  574. SelectedForge = TryCast(BtnForgeRestore.Tag, DlForgeVersionEntry)
  575. SelectedNeoForge = TryCast(BtnNeoForgeRestore.Tag, DlNeoForgeListEntry)
  576. SelectedFabric = TryCast(BtnFabricRestore.Tag, String)
  577. SelectedLiteLoader = TryCast(BtnLiteLoaderRestore.Tag, DlLiteLoaderListEntry)
  578. SelectedFabricApi = TryCast(BtnFabricApiRestore.Tag, ResourceVersion)
  579. SelectedOptiFabric = TryCast(BtnOptiFabricRestore.Tag, ResourceVersion)
  580. AutoSelectedFabricApi = SelectedFabric IsNot Nothing
  581. AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
  582. Finally
  583. IsRestoringComponents = False
  584. End Try
  585. ReloadComponentLists()
  586. Return
  587. ElseIf Button Is BtnOptiFineRestore Then
  588. SelectedOptiFine = TryCast(Button.Tag, DlOptiFineListEntry)
  589. AutoSelectedOptiFabric = SelectedOptiFine IsNot Nothing AndAlso SelectedFabric IsNot Nothing
  590. If SelectedOptiFine Is Nothing Then SelectedOptiFabric = Nothing
  591. ElseIf Button Is BtnForgeRestore Then
  592. SelectedForge = TryCast(Button.Tag, DlForgeVersionEntry)
  593. ElseIf Button Is BtnNeoForgeRestore Then
  594. SelectedNeoForge = TryCast(Button.Tag, DlNeoForgeListEntry)
  595. ElseIf Button Is BtnFabricRestore Then
  596. SelectedFabric = TryCast(Button.Tag, String)
  597. AutoSelectedFabricApi = SelectedFabric IsNot Nothing
  598. AutoSelectedOptiFabric = SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing
  599. If SelectedFabric Is Nothing Then
  600. SelectedFabricApi = Nothing
  601. SelectedOptiFabric = Nothing
  602. End If
  603. ElseIf Button Is BtnFabricApiRestore Then
  604. SelectedFabricApi = TryCast(Button.Tag, ResourceVersion)
  605. AutoSelectedFabricApi = True
  606. ElseIf Button Is BtnOptiFabricRestore Then
  607. SelectedOptiFabric = TryCast(Button.Tag, ResourceVersion)
  608. AutoSelectedOptiFabric = True
  609. ElseIf Button Is BtnLiteLoaderRestore Then
  610. SelectedLiteLoader = TryCast(Button.Tag, DlLiteLoaderListEntry)
  611. End If
  612. DirectCast(Button.Parent, MyCard).IsSwapped = True
  613. '刷新组件
  614. ReloadComponentLists()
  615. End Sub
  616. Private Sub CardMinecraft_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles CardMinecraft.MouseLeftButtonUp
  617. ShowMinecraftVersionList()
  618. e.Handled = True
  619. End Sub
  620. Private Sub UpdateStartButton() Handles TextSelectName.TextChanged, TextSelectName.ValidateChanged
  621. BtnStart.IsEnabled = TextSelectName.IsValidated AndAlso InitialTarget IsNot Nothing AndAlso
  622. Not PendingComponents.Any() AndAlso
  623. (Not IsVanillaVersionReliable OrElse Not InitialTarget.IsEquivalentTo(GetSelectedComponents()) OrElse
  624. Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal))
  625. End Sub
  626. Private Sub UpdateOriginalNameVisibility() Handles TextSelectName.TextChanged
  627. Dim IsNameChanged = Not String.Equals(TextSelectName.Text, OriginalName, StringComparison.Ordinal)
  628. Dim NewVisibility = If(IsNameChanged, Visibility.Visible, Visibility.Collapsed)
  629. If LabSelectName.Visibility = NewVisibility Then Return
  630. LabSelectName.Visibility = NewVisibility
  631. BtnOriginalNameRestore.Visibility = NewVisibility
  632. Dim RightMargin = If(IsNameChanged, 40, 16)
  633. If IsInSelectPage AndAlso AniControlEnabled = 0 Then
  634. AniStart(AaDouble(Sub(Delta) TextSelectName.Margin = New Thickness(56, 16, TextSelectName.Margin.Right + Delta, 0),
  635. RightMargin - TextSelectName.Margin.Right, 150,, New AniEaseOutFluent), "DownloadModify Name Margin")
  636. Else
  637. AniStop("DownloadModify Name Margin")
  638. TextSelectName.Margin = New Thickness(56, 16, RightMargin, 0)
  639. End If
  640. End Sub
  641. Private Sub BtnOriginalNameRestore_Click(sender As Object, e As EventArgs) Handles BtnOriginalNameRestore.Click
  642. TextSelectName.Text = OriginalName
  643. IsSelectNameEdited = Not String.Equals(OriginalName, GetSelectName(), StringComparison.Ordinal)
  644. End Sub
  645. Public Overrides Sub StartInstall(Request As McInstallRequest)
  646. UpdateStartButton()
  647. If Not BtnStart.IsEnabled Then Return
  648. Dim TargetInstance = InitLoader.Input
  649. Dim Target = Request.Components
  650. Dim InstanceName = Request.NewInstanceName
  651. Dim IsNameChanged = Not String.Equals(InstanceName, TargetInstance.Name, StringComparison.Ordinal)
  652. Request.NewInstanceName = If(IsNameChanged, InstanceName, Nothing)
  653. Request.InstanceFolder = TargetInstance.PathVersion
  654. '警告
  655. Dim Warnings As New List(Of String)
  656. If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
  657. Not PathUtils.IsSame(TargetInstance.PathIndie, TargetInstance.PathVersion) Then
  658. Warnings.Add("该版本尚未开启版本隔离,可能与别的 MC 版本共用同一个 Mod 和存档文件夹。" & vbCrLf &
  659. "修改版本后,已有的 Mod、存档等需要手动迁移到新的游戏目录。")
  660. End If
  661. Dim IsMinecraftVersionChanged = InitialTarget.MinecraftVersion <> Target.MinecraftVersion
  662. If Not IsVanillaVersionReliable AndAlso Not IsMinecraftVersionChanged Then
  663. Warnings.Add($"PCL 未能精准识别该版本,请确认它是不是 Minecraft {InitialTarget.MinecraftVersion},如果不正确,请取消并选择正确的版本!")
  664. End If
  665. If Not Configs.HintModifyInstanceWarning.Get() Then
  666. Warnings.Add("如果该版本安装了 PCL 不兼容的加载器,或经过第三方修改,这些改动将会丢失!")
  667. End If
  668. Dim IsModLoaderChanged As Boolean = False
  669. For Each Loader In InitialTarget.ModLoaderVersions.Keys.Union(Target.ModLoaderVersions.Keys)
  670. Dim CurrentVersion As String = Nothing, TargetVersion As String = Nothing
  671. Dim HasCurrent = InitialTarget.ModLoaderVersions.TryGetValue(Loader, CurrentVersion)
  672. Dim HasTarget = Target.ModLoaderVersions.TryGetValue(Loader, TargetVersion)
  673. If HasCurrent <> HasTarget OrElse CurrentVersion <> TargetVersion Then
  674. IsModLoaderChanged = True
  675. Exit For
  676. End If
  677. Next
  678. If (IsModLoaderChanged OrElse IsMinecraftVersionChanged) AndAlso InitialTarget.ModLoaderVersions.Any Then
  679. Warnings.Add("现有的 Mod、资源包等在修改后可能会不兼容!")
  680. End If
  681. If Warnings.Any() Then
  682. If MyMsgBox("请注意:" & vbCrLf & "• " & Warnings.Join(vbCrLf & "• "), "修改确认", "开始修改", "取消", IsWarn:=True) <> 1 Then Return
  683. End If
  684. If Not Configs.HintModifyInstanceWarning.Get() Then Configs.HintModifyInstanceWarning.Set(True)
  685. '开始修改
  686. If IsVanillaVersionReliable AndAlso InitialTarget.IsEquivalentTo(Target) Then
  687. '仅重命名
  688. Try
  689. Dim OldPath = TargetInstance.PathVersion
  690. Dim RenamedInstance = TargetInstance.Rename(InstanceName)
  691. If PageInstanceLeft.Instance IsNot Nothing AndAlso PathUtils.IsSame(PageInstanceLeft.Instance.PathVersion, OldPath) Then PageInstanceLeft.Instance = RenamedInstance
  692. If McInstanceSelected IsNot Nothing AndAlso PathUtils.IsSame(McInstanceSelected.PathVersion, OldPath) Then McInstanceSelected = RenamedInstance
  693. PageInstanceLeft.ReloadCurrentJava()
  694. Hint("重命名成功!", HintType.Green)
  695. LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
  696. FrmMain.PageBack()
  697. Catch ex As Exception
  698. Logger.Error(ex, "重命名版本失败", LogBehavior.Alert)
  699. End Try
  700. Else
  701. '实际修改
  702. If McModificationStart(Request, TargetInstance) Then FrmMain.PageBack()
  703. End If
  704. End Sub
  705. End Class