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>
     /// <summary>
     /// 版本管理中 Mod 信息缓存的版本号。
     /// 版本管理中 Mod 信息缓存的版本号。
     /// </summary>
     /// </summary>
-    public const int LocalModCacheVersion = 18;
+    public const int LocalModCacheVersion = 19;
     /// <summary>
     /// <summary>
     /// Minecraft 本地实例信息缓存的版本号。
     /// Minecraft 本地实例信息缓存的版本号。
     /// </summary>
     /// </summary>

+ 1 - 1
PCLCS/Java.cs

@@ -204,7 +204,7 @@ public static class JavaUtils {
                         static bool HasReparsePoint(FileSystemInfo info) {
                         static bool HasReparsePoint(FileSystemInfo info) {
                             if (PathUtils.IsParentOf(Paths.AppData + @".minecraft\runtime\", info.FullName)) return false; // 不筛查 PCL 的下载路径(#8928)
                             if (PathUtils.IsParentOf(Paths.AppData + @".minecraft\runtime\", info.FullName)) return false; // 不筛查 PCL 的下载路径(#8928)
                             do {
                             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;
                                 info = info is FileInfo fileInfo ? fileInfo.Directory : ((DirectoryInfo) info).Parent;
                             } while (info is not null);
                             } while (info is not null);
                             return false;
                             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))
         Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
         '统计更新日志条目
         '统计更新日志条目
         If BuildType = BuildTypes.Release Then
         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, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
                 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
             End If
             If LastVersion < 406 Then 'Release 2.13.0.1
             If LastVersion < 406 Then 'Release 2.13.0.1
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:重做 Java 管理与相关设置,允许调整 Java 优先级、指定 Java 版本范围等"))
                 FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:重做 Java 管理与相关设置,允许调整 Java 优先级、指定 Java 版本范围等"))
@@ -93,6 +93,11 @@ Public Class FormMain
             '3:BUG+ IMP* FEAT-
             '3:BUG+ IMP* FEAT-
             '2:BUG* IMP-
             '2:BUG* IMP-
             '1:BUG-
             '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
             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, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
                 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 PanMsg.Children.Any Then
             If e.Key = Key.Enter Then
             If e.Key = Key.Enter Then
                 CType(PanMsg.Children(0), Object).Btn1_Click()
                 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)
                 Dim Msg As Object = PanMsg.Children(0)
                 If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
                 If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
                     Msg.Btn3_Click()
                     Msg.Btn3_Click()
@@ -823,6 +828,11 @@ Public Class FormMain
             If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh()
             If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh()
             Return
             Return
         End If
         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 e.Key = Key.Enter AndAlso PageCurrent = FormMain.PageType.Launch Then
             If IsAprilEnabled AndAlso Not IsAprilGiveup 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 "声明"
 #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 自动替换
     Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
 #If RELEASE Then
 #If RELEASE Then
     Public Const VersionCode As Integer = 408 '正式版
     Public Const VersionCode As Integer = 408 '正式版
 #Else
 #Else
-    Public Const VersionCode As Integer = 409 '快照版
+    Public Const VersionCode As Integer = 410 '快照版
 #End If
 #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 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()
             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 线程执行了网络请求")
         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
         Try
             '构建 RequestClient
             '构建 RequestClient
             Directory.CreateDirectory(PathTemp & "Cache\Http\")
             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:=
             Retrier.Attempt(delay:=Function() TimeSpan.FromMilliseconds(200), isRetryAllowed:=Function(ex) TypeOf ex Is FileNotFoundException OrElse TypeOf ex Is IOException, action:=
             Sub(Attempt)
             Sub(Attempt)
                 CancelToken?.Dispose() : CancelToken = New CancellationTokenSource(Timeout)
                 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 只能发送一次,重试时需要克隆一个新的
                 Dim ClonedRequest As HttpRequestMessage = Request.Clone() 'HttpRequestMessage 只能发送一次,重试时需要克隆一个新的
                 Try
                 Try
                     Response = RequestClient.SendAsync(ClonedRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
                     Response = RequestClient.SendAsync(ClonedRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
@@ -274,26 +271,22 @@ Retry:
                 End Using
                 End Using
             End Using
             End Using
             If Not Response.IsSuccessStatusCode Then
             If Not Response.IsSuccessStatusCode Then
-                RecordIPReliability(HostIp, -0.7)
                 Dim ResponseMessage = ResponseBytes.GetString(Encoding)
                 Dim ResponseMessage = ResponseBytes.GetString(Encoding)
                 Throw New HttpRequestCodeException(
                 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)
                     If(String.IsNullOrEmpty(ResponseMessage), "", vbCrLf & ResponseMessage), Response.StatusCode, ResponseMessage)
             End If
             End If
-            '输出
-            RecordIPReliability(HostIp, 0.5)
             Return ResponseBytes
             Return ResponseBytes
         Catch ex As HttpRequestException
         Catch ex As HttpRequestException
             Throw
             Throw
         Catch ex As TimeoutException
         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
         Catch ex As Exception
             If TypeOf ex IsNot TaskCanceledException Then ex.ThrowIfCanceled() 'GetResultWithTimeout 会抛出 TaskCanceledException
             If TypeOf ex IsNot TaskCanceledException Then ex.ThrowIfCanceled() 'GetResultWithTimeout 会抛出 TaskCanceledException
-            RecordIPReliability(HostIp, -1)
             If ex.IsBadNetwork Then
             If ex.IsBadNetwork Then
-                Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
+                Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
             Else
             Else
-                Throw New Exception($"网络请求出现意外异常({Method}, {Url},{HostIp})", ex)
+                Throw New Exception($"网络请求出现意外异常({Method}, {Url})", ex)
             End If
             End If
         Finally
         Finally
             Request?.Dispose()
             Request?.Dispose()
@@ -304,72 +297,6 @@ Retry:
     Private RequestClient As HttpClient = Nothing
     Private RequestClient As HttpClient = Nothing
     Private RequestClientLock As New Object
     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
 #End Region
 
 
 #Region "多线程下载引擎"
 #Region "多线程下载引擎"
@@ -976,7 +903,7 @@ StartThread:
             Logger.Log($"{LocalName}:开始,起始点 {Th.DownloadStart},{Th.Source.Url}", If(Th.DownloadStart = 0, LogLevel.Info, LogLevel.Trace))
             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,
             Dim ResultStream As Stream = Nothing, HttpRequest As HttpRequestMessage = Nothing,
                 Response As HttpResponseMessage = Nothing, ResponseStream As Stream = 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... 的极大延迟
             '部分下载源真的特别慢,并且只需要一个请求,例如 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)
             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)
                 HttpRequest = New HttpRequestMessage(HttpMethod.Get, Th.Source.Url)
                 SecretHeadersSign(Th.Source.Url, HttpRequest, SimulateBrowserHeaders)
                 SecretHeadersSign(Th.Source.Url, HttpRequest, SimulateBrowserHeaders)
                 CancelToken = New CancellationTokenSource(Timeout)
                 CancelToken = New CancellationTokenSource(Timeout)
-                HostIp = DNSLookup(HttpRequest, CancelToken.Token) 'DNS 预解析
                 If Not Th.IsFirstThread Then HttpRequest.Headers.Range = New Headers.RangeHeaderValue(Th.DownloadStart, Nothing)
                 If Not Th.IsFirstThread Then HttpRequest.Headers.Range = New Headers.RangeHeaderValue(Th.DownloadStart, Nothing)
                 Dim ContentLength As Long = 0
                 Dim ContentLength As Long = 0
                 Response = ThreadClient.SendAsync(HttpRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
                 Response = ThreadClient.SendAsync(HttpRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
@@ -1119,15 +1045,13 @@ SourceBreak:
                     '本线程完成
                     '本线程完成
                     Th.State = NetState.Finished
                     Th.State = NetState.Finished
                     Logger.Trace(Function() $"{LocalName}:完成,已下载 {Th.DownloadDone}({Th.DownloadStart}~{Th.DownloadEnd})")
                     Logger.Trace(Function() $"{LocalName}:完成,已下载 {Th.DownloadDone}({Th.DownloadStart}~{Th.DownloadEnd})")
-                    RecordIPReliability(HostIp, 0.5)
                 End If
                 End If
             Catch ex As Exception
             Catch ex As Exception
                 If ex.IsBadNetwork OrElse TypeOf ex Is TaskCanceledException Then
                 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
                 Else
-                    Logger.Info(ex, $"{LocalName}:出错,IP:{HostIp}")
+                    Logger.Info(ex, $"{LocalName}:出错")
                 End If
                 End If
-                RecordIPReliability(HostIp, -0.7)
                 SourceFail(Th, ex, False)
                 SourceFail(Th, ex, False)
             Finally
             Finally
                 '释放资源
                 '释放资源

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

@@ -1379,11 +1379,16 @@ OnLoaded:
                         If Instance.State <> McInstanceState.Error AndAlso
                         If Instance.State <> McInstanceState.Error AndAlso
                            ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown") <> "Unknown" Then
                            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 {
                             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),
                                 .HasLiteLoader = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionLiteLoader", False),
                                 .VanillaName = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown")
                                 .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,
             Urls = ResourceVersion.ParseModrinthTrackArguments(Urls,
                 ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
                 ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
             Urls = Urls.Distinct.ToList()
             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)
                 MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True)
                 Throw New OperationCanceledException
                 Throw New OperationCanceledException
             End If
             End If

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

@@ -120,12 +120,12 @@ Public Module LocalResourceLoaders
         Next
         Next
         Logger.Info($"有 {Mods.Where(Function(m) m.Project Is Nothing).Count} 个 Mod 需要联网获取信息,{Mods.Where(Function(m) m.Project IsNot Nothing).Count} 个 Mod 需要更新信息")
         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
         If Not Mods.Any Then Return
-        '获取作为检查目标的加载器和版本
+        '获取作为检查目标的版本
         '此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
         '此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
         Dim TargetMcVersion As McVersion = Loader.Input.Target
         Dim TargetMcVersion As McVersion = Loader.Input.Target
         Dim VanillaVersion = TargetMcVersion.VanillaName
         Dim VanillaVersion = TargetMcVersion.VanillaName
         '开始网络获取
         '开始网络获取
-        Logger.Info($"目标加载器:{ModLoaders},版本:{VanillaVersion}")
+        Logger.Info($"正在检查 Mod 信息的版本:{VanillaVersion},加载器 {ModLoaders}")
         Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
         Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
         Dim CurrentTaskThread As Thread = Thread.CurrentThread
         Dim CurrentTaskThread As Thread = Thread.CurrentThread
         '从 Modrinth 获取信息
         '从 Modrinth 获取信息
@@ -140,6 +140,7 @@ Public Module LocalResourceLoaders
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 If ModrinthVersion.Count = 0 Then Return
                 If ModrinthVersion.Count = 0 Then Return
                 Dim ModrinthMapping As New Dictionary(Of String, List(Of LocalResourceFile))
                 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
                 For Each Entry In Mods
                     If Not ModrinthVersion.ContainsKey(Entry.ModrinthHash) Then Continue For
                     If Not ModrinthVersion.ContainsKey(Entry.ModrinthHash) Then Continue For
                     If ModrinthVersion(Entry.ModrinthHash)("files")(0)("hashes")("sha1") <> 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)
                     ModrinthMapping(ProjectId).Add(Entry)
                     '记录对应的 ProjectVersion
                     '记录对应的 ProjectVersion
                     Dim File = ResourceVersion.FromPlatformJson(ModrinthVersion(Entry.ModrinthHash), ResourceTypes.Mod)
                     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
                     If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then
                         Entry.ProjectVersion = File
                         Entry.ProjectVersion = File
                     Else
                     Else
@@ -169,17 +172,20 @@ Public Module LocalResourceLoaders
                 Next
                 Next
                 Logger.Info($"已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
                 Logger.Info($"已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
                 '步骤 4:获取更新信息
                 '步骤 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
                 For Each Entry In Mods
                     If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.ProjectVersion Is Nothing Then Continue For
                     If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.ProjectVersion Is Nothing Then Continue For
                     Dim UpdateFile = ResourceVersion.FromPlatformJson(ModrinthUpdate(Entry.ModrinthHash), ResourceTypes.Mod)
                     Dim UpdateFile = ResourceVersion.FromPlatformJson(ModrinthUpdate(Entry.ModrinthHash), ResourceTypes.Mod)
                     If Not UpdateFile.DownloadAvailable Then Continue For
                     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}")
                     Logger.Trace(Function() $"本地文件 {Entry.ProjectVersion.FileName} 在 Modrinth 上的最新版为 {UpdateFile.FileName}")
                     If Entry.ProjectVersion.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.ProjectVersion.Hash = UpdateFile.Hash Then Continue For
                     If Entry.ProjectVersion.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.ProjectVersion.Hash = UpdateFile.Hash Then Continue For
                     '设置更新日志与更新文件
                     '设置更新日志与更新文件
@@ -217,6 +223,7 @@ Public Module LocalResourceLoaders
 
 
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 '步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
                 Dim ProjectIdToLocalFiles As New Dictionary(Of Integer, List(Of LocalResourceFile))
                 Dim ProjectIdToLocalFiles As New Dictionary(Of Integer, List(Of LocalResourceFile))
+                Dim CurseForgeLoaders As New Dictionary(Of LocalResourceFile, ModLoaders)
                 For Each Project In CurseForgeRaw
                 For Each Project In CurseForgeRaw
                     Dim ProjectId = Project("id").ToString
                     Dim ProjectId = Project("id").ToString
                     Dim Hash As UInteger = Project("file")("fileFingerprint")
                     Dim Hash As UInteger = Project("file")("fileFingerprint")
@@ -226,6 +233,7 @@ Public Module LocalResourceLoaders
                         ProjectIdToLocalFiles.AddIntoValueCollection(ProjectId, Entry)
                         ProjectIdToLocalFiles.AddIntoValueCollection(ProjectId, Entry)
                         '记录对应的 ProjectVersion
                         '记录对应的 ProjectVersion
                         Dim File = ResourceVersion.FromPlatformJson(Project("file"), ResourceTypes.Mod)
                         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
                         If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then Entry.ProjectVersion = File
                     Next
                     Next
                 Next
                 Next
@@ -242,34 +250,34 @@ Public Module LocalResourceLoaders
                     If ProjectJson("isAvailable") IsNot Nothing AndAlso Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
                     If ProjectJson("isAvailable") IsNot Nothing AndAlso Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
                     '设置 Entry 中的工程信息
                     '设置 Entry 中的工程信息
                     Dim Project As New ResourceProject(ProjectJson)
                     Dim Project As New ResourceProject(ProjectJson)
-                    Dim TargetLoaders As ModLoaders = ModLoaders.None
                     For Each LocalFile In ProjectIdToLocalFiles(Project.Id) '倒查防止 CurseForge 返回的内容有漏
                     For Each LocalFile In ProjectIdToLocalFiles(Project.Id) '倒查防止 CurseForge 返回的内容有漏
                         If LocalFile.Project?.Platform = ResourcePlatforms.Modrinth Then
                         If LocalFile.Project?.Platform = ResourcePlatforms.Modrinth Then
                             LocalFile.Project = LocalFile.Project '再次触发修改事件
                             LocalFile.Project = LocalFile.Project '再次触发修改事件
                         Else
                         Else
                             LocalFile.Project = Project
                             LocalFile.Project = Project
-                            TargetLoaders = TargetLoaders Or LocalFile.Project.ModLoaders
                         End If
                         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
                 Next
                 Next
                 Logger.Info($"已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
                 Logger.Info($"已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
@@ -283,6 +291,10 @@ Public Module LocalResourceLoaders
                     Dim File = ResourceVersion.FromPlatformJson(FileJson, ResourceTypes.Mod)
                     Dim File = ResourceVersion.FromPlatformJson(FileJson, ResourceTypes.Mod)
                     If Not File.DownloadAvailable Then Continue For
                     If Not File.DownloadAvailable Then Continue For
                     For Each Entry As LocalResourceFile In UpdateFileIds(File.Id)
                     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
                         If UpdateFiles.ContainsKey(Entry) AndAlso UpdateFiles(Entry).ReleaseDate >= File.ReleaseDate Then Continue For
                         UpdateFiles(Entry) = File
                         UpdateFiles(Entry) = File
                     Next
                     Next

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

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

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

@@ -21,8 +21,8 @@ Public Class ResourceSearcher
         ''' </summary>
         ''' </summary>
         Public ReadOnly Property CanContinue As Boolean
         Public ReadOnly Property CanContinue As Boolean
             Get
             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
                 If Storage.CurseForgeTotal = -1 OrElse Storage.ModrinthTotal = -1 Then Return True
                 Return Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
                 Return Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
             End Get
             End Get
@@ -241,7 +241,7 @@ Public Class ResourceSearcher
                 Next
                 Next
             End Function
             End Function
             'CurseForge
             '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()
                 Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.CurseForge).ToList()
                 Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
                 Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
@@ -258,7 +258,7 @@ Public Class ResourceSearcher
                 End If
                 End If
             End If
             End If
             'Modrinth
             '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()
                 Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.Modrinth).ToList()
                 Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
                 Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
@@ -303,7 +303,7 @@ NextPage:
         Try
         Try
 
 
             'CurseForge 搜索
             '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 (Storage.CurseForgeTotal > -1 AndAlso Storage.CurseForgeTotal <= Storage.CurseForgeOffset) AndAlso '剩余的未显示的搜索结果不足
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(CurseForgeAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(CurseForgeAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                 WorkThreads.Add(RunInNewThread(
                 WorkThreads.Add(RunInNewThread(
@@ -317,7 +317,7 @@ NextPage:
                         For Each JsonEntry As JObject In RequestResult("data")
                         For Each JsonEntry As JObject In RequestResult("data")
                             Dim Project As New ResourceProject(JsonEntry)
                             Dim Project As New ResourceProject(JsonEntry)
                             If Request.Type = ResourceTypes.ResourcePack AndAlso Project.Tags.Contains("数据包") Then Continue For 'CurseForge 将一些数据包分类成了资源包
                             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)
                             ProjectList.Add(Project)
                         Next
                         Next
                         '更新结果
                         '更新结果
@@ -335,7 +335,7 @@ NextPage:
             End If
             End If
 
 
             'Modrinth 搜索
             '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 (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(ModrinthAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                (Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(ModrinthAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
                 WorkThreads.Add(RunInNewThread(
                 WorkThreads.Add(RunInNewThread(
@@ -363,7 +363,7 @@ NextPage:
             End If
             End If
 
 
             'Modrinth 直接获取工程
             '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 (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
                ModrinthSlugs.Any Then '有直接获取的 Slug
                ModrinthSlugs.Any Then '有直接获取的 Slug
                 WorkThreads.Add(RunInNewThread(
                 WorkThreads.Add(RunInNewThread(
@@ -375,7 +375,7 @@ NextPage:
                         For Each JsonEntry As JObject In DlModRequest(ModrinthUrl)
                         For Each JsonEntry As JObject In DlModRequest(ModrinthUrl)
                             Dim Project As New ResourceProject(JsonEntry)
                             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
                             If Not String.IsNullOrEmpty(Request.Tag) AndAlso
                                 Not JsonEntry("categories").Any(Function(c) c.ToString = Request.Tag.AfterLast("/")) Then Continue For 'Project.Tags 已经转换成中文了,只能从 json 判
                                 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
                             If Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
@@ -405,7 +405,7 @@ NextPage:
 
 
             '仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
             '仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
             If IgnoreModLoaderFilter Then
             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
             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)()
                 For Each Loader As ModLoaders In EnumUtils.GetAllFlags(Of ModLoaders)()
                     If RawLoaders.Contains(Loader.ToString.Lower) Then .ModLoaders = .ModLoaders Or Loader
                     If RawLoaders.Contains(Loader.ToString.Lower) Then .ModLoaders = .ModLoaders Or Loader
                 Next
                 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.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then .ResourceType = ResourceTypes.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
                     If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
                     If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
                     If .ModLoaders.Flags().Any() Then .ResourceType = ResourceTypes.Mod
                     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("")>
 <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 VersionFolder As String = McFolderSelected & "versions\" & InstanceName & "\"
 
 
         Dim Loaders As New List(Of LoaderBase)
         Dim Loaders As New List(Of LoaderBase)
+        Dim NewInstance As McInstance = Nothing
 
 
         '下载版本 Json 文件
         '下载版本 Json 文件
         If JsonUrl Is Nothing Then
         If JsonUrl Is Nothing Then
@@ -23,14 +24,19 @@ Public Module ModDownloadLib
         Loaders.Add(New LoaderDownload(McDownloadClientJsonName, New List(Of NetFile) From {
         Loaders.Add(New LoaderDownload(McDownloadClientJsonName, New List(Of NetFile) From {
             New NetFile(DlSourceLauncherOrMetaGet(If(JsonUrl, "")), VersionFolder & InstanceName & ".json", New FileChecker With {.IsJson = True})
             New NetFile(DlSourceLauncherOrMetaGet(If(JsonUrl, "")), VersionFolder & InstanceName & ".json", New FileChecker With {.IsJson = True})
         }) With {.ProgressWeight = 3})
         }) 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)
         Dim LoadersLib As New List(Of LoaderBase)
         LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
         LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
-            Thread.Sleep(50) '等待 JSON 文件实际写入硬盘(#3710)
             Logger.Info($"开始分析原版支持库文件:{VersionFolder}")
             Logger.Info($"开始分析原版支持库文件:{VersionFolder}")
-            Task.Output = McLibNetFilesFromInstance(New McInstance(VersionFolder))
+            Task.Output = McLibNetFilesFromInstance(NewInstance)
         End Sub) With {.ProgressWeight = 1, .Show = False})
         End Sub) With {.ProgressWeight = 1, .Show = False})
         LoadersLib.Add(New LoaderDownload("下载原版支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 13, .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})
         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))("分析资源文件索引地址(副加载器)",
         LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址(副加载器)",
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
         Sub(Task As LoaderTask(Of String, List(Of NetFile)))
             Try
             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})
                 Task.Output = If(AssetIndex Is Nothing, New List(Of NetFile), New List(Of NetFile) From {AssetIndex})
             Catch ex As Exception
             Catch ex As Exception
                 Throw New Exception("分析资源文件索引地址失败", ex)
                 Throw New Exception("分析资源文件索引地址失败", ex)
@@ -56,7 +62,7 @@ Public Module ModDownloadLib
         End Sub) With {.ProgressWeight = 1, .Show = False})
         End Sub) With {.ProgressWeight = 1, .Show = False})
         LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
         LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
         LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析所需资源文件(副加载器)",
         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})
         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})
         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 IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
             Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected
             Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected
             Dim SavesFolder = DirectoryUtils.GetInfo(PageInstanceLeft.Instance.PathIndie & "saves\")
             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} 吗?" &
             Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageInstanceLeft.Instance.Name} 吗?" &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!" &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!" &
                             If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""), ""),
                             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" />
                     <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="Z" />
                         <TextBlock Margin="0,1.5,6,0" Text="N" />
                         <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="妖" />
                         <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="乔" />
                         <TextBlock Margin="0,1.5,6,0" Text="乔" />
                         <TextBlock Margin="0,1.5,6,0" Text="cc" />
                         <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="33" />
                         <TextBlock Margin="0,1.5,6,0" Text="kk" />
                         <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="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="+1" />
                         <TextBlock Margin="0,1.5,6,0" Text="Iirs" />
                         <TextBlock Margin="0,1.5,6,0" Text="Iirs" />
                         <TextBlock Margin="0,1.5,6,0" Text="Lio" />
                         <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="Sht" />
                         <TextBlock Margin="0,1.5,6,0" Text="Nul" />
                         <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="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="kile" />
                         <TextBlock Margin="0,1.5,6,0" Text="pbc" />
                         <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="Aya" />
                         <TextBlock Margin="0,1.5,6,0" Text="mjp" />
                         <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="墨渊" />
                         <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="菜菜" />
+                        <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="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="ZDD" />
                         <TextBlock Margin="0,1.5,6,0" Text="Um1" />
                         <TextBlock Margin="0,1.5,6,0" Text="Um1" />
                         <TextBlock Margin="0,1.5,6,0" Text="2514" />
                         <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="joker" />
                         <TextBlock Margin="0,1.5,6,0" Text="RXZY" />
                         <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="白成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="Rofni" />
                         <TextBlock Margin="0,1.5,6,0" Text="mzqc" />
                         <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="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="xxlzta" />
                         <TextBlock Margin="0,1.5,6,0" Text="end_x" />
                         <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="QWYJ" />
                         <TextBlock Margin="0,1.5,6,0" Text="qwqw" />
                         <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="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="晓空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="无名氏" />
                         <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="乐永昶" />
                         <TextBlock Margin="0,1.5,6,0" Text="lingke" />
                         <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="伊文斯" />
                         <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="厄尔萨" />
+                        <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="慕qwq" />
                         <TextBlock Margin="0,1.5,6,0" Text="Twicat" />
                         <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="stenac" />
                         <TextBlock Margin="0,1.5,6,0" Text="JOKER" />
                         <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="半日谣." />
+                        <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="Triple9" />
                         <TextBlock Margin="0,1.5,6,0" Text="gediao" />
                         <TextBlock Margin="0,1.5,6,0" Text="gediao" />
                         <TextBlock Margin="0,1.5,6,0" Text="Zen1th" />
                         <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="Yi_xuan" />
                         <TextBlock Margin="0,1.5,6,0" Text="MC芬达" />
                         <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="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="𝕳轩辕氏" />
                         <TextBlock Margin="0,1.5,6,0" Text="谜茶360" />
                         <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="Eventful" />
                         <TextBlock Margin="0,1.5,6,0" Text="Molioos" />
                         <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="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="狼烟居士" />
                         <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" />
                         <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="好人好逝" />
                         <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="MuMAA" />
                         <TextBlock Margin="0,1.5,6,0" Text="溯枫qwq" />
                         <TextBlock Margin="0,1.5,6,0" Text="溯枫qwq" />
                         <TextBlock Margin="0,1.5,6,0" Text="Tacenda" />
                         <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="👁👄👃" />
                         <TextBlock Margin="0,1.5,6,0" Text="秋慕owo" />
                         <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="船长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="是小61呀" />
                         <TextBlock Margin="0,1.5,6,0" Text="47号虾球" />
                         <TextBlock Margin="0,1.5,6,0" Text="47号虾球" />
                         <TextBlock Margin="0,1.5,6,0" Text="Alancnel" />
                         <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="MselQAQ" />
                         <TextBlock Margin="0,1.5,6,0" Text="UZQueen" />
                         <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="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="Prifo_lone" />
                         <TextBlock Margin="0,1.5,6,0" Text="有....暗星!" />
                         <TextBlock Margin="0,1.5,6,0" Text="有....暗星!" />
                         <TextBlock Margin="0,1.5,6,0" Text="Hitsser 1.0" />
                         <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="水星有海吗" />
                         <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="ac纯萌新cy" />
                         <TextBlock Margin="0,1.5,6,0" Text="岁月suiyue" />
                         <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="陌然MajorC" />
                         <TextBlock Margin="0,1.5,6,0" Text="-Pareidolia." />
                         <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="蛙蛙大王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="Yhcmdbfwz" />
                         <TextBlock Margin="0,1.5,6,0" Text="Ayers赜蠹嬭" />
                         <TextBlock Margin="0,1.5,6,0" Text="Ayers赜蠹嬭" />
                         <TextBlock Margin="0,1.5,6,0" Text="We1J1uSh1" />
                         <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="小田love白子" />
                         <TextBlock Margin="0,1.5,6,0" Text="Sasha_Xiong" />
                         <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="赤狸_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="Name0emaN" />
                         <TextBlock Margin="0,1.5,6,0" Text="happypickles" />
                         <TextBlock Margin="0,1.5,6,0" Text="happypickles" />
                         <TextBlock Margin="0,1.5,6,0" Text="无中生有为dd" />
                         <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="没什么名字好取" />
                         <TextBlock Margin="0,1.5,6,0" Text="Wanderer5147" />
                         <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="向着有风的地方" />
                         <TextBlock Margin="0,1.5,6,0" Text="AAA神秘人批发" />
                         <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="Bangbang2025" />
                         <TextBlock Margin="0,1.5,6,0" Text="贝吉蒙Begimon" />
                         <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="取个名字真难0.0" />
                         <TextBlock Margin="0,1.5,6,0" Text="NoTuring_Steve" />
                         <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="冰冰watermelon" />
                         <TextBlock Margin="0,1.5,6,0" Text="DOODOODOOD" />
                         <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="弹跳爆炸小鱼珠🐟" />
                         <TextBlock Margin="0,1.5,6,0" Text="弹跳爆炸小鱼珠🐟" />
                         <TextBlock Margin="0,1.5,6,0" Text="Xanadu Silk Fable" />
                         <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 IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
             Dim IsHintIndie As Boolean = Instance.State <> McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected
             Dim IsHintIndie As Boolean = Instance.State <> McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected
             Dim SavesFolder = DirectoryUtils.GetInfo(Instance.PathIndie & "saves\")
             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} 吗?" &
             Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {Instance.Name} 吗?" &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!", "") &
                         If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!", "") &
                         If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""),
                         If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""),