Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion PCL.Core/App/Localization/Languages/en-US.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,13 @@
<sys:String x:Key="Instance.Resource.ExportInfo">Export info</sys:String>
<sys:String x:Key="Instance.Resource.ExportInfo.ToolTip">Export detailed version info for resources</sys:String>
<sys:String x:Key="Instance.Resource.Favorite">Favorite</sys:String>
<sys:String x:Key="Instance.Resource.Import.ItemCount">{0} resource item(s) available</sys:String>
<sys:String x:Key="Instance.Resource.Import.NoAvailableInstance">No other instance contains resources available for import</sys:String>
<sys:String x:Key="Instance.Resource.Import.OverwriteConfirm.Message">A resource named {0} already exists. Overwrite it?</sys:String>
<sys:String x:Key="Instance.Resource.Import.OverwriteConfirm.Title">Confirm resource overwrite</sys:String>
<sys:String x:Key="Instance.Resource.Import.SelectInstance">Select source instance</sys:String>
<sys:String x:Key="Instance.Resource.Import.Success">Imported {0} resource item(s)</sys:String>
<sys:String x:Key="Instance.Resource.ImportFromInstance">Import from another instance</sys:String>
<sys:String x:Key="Instance.Resource.InstallFromFiles">Install from files</sys:String>
<sys:String x:Key="Instance.Resource.List.Loading">Loading resource list</sys:String>
<sys:String x:Key="Instance.Resource.Loading">Loading resource list</sys:String>
Expand Down Expand Up @@ -3570,4 +3577,4 @@
<sys:String x:Key="Watcher.WindowLoaded">Minecraft window loaded: {0} ({1})</sys:String>
<sys:String x:Key="Watcher.WindowMaximized">Minecraft window maximized: {0}</sys:String>

</ResourceDictionary>
</ResourceDictionary>
9 changes: 8 additions & 1 deletion PCL.Core/App/Localization/Languages/zh-CN.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,13 @@
<sys:String x:Key="Instance.Resource.ExportInfo">导出信息</sys:String>
<sys:String x:Key="Instance.Resource.ExportInfo.ToolTip">导出详细的资源的版本信息</sys:String>
<sys:String x:Key="Instance.Resource.Favorite">收藏</sys:String>
<sys:String x:Key="Instance.Resource.Import.ItemCount">{0} 个可导入资源</sys:String>
<sys:String x:Key="Instance.Resource.Import.NoAvailableInstance">没有找到包含可导入资源的其他实例</sys:String>
<sys:String x:Key="Instance.Resource.Import.OverwriteConfirm.Message">已存在同名资源:{0},是否覆盖?</sys:String>
<sys:String x:Key="Instance.Resource.Import.OverwriteConfirm.Title">资源覆盖确认</sys:String>
<sys:String x:Key="Instance.Resource.Import.SelectInstance">选择来源实例</sys:String>
<sys:String x:Key="Instance.Resource.Import.Success">已导入 {0} 个资源</sys:String>
<sys:String x:Key="Instance.Resource.ImportFromInstance">从其他实例导入</sys:String>
<sys:String x:Key="Instance.Resource.InstallFromFiles">从文件安装</sys:String>
<sys:String x:Key="Instance.Resource.List.Loading">正在加载资源列表</sys:String>
<sys:String x:Key="Instance.Resource.Loading">正在加载资源列表</sys:String>
Expand Down Expand Up @@ -3570,4 +3577,4 @@
<sys:String x:Key="Watcher.WindowLoaded">Minecraft 窗口已加载:{0}({1})</sys:String>
<sys:String x:Key="Watcher.WindowMaximized">已最大化 Minecraft 窗口:{0}</sys:String>

</ResourceDictionary>
</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

<local:MyButton x:Name="BtnManageInstall" MinWidth="110" Text="{DynamicResource Instance.Resource.InstallFromFiles}" Padding="13,7"
Margin="0,7,15,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnManageImport" MinWidth="110" Text="{DynamicResource Instance.Resource.ImportFromInstance}" Padding="13,7"
Margin="0,7,15,0" HorizontalAlignment="Left" Visibility="Collapsed" />
<local:MyButton x:Name="BtnManageDownload" MinWidth="110" Text="{DynamicResource Instance.Resource.DownloadNew}" Padding="13,7"
Margin="0,7,15,0" HorizontalAlignment="Left" />
<local:MyButton x:Name="BtnManageSelectAll" MinWidth="110" Text="{DynamicResource Instance.Resource.SelectAll}" Padding="13,7"
Expand Down Expand Up @@ -71,6 +73,8 @@
Padding="12,0" Visibility="Collapsed" />
<local:MyButton Height="35" x:Name="BtnHintInstall" MinWidth="130" Text="{DynamicResource Instance.Resource.InstallFromFiles}" Margin="5,0"
Padding="12,0" ColorType="Highlight" />
<local:MyButton Height="35" x:Name="BtnHintImport" MinWidth="130" Text="{DynamicResource Instance.Resource.ImportFromInstance}" Margin="5,0"
Padding="12,0" Visibility="Collapsed" />
<local:MyButton Height="35" x:Name="BtnHintDownload" MinWidth="130" Text="{DynamicResource Instance.Resource.DownloadNew}" Margin="5,0"
Padding="12,0" />
<local:MyButton Height="35" x:Name="BtnHintOpen" MinWidth="130" Text="{DynamicResource Common.Action.OpenFolder}" Margin="5,0"
Expand Down Expand Up @@ -134,4 +138,4 @@
</StackPanel>
</local:MyCard>
</Grid>
</local:MyPageRight>
</local:MyPageRight>
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public PageInstanceCompResource()
BtnHintOpen.Click += BtnManageOpen_Click;
BtnManageSelectAll.Click += BtnManageSelectAll_Click;
BtnManageInstall.Click += BtnManageInstall_Click;
BtnManageImport.Click += BtnManageImport_Click;
BtnHintInstall.Click += BtnManageInstall_Click;
BtnHintImport.Click += BtnManageImport_Click;
BtnManageInfoExport.Click += BtnManageInfoExport_Click;
BtnManageDownload.Click += BtnManageDownload_Click;
BtnHintDownload.Click += BtnManageDownload_Click;
Expand Down Expand Up @@ -117,6 +119,12 @@ public PageInstanceCompResource(ModComp.CompType loadCompType)
BtnSelectDisable.Visibility = Visibility.Collapsed;
}

if (new[] { ModComp.CompType.Shader, ModComp.CompType.ResourcePack }.Contains(currentCompType))
{
BtnManageImport.Visibility = Visibility.Visible;
BtnHintImport.Visibility = Visibility.Visible;
}

// 投影文件管理页隐藏下载按钮
if (currentCompType == ModComp.CompType.Schematic)
{
Expand All @@ -136,7 +144,9 @@ public PageInstanceCompResource(ModComp.CompType loadCompType)
BtnHintOpen.Click += BtnManageOpen_Click;
BtnManageSelectAll.Click += BtnManageSelectAll_Click;
BtnManageInstall.Click += BtnManageInstall_Click;
BtnManageImport.Click += BtnManageImport_Click;
BtnHintInstall.Click += BtnManageInstall_Click;
BtnHintImport.Click += BtnManageImport_Click;
BtnManageDownload.Click += BtnManageDownload_Click;
BtnHintDownload.Click += BtnManageDownload_Click;
BtnManageInfoExport.Click += BtnManageInfoExport_Click;
Expand Down Expand Up @@ -1021,6 +1031,147 @@ private void BtnManageInstall_Click(object sender, MouseButtonEventArgs e)
InstallCompFiles(fileList, currentCompType, CurrentFolderPath);
}

/// <summary>
/// 从其他实例导入资源包或光影包。
/// </summary>
private void BtnManageImport_Click(object sender, MouseButtonEventArgs e)
{
var folderName = currentCompType switch
{
ModComp.CompType.ResourcePack => "resourcepacks",
ModComp.CompType.Shader => "shaderpacks",
_ => null
};
if (folderName is null) return;

var targetFolder = Path.GetFullPath(Path.Combine(PageInstanceLeft.McInstance.PathIndie, folderName));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the LabyMod-aware component directory for imports

When either the current or source instance is a LabyMod instance, LoaderRun manages resources under the instance's LabyMod-specific labymod-neo/fabric/<VanillaName> prefix, while this code always targets PathIndie/<folderName> and line 1053 searches sources there as well. As a result, a target import can report success but remain absent after the refresh, and LabyMod sources containing resources in the managed directory are not offered; derive both paths using the same LabyMod-aware component-path logic as the page loader.

Useful? React with 👍 / 👎.

var sources = ModInstanceList.mcInstanceList.Values.SelectMany(list => list)
.Where(instance => instance.state != McInstanceState.Error &&
!instance.Equals(PageInstanceLeft.McInstance))
.Select(instance =>
{
var sourceFolder = Path.GetFullPath(Path.Combine(instance.PathIndie, folderName));
var entries = _GetImportEntries(instance, sourceFolder);
return (Instance: instance, Folder: sourceFolder, Entries: entries);
})
.Where(source => !source.Folder.Equals(targetFolder, StringComparison.OrdinalIgnoreCase) &&
source.Entries.Length > 0)
.OrderBy(source => source.Instance.Name)
.ToList();

if (sources.Count == 0)
{
HintService.Hint(Lang.Text("Instance.Resource.Import.NoAvailableInstance"));
return;
}

var selection = sources.Select(source => (IMyRadio)new MyListItem
{
Title = source.Instance.Name,
Info = Lang.Text("Instance.Resource.Import.ItemCount", source.Entries.Length),
Type = MyListItem.CheckType.RadioBox
}).ToList();
var selectedIndex = ModMain.MyMsgBoxSelect(selection,
Lang.Text("Instance.Resource.Import.SelectInstance"),
Lang.Text("Common.Action.Confirm"), Lang.Text("Common.Action.Cancel"));
if (selectedIndex is null) return;

_ImportEntries(sources[selectedIndex.Value].Entries, targetFolder);
}

private static string[] _GetImportEntries(McInstance instance, string sourceFolder)
{
try
{
return Directory.Exists(sourceFolder)
? Directory.EnumerateFileSystemEntries(sourceFolder, "*", new EnumerationOptions
{
RecurseSubdirectories = false,
IgnoreInaccessible = false,
AttributesToSkip = FileAttributes.ReparsePoint
}).ToArray()
: [];
}
catch (Exception ex)
{
ModBase.Log(ex, $"读取实例 {instance.Name} 的资源文件失败");
return [];
}
}

private void _ImportEntries(IEnumerable<string> entries, string targetFolder)
{
try
{
Directory.CreateDirectory(targetFolder);
var importedCount = 0;
foreach (var sourceEntry in entries)
{
var entryName = Path.GetFileName(Path.TrimEndingDirectorySeparator(sourceEntry));
var targetEntry = Path.Combine(targetFolder, entryName);
if (File.Exists(targetEntry) || Directory.Exists(targetEntry))
{
if (ModMain.MyMsgBox(
Lang.Text("Instance.Resource.Import.OverwriteConfirm.Message", entryName),
Lang.Text("Instance.Resource.Import.OverwriteConfirm.Title"),
Lang.Text("Common.Action.Overwrite"), Lang.Text("Common.Action.Cancel")) != 1)
continue;
_DeleteImportTarget(targetEntry);
}

if (File.Exists(sourceEntry))
ModBase.CopyFile(sourceEntry, targetEntry);
else if (Directory.Exists(sourceEntry))
_CopyImportDirectory(sourceEntry, targetEntry);
else
continue;
importedCount += 1;
}

if (importedCount > 0)
{
HintService.Hint(Lang.Text("Instance.Resource.Import.Success", importedCount), HintType.Success);
ReloadCompFileList(true);
}
}
catch (Exception ex)
{
ModBase.Log(ex, "从其他实例复制资源失败", ModBase.LogLevel.Msgbox,
userSummary: Lang.Text("Instance.Resource.Error.OperationFailed"));
}
}

private static void _DeleteImportTarget(string targetEntry)
{
if (File.Exists(targetEntry))
{
File.Delete(targetEntry);
return;
}

if (!Directory.Exists(targetEntry)) return;
if (File.GetAttributes(targetEntry).HasFlag(FileAttributes.ReparsePoint))
Directory.Delete(targetEntry);
else
ModBase.DeleteDirectory(targetEntry);
}

private static void _CopyImportDirectory(string sourceFolder, string targetFolder)
{
Directory.CreateDirectory(targetFolder);
var options = new EnumerationOptions
{
RecurseSubdirectories = true,
IgnoreInaccessible = false,
AttributesToSkip = FileAttributes.ReparsePoint
};
foreach (var sourceFile in Directory.EnumerateFiles(sourceFolder, "*", options))
{
var targetFile = Path.Combine(targetFolder, Path.GetRelativePath(sourceFolder, sourceFile));
ModBase.CopyFile(sourceFile, targetFile);
}
}

/// <summary>
/// 尝试安装 Mod。
/// 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。
Expand Down
Loading