LTCat 2 месяцев назад
Родитель
Сommit
0ce35fe7e5

+ 1 - 1
MeloongCore

@@ -1 +1 @@
-Subproject commit 8bfb4f709f78a528a40e0bd48e6f77bef0e07e24
+Subproject commit 811438aa3c7c0437c7e60e85b516cc593dcf6cb1

+ 1 - 1
PCLCS/Constants.cs

@@ -68,7 +68,7 @@ public static class Versions {
     /// <summary>
     /// 版本管理中 Mod 信息缓存的版本号。
     /// </summary>
-    public const int LocalModCacheVersion = 18;
+    public const int LocalModCacheVersion = 19;
     /// <summary>
     /// Minecraft 本地实例信息缓存的版本号。
     /// </summary>

+ 1 - 1
PCLCS/Java.cs

@@ -204,7 +204,7 @@ public static class JavaUtils {
                         static bool HasReparsePoint(FileSystemInfo info) {
                             if (PathUtils.IsParentOf(Paths.AppData + @".minecraft\runtime\", info.FullName)) return false; // 不筛查 PCL 的下载路径(#8928)
                             do {
-                                if (info.Attributes.HasFlag(FileAttributes.ReparsePoint)) return true;
+                                if (info.Attributes.HasFlagF(FileAttributes.ReparsePoint)) return true;
                                 info = info is FileInfo fileInfo ? fileInfo.Directory : ((DirectoryInfo) info).Parent;
                             } while (info is not null);
                             return false;

+ 14 - 4
Plain Craft Launcher 2/FormMain.xaml.vb

@@ -11,11 +11,11 @@ Public Class FormMain
         Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
         '统计更新日志条目
         If BuildType = BuildTypes.Release Then
-            If LastVersion < 408 Then 'Release 2.13.1.0
+            If LastVersion < 408 Then 'Release 2.13.1.1
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
-                FeatureCount += 21
-                BugCount += 22
+                FeatureCount += 23
+                BugCount += 28
             End If
             If LastVersion < 406 Then 'Release 2.13.0.1
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:重做 Java 管理与相关设置,允许调整 Java 优先级、指定 Java 版本范围等"))
@@ -93,6 +93,11 @@ Public Class FormMain
             '3:BUG+ IMP* FEAT-
             '2:BUG* IMP-
             '1:BUG-
+            If LastVersion < 410 Then 'Snapshot 2.13.1.1
+                If LastVersion = 409 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:更新 Mod 时可能更新成另一个 Mod 加载器的版本"))
+                FeatureCount += 2
+                BugCount += 6
+            End If
             If LastVersion < 409 Then 'Snapshot 2.13.1.0
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
@@ -781,7 +786,7 @@ Public Class FormMain
         If PanMsg.Children.Any Then
             If e.Key = Key.Enter Then
                 CType(PanMsg.Children(0), Object).Btn1_Click()
-            ElseIf e.Key = Key.Escape Then
+            ElseIf e.Key = Key.Escape OrElse (e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) Then
                 Dim Msg As Object = PanMsg.Children(0)
                 If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
                     Msg.Btn3_Click()
@@ -823,6 +828,11 @@ Public Class FormMain
             If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh()
             Return
         End If
+        'Ctrl + w 关闭程序
+        If e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control) Then
+            EndProgram(True)
+            Return
+        End If
         '调用启动游戏
         If e.Key = Key.Enter AndAlso PageCurrent = FormMain.PageType.Launch Then
             If IsAprilEnabled AndAlso Not IsAprilGiveup Then

+ 2 - 2
Plain Craft Launcher 2/Modules/Base/ModBase.vb

@@ -6,12 +6,12 @@ Public Module ModBase
 #Region "声明"
 
     '下列版本信息由更新器自动修改
-    Public Const VersionBaseName As String = "2.13.1.0" '显示用版本名
+    Public Const VersionBaseName As String = "2.13.1.1" '显示用版本名
     Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
 #If RELEASE Then
     Public Const VersionCode As Integer = 408 '正式版
 #Else
-    Public Const VersionCode As Integer = 409 '快照版
+    Public Const VersionCode As Integer = 410 '快照版
 #End If
 
     '版本信息

+ 8 - 84
Plain Craft Launcher 2/Modules/Base/ModNet.vb

@@ -217,8 +217,7 @@ Retry:
             Optional Content As Object = Nothing, Optional Headers As Dictionary(Of String, String) = Nothing,
             Optional SimulateBrowserHeaders As Boolean = False, Optional Timeout As Integer = 25000, Optional Encoding As Encoding = Nothing) As Byte()
         If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
-        Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing,
-            Response As HttpResponseMessage = Nothing, HostIp As String = Nothing
+        Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing, Response As HttpResponseMessage = Nothing
         Try
             '构建 RequestClient
             Directory.CreateDirectory(PathTemp & "Cache\Http\")
@@ -255,8 +254,6 @@ Retry:
             Retrier.Attempt(delay:=Function() TimeSpan.FromMilliseconds(200), isRetryAllowed:=Function(ex) TypeOf ex Is FileNotFoundException OrElse TypeOf ex Is IOException, action:=
             Sub(Attempt)
                 CancelToken?.Dispose() : CancelToken = New CancellationTokenSource(Timeout)
-                HostIp = DNSLookup(Request, CancelToken.Token)
-                If HostIp IsNot Nothing Then IPReliability.GetOrAdd(HostIp, -0.01) '预先降低一点,这样快速的重复请求会使用不同的 IP 以提高成功率
                 Dim ClonedRequest As HttpRequestMessage = Request.Clone() 'HttpRequestMessage 只能发送一次,重试时需要克隆一个新的
                 Try
                     Response = RequestClient.SendAsync(ClonedRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
@@ -274,26 +271,22 @@ Retry:
                 End Using
             End Using
             If Not Response.IsSuccessStatusCode Then
-                RecordIPReliability(HostIp, -0.7)
                 Dim ResponseMessage = ResponseBytes.GetString(Encoding)
                 Throw New HttpRequestCodeException(
-                    $"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url},{HostIp}" &
+                    $"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url}" &
                     If(String.IsNullOrEmpty(ResponseMessage), "", vbCrLf & ResponseMessage), Response.StatusCode, ResponseMessage)
             End If
-            '输出
-            RecordIPReliability(HostIp, 0.5)
             Return ResponseBytes
         Catch ex As HttpRequestException
             Throw
         Catch ex As TimeoutException
-            Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
+            Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
         Catch ex As Exception
             If TypeOf ex IsNot TaskCanceledException Then ex.ThrowIfCanceled() 'GetResultWithTimeout 会抛出 TaskCanceledException
-            RecordIPReliability(HostIp, -1)
             If ex.IsBadNetwork Then
-                Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
+                Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
             Else
-                Throw New Exception($"网络请求出现意外异常({Method}, {Url},{HostIp})", ex)
+                Throw New Exception($"网络请求出现意外异常({Method}, {Url})", ex)
             End If
         Finally
             Request?.Dispose()
@@ -304,72 +297,6 @@ Retry:
     Private RequestClient As HttpClient = Nothing
     Private RequestClientLock As New Object
 
-    ''' <summary>
-    ''' 进行 DNS 解析。它仅在选择的 IP 与系统默认的不一致时才对 URL 中的 Host 进行替换。
-    ''' 返回请求时应使用的 IP。若解析失败,则返回 Nothing。
-    ''' </summary>
-    Private Function DNSLookup(Request As HttpRequestMessage, CancelToken As CancellationToken) As String
-        Dim Host = Request.RequestUri.Host
-        '不对部分有严格 SNI 限制的域名进行 DNS 解析(#8295)
-        If {"mojang.com", "minecraft.net", "minecraftservices.com"}.Any(Function(h) Host.ContainsIgnoreCase(h)) Then Return Nothing
-        '若一分钟内已失败过,则不再重复尝试解析,以减少断网时的 Exception 数量
-        Dim LastFailureTime As Date
-        If DNSFailureRecord.TryGetValue(Host, LastFailureTime) Then
-            If Date.Now - LastFailureTime < New TimeSpan(0, 1, 0) Then
-                Return Nothing
-            Else
-                DNSFailureRecord.Remove(Host)
-            End If
-        End If
-        '初步 DNS 解析
-        Dim Candidates As IPAddress()
-        Try
-            Dim AsyncResult = Dns.BeginGetHostAddresses(Host, Nothing, Nothing)
-            If WaitHandle.WaitAny(New WaitHandle() {AsyncResult.AsyncWaitHandle, CancelToken.WaitHandle}) Then Throw New TimeoutException("DNS 解析超时")
-            Candidates = Dns.EndGetHostAddresses(AsyncResult).Distinct.ToArray
-        Catch ex As Exception
-            Logger.Warn(ex, $"DNS 解析失败({Host})")
-            DNSFailureRecord(Host) = Date.Now
-            Return Nothing
-        End Try
-        If Not Candidates.Any Then
-            Logger.Info($"DNS 解析无结果({Host})")
-            Return Nothing
-        End If
-        '若同时存在 IPv4 和 IPv6 地址,仅选择其中一类(因为 GFW 可能只屏蔽了 IPv4 或 IPv6)
-        Dim Reliabilities = Candidates.ToDictionary(Function(i) i, Function(i) IPReliability.GetOrDefault(i.ToString, 0))
-        Dim IPv4Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetwork).ToArray
-        Dim IPv6Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetworkV6).ToArray
-        If IPv4Targets.Any AndAlso IPv6Targets.Any Then
-            Dim IPv4Reliability = IPv4Targets.Max(Function(i) Reliabilities(i))
-            Dim IPv6Reliability = IPv6Targets.Max(Function(i) Reliabilities(i))
-            If Host = "api.modrinth.com" Then IPv6Reliability -= 0.1 '让 Modrinth 优先使用 IPv4 地址(#6887)
-            Logger.Trace(Function() $"DNS IPv4/IPv6 选择({Host}),IPv4 {IPv4Reliability:0.000},IPv6 {IPv6Reliability:0.000}")
-            Candidates = If(IPv4Reliability >= IPv6Reliability, IPv4Targets, IPv6Targets)
-        End If
-        '选择可靠度最高的 IP
-        Dim Target As IPAddress = Candidates.MaxBy(Function(i) Reliabilities(i))
-        Request.Headers.Host = Request.RequestUri.Authority
-        Request.RequestUri = (New UriBuilder(Request.RequestUri) With {.Host = If(Target.AddressFamily = AddressFamily.InterNetworkV6, $"[{Target}]", Target.ToString)}).Uri
-        Return Target.ToString
-    End Function
-    Private DNSFailureRecord As New ConcurrentDictionary(Of String, Date)
-    ''' <summary>
-    ''' 记录每个 IP 地址的请求可靠度。
-    ''' 通常取值 -1 ~ +0.5,越高越好。未尝试过的 IP 应视为 0。
-    ''' </summary>
-    Private IPReliability As New ConcurrentDictionary(Of String, Double)
-    ''' <summary>
-    ''' 根据请求结果,记录 IP 地址的可靠度。
-    ''' </summary>
-    Private Sub RecordIPReliability(IP As String, Result As Double)
-        If IP Is Nothing Then Return
-        IPReliability.AddOrUpdate(IP,
-            Function() Result * 0.5,
-            Function(k, v) v * 0.5 + Result * 0.5
-        )
-    End Sub
-
 #End Region
 
 #Region "多线程下载引擎"
@@ -976,7 +903,7 @@ StartThread:
             Logger.Log($"{LocalName}:开始,起始点 {Th.DownloadStart},{Th.Source.Url}", If(Th.DownloadStart = 0, LogLevel.Info, LogLevel.Trace))
             Dim ResultStream As Stream = Nothing, HttpRequest As HttpRequestMessage = Nothing,
                 Response As HttpResponseMessage = Nothing, ResponseStream As Stream = Nothing,
-                CancelToken As CancellationTokenSource = Nothing, HostIp As String = Nothing
+                CancelToken As CancellationTokenSource = Nothing
             '部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
             '延迟过长会导致某些特别慢的链接迟迟不被掐死
             Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 15000) * (1 + Th.Source.FailCount), 30000)
@@ -987,7 +914,6 @@ StartThread:
                 HttpRequest = New HttpRequestMessage(HttpMethod.Get, Th.Source.Url)
                 SecretHeadersSign(Th.Source.Url, HttpRequest, SimulateBrowserHeaders)
                 CancelToken = New CancellationTokenSource(Timeout)
-                HostIp = DNSLookup(HttpRequest, CancelToken.Token) 'DNS 预解析
                 If Not Th.IsFirstThread Then HttpRequest.Headers.Range = New Headers.RangeHeaderValue(Th.DownloadStart, Nothing)
                 Dim ContentLength As Long = 0
                 Response = ThreadClient.SendAsync(HttpRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
@@ -1119,15 +1045,13 @@ SourceBreak:
                     '本线程完成
                     Th.State = NetState.Finished
                     Logger.Trace(Function() $"{LocalName}:完成,已下载 {Th.DownloadDone}({Th.DownloadStart}~{Th.DownloadEnd})")
-                    RecordIPReliability(HostIp, 0.5)
                 End If
             Catch ex As Exception
                 If ex.IsBadNetwork OrElse TypeOf ex Is TaskCanceledException Then
-                    Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message},IP:{If(String.IsNullOrEmpty(HostIp), "自动", "")}"))
+                    Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message}"))
                 Else
-                    Logger.Info(ex, $"{LocalName}:出错,IP:{HostIp}")
+                    Logger.Info(ex, $"{LocalName}:出错")
                 End If
-                RecordIPReliability(HostIp, -0.7)
                 SourceFail(Th, ex, False)
             Finally
                 '释放资源

+ 9 - 4
Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb

@@ -1379,11 +1379,16 @@ OnLoaded:
                         If Instance.State <> McInstanceState.Error AndAlso
                            ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown") <> "Unknown" Then
                             '读取版本信息
+                            Dim ReadCachedInfo =
+                            Function(Key As String) As String
+                                Dim Value = ReadIni(Instance.PathVersion & "PCL\Setup.ini", Key, Nothing)
+                                Return If(Value = "", Nothing, Value) '对老版本 Setup.ini 的空值兼容
+                            End Function
                             Dim VersionInfo As New McVersion With {
-                                .Fabric = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionFabric", Nothing),
-                                .Forge = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionForge", Nothing),
-                                .NeoForge = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionNeoForge", Nothing),
-                                .OptiFine = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionOptiFine", Nothing),
+                                .Fabric = ReadCachedInfo("VersionFabric"),
+                                .Forge = ReadCachedInfo("VersionForge"),
+                                .NeoForge = ReadCachedInfo("VersionNeoForge"),
+                                .OptiFine = ReadCachedInfo("VersionOptiFine"),
                                 .HasLiteLoader = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionLiteLoader", False),
                                 .VanillaName = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown")
                             }

+ 2 - 2
Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb

@@ -436,8 +436,8 @@ Public Module ModModpack
             Urls = ResourceVersion.ParseModrinthTrackArguments(Urls,
                 ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
             Urls = Urls.Distinct.ToList()
-            Dim TargetPath As String = $"{McFolderSelected}versions\{InstanceName}\{File("path")}"
-            If Not Path.GetFullPath(TargetPath).StartsWithF(Path.GetFullPath($"{McFolderSelected}versions\{InstanceName}\")) Then
+            Dim TargetPath As String = Path.Combine(McFolderSelected, "versions", InstanceName, File("path").ToString().TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
+            If Not PathUtils.IsParentOf($"{McFolderSelected}versions\{InstanceName}\", TargetPath) Then
                 MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True)
                 Throw New OperationCanceledException
             End If

+ 43 - 31
Plain Craft Launcher 2/Modules/Resource/LocalResourceLoaders.vb

@@ -120,12 +120,12 @@ Public Module LocalResourceLoaders
         Next
         Logger.Info($"有 {Mods.Where(Function(m) m.Project Is Nothing).Count} 个 Mod 需要联网获取信息,{Mods.Where(Function(m) m.Project IsNot Nothing).Count} 个 Mod 需要更新信息")
         If Not Mods.Any Then Return
-        '获取作为检查目标的加载器和版本
+        '获取作为检查目标的版本
         '此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
         Dim TargetMcVersion As McVersion = Loader.Input.Target
         Dim VanillaVersion = TargetMcVersion.VanillaName
         '开始网络获取
-        Logger.Info($"目标加载器:{ModLoaders},版本:{VanillaVersion}")
+        Logger.Info($"正在检查 Mod 信息的版本:{VanillaVersion},加载器 {ModLoaders}")
         Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
         Dim CurrentTaskThread As Thread = Thread.CurrentThread
         '从 Modrinth 获取信息
@@ -140,6 +140,7 @@ Public Module LocalResourceLoaders
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 If ModrinthVersion.Count = 0 Then Return
                 Dim ModrinthMapping As New Dictionary(Of String, List(Of LocalResourceFile))
+                Dim ModrinthLoaders As New Dictionary(Of LocalResourceFile, List(Of String))
                 For Each Entry In Mods
                     If Not ModrinthVersion.ContainsKey(Entry.ModrinthHash) Then Continue For
                     If ModrinthVersion(Entry.ModrinthHash)("files")(0)("hashes")("sha1") <> Entry.ModrinthHash Then Continue For
@@ -149,6 +150,8 @@ Public Module LocalResourceLoaders
                     ModrinthMapping(ProjectId).Add(Entry)
                     '记录对应的 ProjectVersion
                     Dim File = ResourceVersion.FromPlatformJson(ModrinthVersion(Entry.ModrinthHash), ResourceTypes.Mod)
+                    ModrinthLoaders(Entry) = ModrinthVersion(Entry.ModrinthHash)("loaders").
+                        Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
                     If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then
                         Entry.ProjectVersion = File
                     Else
@@ -169,17 +172,20 @@ Public Module LocalResourceLoaders
                 Next
                 Logger.Info($"已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
                 '步骤 4:获取更新信息
-                If ModLoaders = ModLoaders.None Then
-                    Logger.Warn("该 Minecraft 版本没有可用的 Mod 加载器,不获取 Mod 更新信息")
-                    Return
-                End If
-                Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update", HttpMethod.Post,
-                    $"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"", 
-                    ""loaders"": [""{ModLoaders.Flags.Join(""",""").Lower}""],""game_versions"": [""{VanillaVersion}""]}}", "application/json")
+                Dim ModsToUpdate = ModrinthMapping.SelectMany(Function(l) l.Value).ToList
+                If Not ModsToUpdate.Any Then Return
+                Dim UpdateData As String = ModsToUpdate.
+                    Select(Function(m) $"{{""hash"": ""{m.ModrinthHash}"", ""loaders"": [""{ModrinthLoaders(m).Join(""",""")}""], ""game_versions"": [""{VanillaVersion}""]}}").
+                    Join(",")
+                Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update_individual", HttpMethod.Post,
+                    $"{{""algorithm"": ""sha1"", ""hashes"": [{UpdateData}]}}", "application/json")
                 For Each Entry In Mods
                     If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.ProjectVersion Is Nothing Then Continue For
                     Dim UpdateFile = ResourceVersion.FromPlatformJson(ModrinthUpdate(Entry.ModrinthHash), ResourceTypes.Mod)
                     If Not UpdateFile.DownloadAvailable Then Continue For
+                    Dim UpdateLoaders = ModrinthUpdate(Entry.ModrinthHash)("loaders").
+                        Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
+                    If Not UpdateLoaders.SequenceEqual(ModrinthLoaders(Entry)) Then Continue For
                     Logger.Trace(Function() $"本地文件 {Entry.ProjectVersion.FileName} 在 Modrinth 上的最新版为 {UpdateFile.FileName}")
                     If Entry.ProjectVersion.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.ProjectVersion.Hash = UpdateFile.Hash Then Continue For
                     '设置更新日志与更新文件
@@ -217,6 +223,7 @@ Public Module LocalResourceLoaders
 
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 Dim ProjectIdToLocalFiles As New Dictionary(Of Integer, List(Of LocalResourceFile))
+                Dim CurseForgeLoaders As New Dictionary(Of LocalResourceFile, ModLoaders)
                 For Each Project In CurseForgeRaw
                     Dim ProjectId = Project("id").ToString
                     Dim Hash As UInteger = Project("file")("fileFingerprint")
@@ -226,6 +233,7 @@ Public Module LocalResourceLoaders
                         ProjectIdToLocalFiles.AddIntoValueCollection(ProjectId, Entry)
                         '记录对应的 ProjectVersion
                         Dim File = ResourceVersion.FromPlatformJson(Project("file"), ResourceTypes.Mod)
+                        CurseForgeLoaders(Entry) = File.ModLoaders
                         If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then Entry.ProjectVersion = File
                     Next
                 Next
@@ -242,34 +250,34 @@ Public Module LocalResourceLoaders
                     If ProjectJson("isAvailable") IsNot Nothing AndAlso Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
                     '设置 Entry 中的工程信息
                     Dim Project As New ResourceProject(ProjectJson)
-                    Dim TargetLoaders As ModLoaders = ModLoaders.None
                     For Each LocalFile In ProjectIdToLocalFiles(Project.Id) '倒查防止 CurseForge 返回的内容有漏
                         If LocalFile.Project?.Platform = ResourcePlatforms.Modrinth Then
                             LocalFile.Project = LocalFile.Project '再次触发修改事件
                         Else
                             LocalFile.Project = Project
-                            TargetLoaders = TargetLoaders Or LocalFile.Project.ModLoaders
                         End If
-                    Next
-                    TargetLoaders = TargetLoaders And ModLoaders '与目标 MC 版本的加载器取交集
-                    '查找或许版本更新的文件列表
-                    If ModLoaders = ModLoaders.None Then Continue For '无法判断 MC 版本使用的加载器,跳过更新检查
-                    Dim LatestFiles = ProjectJson("latestFilesIndexes").Where(
-                    Function(Entry)
-                        'Mod Loader 匹配
-                        If Entry("modLoader") Is Nothing Then Return False
-                        Dim EntryLoader As ModLoaders = Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer))
-                        If Not TargetLoaders.HasFlag(EntryLoader) Then Return False
-                        'MC 版本匹配
-                        Dim EntryVersion As String = Entry("gameVersion")
-                        Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
-                    End Function).
-                    MaxByAll(Function(Entry) Entry("gameVersion").ToString, New VersionComparer) '只保留最新的 MC 版本
-                    For Each Entry In LatestFiles
-                        Dim FileId = Entry("fileId").ToObject(Of Integer)
-                        If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
-                        UpdateFileIds(FileId).AddRange(ProjectIdToLocalFiles(Project.Id))
-                        FileIdToProjectSlug(FileId) = Project.Slug
+                        '查找可能有版本更新的文件列表
+                        Dim LatestFiles = ProjectJson("latestFilesIndexes").Where(
+                        Function(Entry)
+                            'Mod Loader 匹配
+                            Dim EntryLoader As ModLoaders = If(Entry("modLoader") Is Nothing, ModLoaders.None,
+                                Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer)))
+                            If CurseForgeLoaders(LocalFile) = ModLoaders.None Then
+                                If EntryLoader <> ModLoaders.None Then Return False
+                            ElseIf Not CurseForgeLoaders(LocalFile).HasFlagF(EntryLoader) Then
+                                Return False
+                            End If
+                            'MC 版本匹配
+                            Dim EntryVersion As String = Entry("gameVersion")
+                            Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
+                        End Function).
+                        MaxByAll(Function(Entry) Entry("gameVersion").ToString, New VersionComparer) '只保留最新的 MC 版本
+                        For Each Entry In LatestFiles
+                            Dim FileId = Entry("fileId").ToObject(Of Integer)
+                            If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
+                            UpdateFileIds(FileId).Add(LocalFile)
+                            FileIdToProjectSlug(FileId) = Project.Slug
+                        Next
                     Next
                 Next
                 Logger.Info($"已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
@@ -283,6 +291,10 @@ Public Module LocalResourceLoaders
                     Dim File = ResourceVersion.FromPlatformJson(FileJson, ResourceTypes.Mod)
                     If Not File.DownloadAvailable Then Continue For
                     For Each Entry As LocalResourceFile In UpdateFileIds(File.Id)
+                        If File.ModLoaders <> CurseForgeLoaders(Entry) Then
+                            Logger.Warn($"从 CurseForge 获取到的更新文件 {File.FileName} 加载器为 {File.ModLoaders},与本地文件 {Entry.File.Name} 的加载器 {CurseForgeLoaders(Entry)} 不一致,已忽略", LogBehavior.None)
+                            Continue For
+                        End If
                         If UpdateFiles.ContainsKey(Entry) AndAlso UpdateFiles(Entry).ReleaseDate >= File.ReleaseDate Then Continue For
                         UpdateFiles(Entry) = File
                     Next

+ 2 - 2
Plain Craft Launcher 2/Modules/Resource/ResourceProject.vb

@@ -87,7 +87,7 @@ Public Class ResourceProject
         Get
             If Not LoadedDatabase Then
                 LoadedDatabase = True
-                If Types.HasFlag(ResourceTypes.Mod) OrElse Types.HasFlag(ResourceTypes.DataPack) Then
+                If Types.HasFlagF(ResourceTypes.Mod) OrElse Types.HasFlagF(ResourceTypes.DataPack) Then
                     _WikiEntry = WikiEntry.All.Value.FirstOrDefault(Function(c) c.Slugs.GetOrDefault(Platform) = Slug)
                 End If
             End If
@@ -415,7 +415,7 @@ Public Class ResourceProject
         If Not MissingProjectIds.Any() Then Return
         '联网获取剩余的工程对象
         Dim Projects As JArray
-        If Platform.HasFlag(ResourcePlatforms.CurseForge) Then
+        If Platform.HasFlagF(ResourcePlatforms.CurseForge) Then
             Projects = DlModRequest("https://api.curseforge.com/v1/mods",
                 HttpMethod.Post, "{""modIds"": [" & MissingProjectIds.Join(","c) & "]}", "application/json")("data")
         Else

+ 10 - 10
Plain Craft Launcher 2/Modules/Resource/ResourceSearcher.vb

@@ -21,8 +21,8 @@ Public Class ResourceSearcher
         ''' </summary>
         Public ReadOnly Property CanContinue As Boolean
             Get
-                If Tag.StartsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
-                If Tag.EndsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
+                If Tag.StartsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
+                If Tag.EndsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
                 If Storage.CurseForgeTotal = -1 OrElse Storage.ModrinthTotal = -1 Then Return True
                 Return Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
             End Get
@@ -241,7 +241,7 @@ Public Class ResourceSearcher
                 Next
             End Function
             'CurseForge
-            If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) Then
+            If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) Then
                 '数据库搜索
                 Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.CurseForge).ToList()
                 Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
@@ -258,7 +258,7 @@ Public Class ResourceSearcher
                 End If
             End If
             'Modrinth
-            If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) Then
+            If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) Then
                 '数据库搜索
                 Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.Modrinth).ToList()
                 Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
@@ -303,7 +303,7 @@ NextPage:
         Try
 
             'CurseForge 搜索
-            If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) AndAlso
+            If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) AndAlso
                Not (Storage.CurseForgeTotal > -1 AndAlso Storage.CurseForgeTotal <= Storage.CurseForgeOffset) AndAlso '剩余的未显示的搜索结果不足
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(CurseForgeAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                 WorkThreads.Add(RunInNewThread(
@@ -317,7 +317,7 @@ NextPage:
                         For Each JsonEntry As JObject In RequestResult("data")
                             Dim Project As New ResourceProject(JsonEntry)
                             If Request.Type = ResourceTypes.ResourcePack AndAlso Project.Tags.Contains("数据包") Then Continue For 'CurseForge 将一些数据包分类成了资源包
-                            If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
+                            If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
                             ProjectList.Add(Project)
                         Next
                         '更新结果
@@ -335,7 +335,7 @@ NextPage:
             End If
 
             'Modrinth 搜索
-            If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
+            If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
                Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(ModrinthAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                 WorkThreads.Add(RunInNewThread(
@@ -363,7 +363,7 @@ NextPage:
             End If
 
             'Modrinth 直接获取工程
-            If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
+            If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
                Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
                ModrinthSlugs.Any Then '有直接获取的 Slug
                 WorkThreads.Add(RunInNewThread(
@@ -375,7 +375,7 @@ NextPage:
                         For Each JsonEntry As JObject In DlModRequest(ModrinthUrl)
                             Dim Project As New ResourceProject(JsonEntry)
                             '应用筛选
-                            If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(Request.Type) Then Continue For
+                            If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For
                             If Not String.IsNullOrEmpty(Request.Tag) AndAlso
                                 Not JsonEntry("categories").Any(Function(c) c.ToString = Request.Tag.AfterLast("/")) Then Continue For 'Project.Tags 已经转换成中文了,只能从 json 判
                             If Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
@@ -405,7 +405,7 @@ NextPage:
 
             '仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
             If IgnoreModLoaderFilter Then
-                RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlag(ModLoaders.Forge))
+                RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlagF(ModLoaders.Forge))
             End If
 
             '确保存在结果

+ 1 - 1
Plain Craft Launcher 2/Modules/Resource/ResourceVersion.vb

@@ -185,7 +185,7 @@ Public Class ResourceVersion
                 For Each Loader As ModLoaders In EnumUtils.GetAllFlags(Of ModLoaders)()
                     If RawLoaders.Contains(Loader.ToString.Lower) Then .ModLoaders = .ModLoaders Or Loader
                 Next
-                If .ResourceType.HasFlag(ResourceTypes.Mod) OrElse .ResourceType.HasFlag(ResourceTypes.DataPack) Then
+                If .ResourceType.HasFlagF(ResourceTypes.Mod) OrElse .ResourceType.HasFlagF(ResourceTypes.DataPack) Then
                     If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then .ResourceType = ResourceTypes.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
                     If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
                     If .ModLoaders.Flags().Any() Then .ResourceType = ResourceTypes.Mod

+ 2 - 2
Plain Craft Launcher 2/My Project/AssemblyInfo.vb

@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
 ' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
 ' 方法是按如下所示使用“*”
 
-<Assembly: AssemblyVersion("2.13.1.0")>
-<Assembly: AssemblyFileVersion("2.13.1.0")>
+<Assembly: AssemblyVersion("2.13.1.1")>
+<Assembly: AssemblyFileVersion("2.13.1.1")>
 <Assembly: NeutralResourcesLanguage("")>

+ 10 - 4
Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb

@@ -11,6 +11,7 @@ Public Module ModDownloadLib
         Dim VersionFolder As String = McFolderSelected & "versions\" & InstanceName & "\"
 
         Dim Loaders As New List(Of LoaderBase)
+        Dim NewInstance As McInstance = Nothing
 
         '下载版本 Json 文件
         If JsonUrl Is Nothing Then
@@ -23,14 +24,19 @@ Public Module ModDownloadLib
         Loaders.Add(New LoaderDownload(McDownloadClientJsonName, New List(Of NetFile) From {
             New NetFile(DlSourceLauncherOrMetaGet(If(JsonUrl, "")), VersionFolder & InstanceName & ".json", New FileChecker With {.IsJson = True})
         }) With {.ProgressWeight = 3})
+        '读取 Json 文件并创建实例
+        Loaders.Add(New LoaderTask(Of String, String)("读取 json 文件",
+            Sub(Task As LoaderTask(Of String, String))
+                NewInstance = New McInstance(VersionFolder)
+                NewInstance.Load()
+            End Sub) With {.ProgressWeight = 0, .Show = False})
 
         '下载支持库文件
         Dim LoadersLib As New List(Of LoaderBase)
         LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
-            Thread.Sleep(50) '等待 JSON 文件实际写入硬盘(#3710)
             Logger.Info($"开始分析原版支持库文件:{VersionFolder}")
-            Task.Output = McLibNetFilesFromInstance(New McInstance(VersionFolder))
+            Task.Output = McLibNetFilesFromInstance(NewInstance)
         End Sub) With {.ProgressWeight = 1, .Show = False})
         LoadersLib.Add(New LoaderDownload("下载原版支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 13, .Show = False})
         Loaders.Add(New LoaderCombo(Of String)(McDownloadClientLibName, LoadersLib) With {.Block = False, .ProgressWeight = 14})
@@ -40,7 +46,7 @@ Public Module ModDownloadLib
         LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址(副加载器)",
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
             Try
-                Dim AssetIndex = DlClientAssetIndexGet(New McInstance(VersionFolder))
+                Dim AssetIndex = DlClientAssetIndexGet(NewInstance)
                 Task.Output = If(AssetIndex Is Nothing, New List(Of NetFile), New List(Of NetFile) From {AssetIndex})
             Catch ex As Exception
                 Throw New Exception("分析资源文件索引地址失败", ex)
@@ -56,7 +62,7 @@ Public Module ModDownloadLib
         End Sub) With {.ProgressWeight = 1, .Show = False})
         LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
         LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析所需资源文件(副加载器)",
-            Sub(Task) Task.Output = McAssetsFixList(New McInstance(VersionFolder), True, Task)) With {.ProgressWeight = 0.01, .Show = False})
+            Sub(Task) Task.Output = McAssetsFixList(NewInstance, True, Task)) With {.ProgressWeight = 0.01, .Show = False})
         LoadersAssets.Add(New LoaderDownload("下载资源文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 14, .Show = False})
         Loaders.Add(New LoaderCombo(Of String)("下载原版资源文件", LoadersAssets) With {.Block = False, .ProgressWeight = 18})
 

+ 1 - 1
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceOverall.xaml.vb

@@ -307,7 +307,7 @@ Public Class PageInstanceOverall
             Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
             Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected
             Dim SavesFolder = DirectoryUtils.GetInfo(PageInstanceLeft.Instance.PathIndie & "saves\")
-            Dim SaveEntries = If(SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
+            Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
             Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageInstanceLeft.Instance.Name} 吗?" &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!" &
                             If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""), ""),

+ 59 - 0
Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml

@@ -134,8 +134,10 @@
                     <WrapPanel Margin="0,0,0,10">
                        <TextBlock Margin="0,1.5,6,0" Text="L" />
                         <TextBlock Margin="0,1.5,6,0" Text="Z" />
                         <TextBlock Margin="0,1.5,6,0" Text="N" />
+                        <TextBlock Margin="0,1.5,6,0" Text="P." />
                         <TextBlock Margin="0,1.5,6,0" Text="...:" />
                         <TextBlock Margin="0,1.5,6,0" Text="妖" />
+                        <TextBlock Margin="0,1.5,6,0" Text="FL" />
                         <TextBlock Margin="0,1.5,6,0" Text="萧" />
                         <TextBlock Margin="0,1.5,6,0" Text="乔" />
                         <TextBlock Margin="0,1.5,6,0" Text="cc" />
@@ -150,6 +152,7 @@
                         <TextBlock Margin="0,1.5,6,0" Text="33" />
                         <TextBlock Margin="0,1.5,6,0" Text="kk" />
                         <TextBlock Margin="0,1.5,6,0" Text="jjb" />
+                        <TextBlock Margin="0,1.5,6,0" Text="狮." />
                         <TextBlock Margin="0,1.5,6,0" Text="+1" />
                         <TextBlock Margin="0,1.5,6,0" Text="Iirs" />
                         <TextBlock Margin="0,1.5,6,0" Text="Lio" />
@@ -158,16 +161,22 @@
                         <TextBlock Margin="0,1.5,6,0" Text="Sht" />
                         <TextBlock Margin="0,1.5,6,0" Text="Nul" />
                         <TextBlock Margin="0,1.5,6,0" Text="250" />
+                        <TextBlock Margin="0,1.5,6,0" Text="Life" />
                         <TextBlock Margin="0,1.5,6,0" Text="kile" />
                         <TextBlock Margin="0,1.5,6,0" Text="pbc" />
                         <TextBlock Margin="0,1.5,6,0" Text="Aya" />
                         <TextBlock Margin="0,1.5,6,0" Text="mjp" />
+                        <TextBlock Margin="0,1.5,6,0" Text="azzt" />
+                        <TextBlock Margin="0,1.5,6,0" Text="小小" />
                         <TextBlock Margin="0,1.5,6,0" Text="冒子" />
                         <TextBlock Margin="0,1.5,6,0" Text="王道" />
                         <TextBlock Margin="0,1.5,6,0" Text="鸡人" />
                         <TextBlock Margin="0,1.5,6,0" Text="喵根" />
+                        <TextBlock Margin="0,1.5,6,0" Text="折花" />
                         <TextBlock Margin="0,1.5,6,0" Text="归墟" />
+                        <TextBlock Margin="0,1.5,6,0" Text="墨渊" />
                         <TextBlock Margin="0,1.5,6,0" Text="键来" />
+                        <TextBlock Margin="0,1.5,6,0" Text="睚眦" />
                         <TextBlock Margin="0,1.5,6,0" Text="而后" />
                         <TextBlock Margin="0,1.5,6,0" Text="南安" />
                         <TextBlock Margin="0,1.5,6,0" Text="曰久" />
@@ -213,7 +222,21 @@
                         <TextBlock Margin="0,1.5,6,0" Text="哈哈" />
                         <TextBlock Margin="0,1.5,6,0" Text="小洛" />
                         <TextBlock Margin="0,1.5,6,0" Text="璃辰" />
+                        <TextBlock Margin="0,1.5,6,0" Text="茉莉" />
+                        <TextBlock Margin="0,1.5,6,0" Text="旅人" />
+                        <TextBlock Margin="0,1.5,6,0" Text="航卡" />
+                        <TextBlock Margin="0,1.5,6,0" Text="班总" />
+                        <TextBlock Margin="0,1.5,6,0" Text="小圆" />
+                        <TextBlock Margin="0,1.5,6,0" Text="梨子" />
+                        <TextBlock Margin="0,1.5,6,0" Text="风?" />
+                        <TextBlock Margin="0,1.5,6,0" Text="瓜瓜" />
+                        <TextBlock Margin="0,1.5,6,0" Text="阳光" />
+                        <TextBlock Margin="0,1.5,6,0" Text="菜菜" />
+                        <TextBlock Margin="0,1.5,6,0" Text="副连" />
+                        <TextBlock Margin="0,1.5,6,0" Text="如舟" />
+                        <TextBlock Margin="0,1.5,6,0" Text="bow" />
                         <TextBlock Margin="0,1.5,6,0" Text="Byet" />
+                        <TextBlock Margin="0,1.5,6,0" Text="Kian" />
                         <TextBlock Margin="0,1.5,6,0" Text="ZDD" />
                         <TextBlock Margin="0,1.5,6,0" Text="Um1" />
                         <TextBlock Margin="0,1.5,6,0" Text="2514" />
@@ -224,21 +247,26 @@
                         <TextBlock Margin="0,1.5,6,0" Text="joker" />
                         <TextBlock Margin="0,1.5,6,0" Text="RXZY" />
                         <TextBlock Margin="0,1.5,6,0" Text="白成y" />
+                        <TextBlock Margin="0,1.5,6,0" Text="mww" />
                         <TextBlock Margin="0,1.5,6,0" Text="Rofni" />
                         <TextBlock Margin="0,1.5,6,0" Text="mzqc" />
                         <TextBlock Margin="0,1.5,6,0" Text="jason" />
+                        <TextBlock Margin="0,1.5,6,0" Text="youzi" />
                         <TextBlock Margin="0,1.5,6,0" Text="xxlzta" />
                         <TextBlock Margin="0,1.5,6,0" Text="end_x" />
                         <TextBlock Margin="0,1.5,6,0" Text="QWYJ" />
                         <TextBlock Margin="0,1.5,6,0" Text="qwqw" />
                         <TextBlock Margin="0,1.5,6,0" Text="Angel" />
+                        <TextBlock Margin="0,1.5,6,0" Text="15041" />
                         <TextBlock Margin="0,1.5,6,0" Text="晓空er" />
+                        <TextBlock Margin="0,1.5,6,0" Text="kaoyu" />
                         <TextBlock Margin="0,1.5,6,0" Text="肥富帅" />
                         <TextBlock Margin="0,1.5,6,0" Text="小王子" />
                         <TextBlock Margin="0,1.5,6,0" Text="狐狸涩" />
                         <TextBlock Margin="0,1.5,6,0" Text="大涡流" />
                         <TextBlock Margin="0,1.5,6,0" Text="无名氏" />
                         <TextBlock Margin="0,1.5,6,0" Text="紫风狐" />
+                        <TextBlock Margin="0,1.5,6,0" Text="杨巨焕" />
                         <TextBlock Margin="0,1.5,6,0" Text="范桂圆" />
                         <TextBlock Margin="0,1.5,6,0" Text="芸芸云" />
                         <TextBlock Margin="0,1.5,6,0" Text="快夏波" />
@@ -255,6 +283,7 @@
                         <TextBlock Margin="0,1.5,6,0" Text="乐永昶" />
                         <TextBlock Margin="0,1.5,6,0" Text="lingke" />
                         <TextBlock Margin="0,1.5,6,0" Text="东雪莲" />
+                        <TextBlock Margin="0,1.5,6,0" Text="殷疯子" />
                         <TextBlock Margin="0,1.5,6,0" Text="伊文斯" />
                         <TextBlock Margin="0,1.5,6,0" Text="丁乙恒" />
                         <TextBlock Margin="0,1.5,6,0" Text="朔望月" />
@@ -273,12 +302,22 @@
                         <TextBlock Margin="0,1.5,6,0" Text="浮千傀" />
                         <TextBlock Margin="0,1.5,6,0" Text="恰啦啦" />
                         <TextBlock Margin="0,1.5,6,0" Text="梦痕丿" />
+                        <TextBlock Margin="0,1.5,6,0" Text="三叶铭" />
+                        <TextBlock Margin="0,1.5,6,0" Text="卿姬昉" />
+                        <TextBlock Margin="0,1.5,6,0" Text="疯一回" />
+                        <TextBlock Margin="0,1.5,6,0" Text="李困困" />
+                        <TextBlock Margin="0,1.5,6,0" Text="厄尔萨" />
+                        <TextBlock Margin="0,1.5,6,0" Text="伏拉夫" />
                         <TextBlock Margin="0,1.5,6,0" Text="α-蒎烯" />
+                        <TextBlock Margin="0,1.5,6,0" Text="你的JK" />
+                        <TextBlock Margin="0,1.5,6,0" Text="jiujinh" />
                         <TextBlock Margin="0,1.5,6,0" Text="慕qwq" />
                         <TextBlock Margin="0,1.5,6,0" Text="Twicat" />
                         <TextBlock Margin="0,1.5,6,0" Text="stenac" />
                         <TextBlock Margin="0,1.5,6,0" Text="JOKER" />
                         <TextBlock Margin="0,1.5,6,0" Text="半日谣." />
+                        <TextBlock Margin="0,1.5,6,0" Text="EX星辞" />
+                        <TextBlock Margin="0,1.5,6,0" Text="Tefrish" />
                         <TextBlock Margin="0,1.5,6,0" Text="Triple9" />
                         <TextBlock Margin="0,1.5,6,0" Text="gediao" />
                         <TextBlock Margin="0,1.5,6,0" Text="Zen1th" />
@@ -292,12 +331,18 @@
                         <TextBlock Margin="0,1.5,6,0" Text="Yi_xuan" />
                         <TextBlock Margin="0,1.5,6,0" Text="MC芬达" />
                         <TextBlock Margin="0,1.5,6,0" Text="shallow" />
+                        <TextBlock Margin="0,1.5,6,0" Text="ETL蓝夢" />
                         <TextBlock Margin="0,1.5,6,0" Text="𝕳轩辕氏" />
                         <TextBlock Margin="0,1.5,6,0" Text="谜茶360" />
+                        <TextBlock Margin="0,1.5,6,0" Text="颜沐Jun" />
+                        <TextBlock Margin="0,1.5,6,0" Text="CS2七松" />
                         <TextBlock Margin="0,1.5,6,0" Text="Eventful" />
                         <TextBlock Margin="0,1.5,6,0" Text="Molioos" />
                         <TextBlock Margin="0,1.5,6,0" Text="logicum" />
+                        <TextBlock Margin="0,1.5,6,0" Text="Mr_loba" />
                         <TextBlock Margin="0,1.5,6,0" Text="一叶知秋" />
+                        <TextBlock Margin="0,1.5,6,0" Text="我妻狗卷" />
+                        <TextBlock Margin="0,1.5,6,0" Text="御神凌霜" />
                         <TextBlock Margin="0,1.5,6,0" Text="狼烟居士" />
                         <TextBlock Margin="0,1.5,6,0" Text="千风伶羽" />
                         <TextBlock Margin="0,1.5,6,0" Text="mumiao" />
@@ -332,11 +377,16 @@
                         <TextBlock Margin="0,1.5,6,0" Text="春山如笑" />
                         <TextBlock Margin="0,1.5,6,0" Text="幻天阿绝" />
                         <TextBlock Margin="0,1.5,6,0" Text="忘道蜂枫" />
+                        <TextBlock Margin="0,1.5,6,0" Text="深海无渊" />
                         <TextBlock Margin="0,1.5,6,0" Text="心软的神" />
                         <TextBlock Margin="0,1.5,6,0" Text="砂锅豆腐" />
                         <TextBlock Margin="0,1.5,6,0" Text="好人好逝" />
                         <TextBlock Margin="0,1.5,6,0" Text="登峰破晓" />
                         <TextBlock Margin="0,1.5,6,0" Text="东隅未殁" />
+                        <TextBlock Margin="0,1.5,6,0" Text="星花银影" />
+                        <TextBlock Margin="0,1.5,6,0" Text="往事如昔" />
+                        <TextBlock Margin="0,1.5,6,0" Text="林克之空" />
+                        <TextBlock Margin="0,1.5,6,0" Text="0X00001" />
                         <TextBlock Margin="0,1.5,6,0" Text="MuMAA" />
                         <TextBlock Margin="0,1.5,6,0" Text="溯枫qwq" />
                         <TextBlock Margin="0,1.5,6,0" Text="Tacenda" />
@@ -344,6 +394,7 @@
                         <TextBlock Margin="0,1.5,6,0" Text="👁👄👃" />
                         <TextBlock Margin="0,1.5,6,0" Text="秋慕owo" />
                         <TextBlock Margin="0,1.5,6,0" Text="船长Yuki" />
+                        <TextBlock Margin="0,1.5,6,0" Text="HIMEKO" />
                         <TextBlock Margin="0,1.5,6,0" Text="是小61呀" />
                         <TextBlock Margin="0,1.5,6,0" Text="47号虾球" />
                         <TextBlock Margin="0,1.5,6,0" Text="Alancnel" />
@@ -360,6 +411,7 @@
                         <TextBlock Margin="0,1.5,6,0" Text="MselQAQ" />
                         <TextBlock Margin="0,1.5,6,0" Text="UZQueen" />
                         <TextBlock Margin="0,1.5,6,0" Text="hihospital" />
+                        <TextBlock Margin="0,1.5,6,0" Text="cutegirl3k" />
                         <TextBlock Margin="0,1.5,6,0" Text="Prifo_lone" />
                         <TextBlock Margin="0,1.5,6,0" Text="有....暗星!" />
                         <TextBlock Margin="0,1.5,6,0" Text="Hitsser 1.0" />
@@ -382,11 +434,13 @@
                         <TextBlock Margin="0,1.5,6,0" Text="离人如飘萍" />
                         <TextBlock Margin="0,1.5,6,0" Text="秋葉逐殘夏" />
                         <TextBlock Margin="0,1.5,6,0" Text="水星有海吗" />
+                        <TextBlock Margin="0,1.5,6,0" Text="吃饱爱睡觉" />
                         <TextBlock Margin="0,1.5,6,0" Text="ac纯萌新cy" />
                         <TextBlock Margin="0,1.5,6,0" Text="岁月suiyue" />
                         <TextBlock Margin="0,1.5,6,0" Text="陌然MajorC" />
                         <TextBlock Margin="0,1.5,6,0" Text="-Pareidolia." />
                         <TextBlock Margin="0,1.5,6,0" Text="蛙蛙大王Kzr" />
+                        <TextBlock Margin="0,1.5,6,0" Text="Aurorawalk" />
                         <TextBlock Margin="0,1.5,6,0" Text="Yhcmdbfwz" />
                         <TextBlock Margin="0,1.5,6,0" Text="Ayers赜蠹嬭" />
                         <TextBlock Margin="0,1.5,6,0" Text="We1J1uSh1" />
@@ -405,6 +459,7 @@
                         <TextBlock Margin="0,1.5,6,0" Text="小田love白子" />
                         <TextBlock Margin="0,1.5,6,0" Text="Sasha_Xiong" />
                         <TextBlock Margin="0,1.5,6,0" Text="赤狸_crisnow" />
+                        <TextBlock Margin="0,1.5,6,0" Text="dorod月gour" />
                         <TextBlock Margin="0,1.5,6,0" Text="Name0emaN" />
                         <TextBlock Margin="0,1.5,6,0" Text="happypickles" />
                         <TextBlock Margin="0,1.5,6,0" Text="无中生有为dd" />
@@ -439,13 +494,17 @@
                         <TextBlock Margin="0,1.5,6,0" Text="没什么名字好取" />
                         <TextBlock Margin="0,1.5,6,0" Text="Wanderer5147" />
                         <TextBlock Margin="0,1.5,6,0" Text="取个名字耗阳寿" />
+                        <TextBlock Margin="0,1.5,6,0" Text="北熊何时与狼行" />
+                        <TextBlock Margin="0,1.5,6,0" Text="向着有风的地方" />
                         <TextBlock Margin="0,1.5,6,0" Text="AAA神秘人批发" />
                         <TextBlock Margin="0,1.5,6,0" Text="Bangbang2025" />
                         <TextBlock Margin="0,1.5,6,0" Text="贝吉蒙Begimon" />
                         <TextBlock Margin="0,1.5,6,0" Text="取个名字真难0.0" />
                         <TextBlock Margin="0,1.5,6,0" Text="NoTuring_Steve" />
+                        <TextBlock Margin="0,1.5,6,0" Text="VividerCafe2379" />
                         <TextBlock Margin="0,1.5,6,0" Text="冰冰watermelon" />
                         <TextBlock Margin="0,1.5,6,0" Text="DOODOODOOD" />
+                        <TextBlock Margin="0,1.5,6,0" Text="怕做苦力的苦力怕" />
                         <TextBlock Margin="0,1.5,6,0" Text="小句号就是小句号" />
                         <TextBlock Margin="0,1.5,6,0" Text="弹跳爆炸小鱼珠🐟" />
                         <TextBlock Margin="0,1.5,6,0" Text="Xanadu Silk Fable" />

+ 1 - 1
Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb

@@ -186,7 +186,7 @@ Public Class PageSelectRight
             Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
             Dim IsHintIndie As Boolean = Instance.State <> McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected
             Dim SavesFolder = DirectoryUtils.GetInfo(Instance.PathIndie & "saves\")
-            Dim SaveEntries = If(SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
+            Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
             Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {Instance.Name} 吗?" &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!", "") &
                         If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""),