初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态改变事件
|
||||
/// <param name="state">当前状态,将影响日志和 UI 显示效果</param>
|
||||
/// <param name="message">状态消息</param>
|
||||
/// </summary>
|
||||
public delegate void TaskStateEvent(TaskState state, string message);
|
||||
|
||||
/// <summary>
|
||||
/// 响应式任务接口<br/>
|
||||
/// <b>NOTE</b>: 为确保运行时响应式模型的 hash 映射正常工作,若无特殊需求,请勿重写对象相等性实现如
|
||||
/// <see cref="object.GetHashCode"/> 与 <see cref="object.Equals(object)"/>!
|
||||
/// </summary>
|
||||
public interface ITask
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务标题
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行任务
|
||||
/// </summary>
|
||||
/// <param name="cancelToken">取消令牌</param>
|
||||
public Task ExecuteAsync(CancellationToken cancelToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态改变事件
|
||||
/// </summary>
|
||||
public event TaskStateEvent StateChanged;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 用于取消实现的接口
|
||||
/// </summary>
|
||||
public interface ITaskCancelable
|
||||
{
|
||||
public void Cancel();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
public delegate void TaskGroupEvent(ITask task);
|
||||
|
||||
public interface ITaskGroup : ITask
|
||||
{
|
||||
public event TaskGroupEvent AddTask;
|
||||
public event TaskGroupEvent RemoveTask;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 用于暂停实现的接口
|
||||
/// </summary>
|
||||
public interface ITaskPausable
|
||||
{
|
||||
public void Pause();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 任务进度改变事件
|
||||
/// </summary>
|
||||
/// <param name="progress">0.0 ~ 1.0 之间的浮点数,表示任务进度</param>
|
||||
public delegate void TaskProgressEvent(double progress);
|
||||
|
||||
/// <summary>
|
||||
/// 可观察进度的任务模型
|
||||
/// </summary>
|
||||
public interface ITaskProgressive
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务进度改变事件
|
||||
/// </summary>
|
||||
public event TaskProgressEvent ProgressChanged;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using PCL.Core.Logging;
|
||||
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 任务中心,用于管理任务
|
||||
/// </summary>
|
||||
public static class TaskCenter
|
||||
{
|
||||
/// <summary>
|
||||
/// 可观察的任务模型集合
|
||||
/// </summary>
|
||||
public static ObservableCollection<TaskModel> Tasks { get; } = [];
|
||||
|
||||
private static readonly ConditionalWeakTable<ITask, TaskModel> _ModelMap = [];
|
||||
|
||||
private static TaskModel _InitModel(ITask instance)
|
||||
{
|
||||
// ReSharper disable SuspiciousTypeConversion.Global
|
||||
var cancelable = instance as ITaskCancelable;
|
||||
var pausable = instance as ITaskPausable;
|
||||
var progressive = instance as ITaskProgressive;
|
||||
// ReSharper restore SuspiciousTypeConversion.Global
|
||||
|
||||
var model = new TaskModel
|
||||
{
|
||||
Title = instance.Title,
|
||||
SupportProgress = progressive is not null,
|
||||
OnCancel = cancelable is null ? null : (() => cancelable.Cancel()),
|
||||
OnPause = pausable is null ? null : (() => pausable.Pause()),
|
||||
};
|
||||
|
||||
// state event
|
||||
instance.StateChanged += (state, message) =>
|
||||
{
|
||||
LogWrapper.Trace("TaskCenter", $"{instance.Title}: state changed ({state}): {message}");
|
||||
model.State = state;
|
||||
model.StateMessage = message;
|
||||
};
|
||||
|
||||
// progress event
|
||||
if (progressive is not null)
|
||||
{
|
||||
progressive.ProgressChanged += progress =>
|
||||
{
|
||||
model.Progress = Math.Clamp(progress, 0.0, 1.0);
|
||||
};
|
||||
}
|
||||
|
||||
// group events
|
||||
if (instance is ITaskGroup group)
|
||||
{
|
||||
group.AddTask += task =>
|
||||
{
|
||||
var taskModel = _InitModel(task);
|
||||
_ModelMap.Add(task, taskModel);
|
||||
model.Children.Add(taskModel);
|
||||
};
|
||||
group.RemoveTask += task =>
|
||||
{
|
||||
if (_ModelMap.TryGetValue(task, out var taskModel)) model.Children.Remove(taskModel);
|
||||
};
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册响应式任务实例
|
||||
/// </summary>
|
||||
/// <param name="instance">任务实例</param>
|
||||
/// <param name="start">是否立即启动该实例</param>
|
||||
public static void Register(ITask instance, bool start = true)
|
||||
{
|
||||
var model = _InitModel(instance);
|
||||
Tasks.Add(model);
|
||||
|
||||
if (start)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try { await instance.ExecuteAsync(); }
|
||||
catch (OperationCanceledException) { /* ignoring */ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogWrapper.Warn(ex, "TaskCenter", $"{instance.Title}: exception thrown");
|
||||
model.State = TaskState.Failed;
|
||||
model.StateMessage = ex.Message;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除所有已结束的任务
|
||||
/// </summary>
|
||||
public static void RemoveFinished()
|
||||
{
|
||||
foreach (var model in Tasks.Where(x => x.State > TaskState.Running).ToList())
|
||||
Tasks.Remove(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 可观察的任务模型<br/>
|
||||
/// <b>NOTE</b>: 请勿自行修改任何 observable 属性
|
||||
/// </summary>
|
||||
public partial class TaskModel : ObservableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务标题
|
||||
/// </summary>
|
||||
public required string Title { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务是否支持进度
|
||||
/// </summary>
|
||||
public required bool SupportProgress { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务当前状态
|
||||
/// </summary>
|
||||
[ObservableProperty] private TaskState _state = TaskState.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 任务当前状态信息
|
||||
/// </summary>
|
||||
[ObservableProperty] private string _stateMessage = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 任务当前进度,<see cref="SupportProgress"/> 为 <see langword="true"/> 时有效
|
||||
/// </summary>
|
||||
[ObservableProperty] private double _progress = 0.0;
|
||||
|
||||
private static readonly Action _EmptyAction = (static () => {});
|
||||
|
||||
/// <summary>
|
||||
/// 取消任务时触发的事件,值为 <see langword="null"/> 表示不支持取消
|
||||
/// </summary>
|
||||
public required Action? OnCancel { private get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 取消任务命令
|
||||
/// </summary>
|
||||
public RelayCommand Cancel
|
||||
{
|
||||
get => field ??= new RelayCommand(OnCancel ?? _EmptyAction, () => OnCancel is not null);
|
||||
} = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 暂停任务时触发的事件,值为 <see langword="null"/> 表示不支持暂停
|
||||
/// </summary>
|
||||
public required Action? OnPause { private get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 暂停任务命令
|
||||
/// </summary>
|
||||
public RelayCommand Pause
|
||||
{
|
||||
get => field ??= new RelayCommand(OnPause ?? _EmptyAction, () => OnPause is not null);
|
||||
} = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 任务是否为任务组,即是否存在子任务
|
||||
/// </summary>
|
||||
[ObservableProperty] private bool _isGroup;
|
||||
|
||||
/// <summary>
|
||||
/// 子任务模型
|
||||
/// </summary>
|
||||
public ObservableCollection<TaskModel> Children { get; } = [];
|
||||
|
||||
public TaskModel()
|
||||
{
|
||||
Children.CollectionChanged += (sender, _) =>
|
||||
{
|
||||
if (sender is ObservableCollection<TaskModel> c) IsGroup = c.Count > 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace PCL.Core.App.Tasks;
|
||||
|
||||
public enum TaskState
|
||||
{
|
||||
Waiting,
|
||||
Running,
|
||||
Success,
|
||||
Canceled,
|
||||
Failed
|
||||
}
|
||||
Reference in New Issue
Block a user