diff --git a/MeloongCore b/MeloongCore
index 8bfb4f7..811438a 160000
--- a/MeloongCore
+++ b/MeloongCore
@@ -1 +1 @@
-Subproject commit 8bfb4f709f78a528a40e0bd48e6f77bef0e07e24
+Subproject commit 811438aa3c7c0437c7e60e85b516cc593dcf6cb1
diff --git a/PCLCS/Constants.cs b/PCLCS/Constants.cs
index 4870d0c..2e1b7fd 100644
--- a/PCLCS/Constants.cs
+++ b/PCLCS/Constants.cs
@@ -68,7 +68,7 @@ public static class Versions {
///
/// 版本管理中 Mod 信息缓存的版本号。
///
- public const int LocalModCacheVersion = 18;
+ public const int LocalModCacheVersion = 19;
///
/// Minecraft 本地实例信息缓存的版本号。
///
diff --git a/PCLCS/Java.cs b/PCLCS/Java.cs
index 4074c97..8c88fd3 100644
--- a/PCLCS/Java.cs
+++ b/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;
diff --git a/Plain Craft Launcher 2/FormMain.xaml.vb b/Plain Craft Launcher 2/FormMain.xaml.vb
index 2ba60a7..1f350cc 100644
--- a/Plain Craft Launcher 2/FormMain.xaml.vb
+++ b/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
diff --git a/Plain Craft Launcher 2/Modules/Base/ModBase.vb b/Plain Craft Launcher 2/Modules/Base/ModBase.vb
index b481080..197870c 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModBase.vb
+++ b/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
'版本信息
diff --git a/Plain Craft Launcher 2/Modules/Base/ModNet.vb b/Plain Craft Launcher 2/Modules/Base/ModNet.vb
index 357902b..34c994e 100644
--- a/Plain Craft Launcher 2/Modules/Base/ModNet.vb
+++ b/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
- '''
- ''' 进行 DNS 解析。它仅在选择的 IP 与系统默认的不一致时才对 URL 中的 Host 进行替换。
- ''' 返回请求时应使用的 IP。若解析失败,则返回 Nothing。
- '''
- 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)
- '''
- ''' 记录每个 IP 地址的请求可靠度。
- ''' 通常取值 -1 ~ +0.5,越高越好。未尝试过的 IP 应视为 0。
- '''
- Private IPReliability As New ConcurrentDictionary(Of String, Double)
- '''
- ''' 根据请求结果,记录 IP 地址的可靠度。
- '''
- 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
'释放资源
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
index d4ea6d0..f6ddaba 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModMinecraft.vb
+++ b/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")
}
diff --git a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb b/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
index 7d21482..5b86369 100644
--- a/Plain Craft Launcher 2/Modules/Minecraft/ModModpack.vb
+++ b/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
diff --git a/Plain Craft Launcher 2/Modules/Resource/LocalResourceLoaders.vb b/Plain Craft Launcher 2/Modules/Resource/LocalResourceLoaders.vb
index 5168084..4bed629 100644
--- a/Plain Craft Launcher 2/Modules/Resource/LocalResourceLoaders.vb
+++ b/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
diff --git a/Plain Craft Launcher 2/Modules/Resource/ResourceProject.vb b/Plain Craft Launcher 2/Modules/Resource/ResourceProject.vb
index 516c26b..53e70d9 100644
--- a/Plain Craft Launcher 2/Modules/Resource/ResourceProject.vb
+++ b/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
diff --git a/Plain Craft Launcher 2/Modules/Resource/ResourceSearcher.vb b/Plain Craft Launcher 2/Modules/Resource/ResourceSearcher.vb
index cf4a651..263be2f 100644
--- a/Plain Craft Launcher 2/Modules/Resource/ResourceSearcher.vb
+++ b/Plain Craft Launcher 2/Modules/Resource/ResourceSearcher.vb
@@ -21,8 +21,8 @@ Public Class ResourceSearcher
'''
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
'确保存在结果
diff --git a/Plain Craft Launcher 2/Modules/Resource/ResourceVersion.vb b/Plain Craft Launcher 2/Modules/Resource/ResourceVersion.vb
index 79dc5d2..857c7e4 100644
--- a/Plain Craft Launcher 2/Modules/Resource/ResourceVersion.vb
+++ b/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
diff --git a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
index e39984b..c71c896 100644
--- a/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
+++ b/Plain Craft Launcher 2/My Project/AssemblyInfo.vb
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
' 方法是按如下所示使用“*”
-
-
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb b/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
index 38bd0d1..c1e0c69 100644
--- a/Plain Craft Launcher 2/Pages/PageDownload/ModDownloadLib.vb
+++ b/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})
diff --git a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceOverall.xaml.vb b/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceOverall.xaml.vb
index 5edaa53..3c160cc 100644
--- a/Plain Craft Launcher 2/Pages/PageInstance/PageInstanceOverall.xaml.vb
+++ b/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 & "· "), ""), ""),
diff --git a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
index 59ed685..4621e50 100644
--- a/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
+++ b/Plain Craft Launcher 2/Pages/PageOther/PageOtherAbout.xaml
@@ -134,8 +134,10 @@
+
+
@@ -150,6 +152,7 @@
+
@@ -158,16 +161,22 @@
+
+
+
+
+
+
@@ -213,7 +222,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -224,21 +247,26 @@
+
+
+
+
+
@@ -255,6 +283,7 @@
+
@@ -273,12 +302,22 @@
+
+
+
+
+
+
+
+
+
+
@@ -292,12 +331,18 @@
+
+
+
+
+
+
@@ -332,11 +377,16 @@
+
+
+
+
+
@@ -344,6 +394,7 @@
+
@@ -360,6 +411,7 @@
+
@@ -382,11 +434,13 @@
+
+
@@ -405,6 +459,7 @@
+
@@ -439,13 +494,17 @@
+
+
+
+
diff --git a/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb b/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb
index 25393d3..63ae8c5 100644
--- a/Plain Craft Launcher 2/Pages/PageSelectRight.xaml.vb
+++ b/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 & "· "), ""),