Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

413 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using PCL.Network;
using PCL.Core.App.Localization;
namespace PCL;
public partial class PageSpeedLeft
{
private const int watcherInterval = 300;
// 定时器任务
private readonly Dictionary<string, MyCard> rightCards = new();
// 初始化
private bool isLoad;
public PageSpeedLeft()
{
InitializeComponent();
Loaded += Page_Loaded;
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// 进入时就刷新一次显示
Watcher();
// 如果在页面切换动画的 “上一页消失” 部分已经完成了下载,就直接尝试返回
TryReturnToHome();
if (isLoad)
return;
isLoad = true;
// 监控定时器
var timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, watcherInterval) };
timer.Tick += (_, _) => Watcher();
timer.Start();
// 非调试模式隐藏线程数
if (!ModBase.modeDebug)
{
RowDefinitions[12].Height = new GridLength(0d);
RowDefinitions[13].Height = new GridLength(0d);
RowDefinitions[14].Height = new GridLength(0d);
RowDefinitions[15].Height = new GridLength(0d);
}
}
private void Watcher()
{
if (ModMain.frmMain.pageCurrent != FormMain.PageType.TaskManager)
return;
try
{
#region 更新左边栏
if (!ModLoader.loaderTaskbar.Any())
{
// 无任务
LabProgress.Text = Lang.Number(1d, "P0");
LabSpeed.Text = ModBase.GetString(0) + "/s";
LabFile.Text = Lang.Number(0, "N0");
LabThread.Text = Lang.Number(0, "N0") + " / " + Lang.Number(ModNet.NetTaskThreadLimit, "N0");
}
else
{
// 有任务,输出基本信息
var tasks = ModLoader.loaderTaskbar.Where(l => l.show).ToList(); // 筛选掉启动 MC 的任务(#6270)
var rawPercent = tasks.Any()
? ModBase.MathClamp(
tasks.Average(l => l.Progress),
0, 1)
: 1d;
var predictText = Lang.Number(rawPercent, "P2");
LabProgress.Text = rawPercent > 0.999999d ? Lang.Number(1d, "P0") : predictText;
LabSpeed.Text = ModBase.GetString(ModNet.NetManager.Speed) + "/s";
LabFile.Text = ModNet.NetManager.FileRemain < 0 ? "0*" : Lang.Number(ModNet.NetManager.FileRemain, "N0");
LabThread.Text = Lang.Number(ModNet.NetManager.ThreadCount, "N0") + " / " +
Lang.Number(ModNet.NetTaskThreadLimit, "N0");
}
}
#endregion
catch (Exception ex)
{
ModBase.Log(
ex,
"任务管理左栏监视出错",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
if (ModMain.frmSpeedRight is null || ModMain.frmSpeedRight.PanMain is null)
return;
try
{
foreach (var Loader in ModLoader.loaderTaskbar.ToList())
TaskRefresh(Loader);
}
catch (Exception ex)
{
ModBase.Log(
ex,
"任务管理右栏监视出错",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
}
public void TaskRefresh(ModLoader.LoaderBase loader)
{
if (loader is null || !loader.show)
return;
try
{
// 获取实际加载器列表
var loaderList = ((ModLoader.LoaderCombo)loader).GetLoaderList();
if (rightCards.ContainsKey(loader.name))
{
// 已有此卡片
Grid card = rightCards[loader.name];
var newValue = loader.Progress + (double)loader.State;
if (ModBase.Val(card.Tag) == newValue)
return;
card.Tag = newValue;
if (card.Children.Count <= 3)
{
ModBase.Log("[Watcher] 元素不足的卡片:" + loader.name, ModBase.LogLevel.Debug);
return;
}
card = (Grid)card.Children[3];
try
{
switch (loader.State)
{
case ModBase.LoadState.Failed:
{
#region 失败,更新卡片
card.RowDefinitions.Clear();
card.Children.Clear();
card.Children.Add((UIElement)ModBase.GetObjectFromXML(
"<Path xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Stretch=\"Uniform\" Tag=\"Failed\" Data=\"F1 M2.5,0 L0,2.5 7.5,10 0,17.5 2.5,20 10,12.5 17.5,20 20,17.5 12.5,10 20,2.5 17.5,0 10,7.5 2.5,0Z\" Height=\"15\" Width=\"15\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"0\" Fill=\"{DynamicResource ColorBrush3}\" Margin=\"0,1,0,0\" VerticalAlignment=\"Top\"/>"));
var tb = (TextBlock)ModBase.GetObjectFromXML(
"<TextBlock xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" TextWrapping=\"Wrap\" HorizontalAlignment=\"Left\" ToolTip=\"" + Lang.Text("Speed.Error.ClickToCopy") + "\" Grid.Column=\"1\" Grid.Row=\"0\" Margin=\"0,0,0,5\" />");
tb.Text = loader.Error.ToString();
tb.MouseLeftButtonDown += (sender, _) =>
{
ModBase.ClipboardSet(((TextBlock)sender).Text, false);
HintService.Hint(Lang.Text("Speed.Error.Copied"), HintType.Success);
};
card.Children.Add(tb);
break;
}
#endregion
case ModBase.LoadState.Finished:
{
#region 完成,销毁卡片并返回
ModAnimation.AniDispose((MyCard)card.Parent, true, _ => TryReturnToHome());
break;
}
#endregion
case ModBase.LoadState.Loading:
case ModBase.LoadState.Waiting:
{
#region 进度不同,更新卡片
do
{
try
{
if (card.Children.Count < loaderList.Count * 2)
{
ModBase.Log(
$"[Watcher] 刷新任务管理卡片 {loader.name} 失败:卡片中仅有 {card.Children.Count} 个子项,要求至少有 {loaderList.Count * 2} 个子项",
ModBase.LogLevel.Debug);
break;
}
var row = 0;
foreach (var SubTask in loaderList)
{
switch (SubTask.State)
{
case ModBase.LoadState.Waiting:
{
if ((string)((FrameworkElement)card.Children[row * 2]).Tag != "Waiting")
{
card.Children.RemoveAt(row * 2);
card.Children.Insert(row * 2,
(UIElement)ModBase.GetObjectFromXML(
"<Path xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:local=\"clr-namespace:PCL;assembly=Plain Craft Launcher 2\" Stretch=\"Uniform\" Tag=\"Waiting\" Data=\"F1 M5,0 a5,5 360 1 0 0,0.0001 m15,0 a5,5 360 1 0 0,0.0001 m15,0 a5,5 360 1 0 0,0.0001 Z\" Width=\"18\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"" +
row +
"\" Fill=\"{DynamicResource ColorBrush3}\" Margin=\"0,7,0,0\" VerticalAlignment=\"Top\" Height=\"6\"/>"));
}
break;
}
case ModBase.LoadState.Loading:
{
if ((string)((FrameworkElement)card.Children[row * 2]).Tag != "Loading")
{
card.Children.RemoveAt(row * 2);
card.Children.Insert(row * 2,
(UIElement)ModBase.GetObjectFromXML(
$"<TextBlock xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:local=\"clr-namespace:PCL;assembly=Plain Craft Launcher 2\" Text=\"{Lang.Number(SubTask.Progress, "P0")}\" Tag=\"Loading\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Foreground=\"{{DynamicResource ColorBrush3}}\"/>"));
}
else
{
((TextBlock)card.Children[row * 2]).Text =
$"{Lang.Number(SubTask.Progress, "P0")}";
}
break;
}
case ModBase.LoadState.Finished:
{
if ((string)((FrameworkElement)card.Children[row * 2]).Tag != "Finished")
{
card.Children.RemoveAt(row * 2);
card.Children.Insert(row * 2,
(UIElement)ModBase.GetObjectFromXML(
$"<Path xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:local=\"clr-namespace:PCL;assembly=Plain Craft Launcher 2\" Stretch=\"Uniform\" Tag=\"Finished\" Data=\"F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z\" Height=\"16\" Width=\"15\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Fill=\"{{DynamicResource ColorBrush3}}\" Margin=\"0,3,0,0\" VerticalAlignment=\"Top\"/>"));
}
break;
}
}
row += 1;
}
}
catch (Exception ex)
{
ModBase.Log(
ex,
$"刷新任务管理卡片 {loader.name} 失败",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
} while (false);
break;
}
#endregion
}
}
catch (Exception ex)
{
ModBase.Log(
ex,
$"更新任务管理显示失败({loader.State}",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
}
else if (!(loader.State == ModBase.LoadState.Aborted || loader.State == ModBase.LoadState.Finished))
{
try
{
#region 没有卡片且未中断或完成,添加新的卡片
var cardXAML = $@"
<local:MyCard xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:local=""clr-namespace:PCL;assembly=Plain Craft Launcher 2""
Tag=""{loader.Progress + (double)loader.State}"" Title=""{ModBase.EscapeXML(loader.name)}"" Margin=""0,0,0,15"">
<Grid Margin=""14,40,15,10"">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""50""/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>";
foreach (var SubTask in loaderList)
cardXAML += "<RowDefinition Height=\"26\"/>";
cardXAML += "</Grid.RowDefinitions>";
var row = 0;
foreach (var SubTask in loaderList)
{
switch (SubTask.State)
{
case ModBase.LoadState.Waiting:
{
cardXAML +=
$"<Path Stretch=\"Uniform\" Tag=\"Waiting\" Data=\"F1 M5,0 a5,5 360 1 0 0,0.0001 m15,0 a5,5 360 1 0 0,0.0001 m15,0 a5,5 360 1 0 0,0.0001 Z\" Width=\"18\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Fill=\"{{DynamicResource ColorBrush3}}\" Margin=\"0,7,0,0\" VerticalAlignment=\"Top\" Height=\"6\"/>";
break;
}
case ModBase.LoadState.Loading:
{
cardXAML += $"<TextBlock Text=\"{Lang.Number(SubTask.Progress, "P0")}\" Tag=\"Loading\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Foreground=\"{{DynamicResource ColorBrush3}}\" />";
break;
}
case ModBase.LoadState.Finished:
{
cardXAML +=
$"<Path Stretch=\"Uniform\" Tag=\"Finished\" Data=\"F1 M 23.7501,33.25L 34.8334,44.3333L 52.2499,22.1668L 56.9999,26.9168L 34.8334,53.8333L 19.0001,38L 23.7501,33.25 Z\" Height=\"16\" Width=\"15\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Fill=\"{{DynamicResource ColorBrush3}}\" Margin=\"0,3,0,0\" VerticalAlignment=\"Top\"/>";
break;
}
default:
{
cardXAML +=
$"<Path Stretch=\"Uniform\" Tag=\"Failed\" Data=\"F1 M2.5,0 L0,2.5 7.5,10 0,17.5 2.5,20 10,12.5 17.5,20 20,17.5 12.5,10 20,2.5 17.5,0 10,7.5 2.5,0Z\" Height=\"15\" Width=\"15\" HorizontalAlignment=\"Center\" Grid.Column=\"0\" Grid.Row=\"{row}\" Fill=\"{{DynamicResource ColorBrush3}}\" Margin=\"0,1,0,0\" VerticalAlignment=\"Top\"/>";
break;
}
}
cardXAML += $"<TextBlock Text=\"{ModBase.EscapeXML(SubTask.name)}\" HorizontalAlignment=\"Left\" Grid.Column=\"1\" Grid.Row=\"{row}\"/>";
row += 1;
}
cardXAML += "</Grid></local:MyCard>";
// 实例化控件
MyCard card;
try
{
card = (MyCard)ModBase.GetObjectFromXML(cardXAML);
}
catch (Exception ex)
{
ModBase.Log(ex, "新建任务管理卡片失败");
ModBase.Log($"出错的卡片内容:\r\n{cardXAML}");
throw;
}
ModMain.frmSpeedRight.PanMain.Children.Insert(0, card);
rightCards.Add(loader.name, card);
ModBase.Log($"[Watcher] 新建任务管理卡片:{loader.name}");
// 添加取消按钮
var cancel = new MyIconButton
{
Name = "BtnCancel",
SvgIcon = "lucide/x", Height = 25d, Width = 25d,
Margin = new Thickness(0d, 10d, 10d, 0d), LogoScale = 1.1d,
HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Top
};
card.Children.Add(cancel);
cancel.Click += (sender, e) =>
{
ModAnimation.AniDispose((MyIconButton)sender, false);
ModAnimation.AniDispose(card, true, _ =>
{
if (ModMain.frmSpeedRight.PanMain.Children.Count == 0 &&
ModMain.frmMain.pageCurrent == FormMain.PageType.TaskManager)
ModMain.frmMain.PageBack();
});
rightCards.Remove(loader.name);
ModLoader.loaderTaskbar.Remove(loader);
ModBase.Log($"[Taskbar] 关闭任务管理卡片:{loader.name},且移出任务列表");
ModBase.RunInThread(() => loader.Abort());
};
// 如果已经失败,再刷新一次,修改成失败的控件
if (loader.State == ModBase.LoadState.Failed)
{
card.Tag = null; // 避免重复导致刷新无效
TaskRefresh(loader);
}
}
#endregion
catch (Exception ex)
{
ModBase.Log(
ex,
"添加任务管理卡片失败",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
}
}
catch (Exception ex)
{
ModBase.Log(
ex,
"刷新任务管理显示失败",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Speed.Error.OperationFailed"));
}
}
public void TaskRemove(ModLoader.LoaderBase loader)
{
if (rightCards.ContainsKey(loader.name))
ModBase.RunInUiWait(() =>
{
// 移除已有的卡片
Grid card = rightCards[loader.name];
ModMain.frmSpeedRight.PanMain.Children.Remove(card);
rightCards.Remove(loader.name);
ModBase.Log($"[Watcher] 移除任务管理卡片:{loader.name}");
});
}
/// <summary>
/// 若没有任务,尝试返回主页。
/// </summary>
private void TryReturnToHome()
{
if (ModMain.frmSpeedRight.PanMain.Children.Count == 0 &&
ModMain.frmMain.pageCurrent == FormMain.PageType.TaskManager) ModMain.frmMain.PageBack();
}
}