PCL.Neo 是一个基于 .NET 9 和 Avalonia 框架开发的 Minecraft 启动器项目。项目采用分层架构设计,包含以下主要模块:
- PCL.Neo: UI 层,基于 Avalonia 框架的用户界面
- PCL.Neo.Core: 核心业务逻辑层,包含游戏启动、版本管理等核心功能
- PCL.Neo.Tests: 单元测试项目,确保代码质量和稳定性
- .NET 9 SDK 或更高版本
- Visual Studio 2022 或 JetBrains Rider 或 VS Code
- Git 版本控制工具
- GitHub Desktop 或其他 Git 客户端
- NuGet Package Manager
- 代码分析工具(如 SonarLint)
项目支持通过命令行进行构建,以下是标准的构建命令:
# 恢复 NuGet 包
dotnet restore
# Debug 构建(单文件发布,关闭代码裁剪)
dotnet publish PCL.Neo/PCL.Neo.csproj -c Debug -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/debug
# 运行测试
dotnet test PCL.Neo.Tests/# 恢复 NuGet 包
dotnet restore
# Release 构建(单文件发布,关闭代码裁剪)
dotnet publish PCL.Neo/PCL.Neo.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/release
# 运行测试
dotnet test PCL.Neo.Tests/ -c Release# Windows x64
dotnet publish PCL.Neo/PCL.Neo.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/win-x64
# Linux x64
dotnet publish PCL.Neo/PCL.Neo.csproj -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/linux-x64
# macOS x64
dotnet publish PCL.Neo/PCL.Neo.csproj -c Release -r osx-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/osx-x64- 单文件发布: 使用
PublishSingleFile=true将应用程序打包为单个可执行文件 - 关闭代码裁剪: 使用
PublishTrimmed=false确保所有依赖项都包含在内,避免运行时缺少程序集 - 自包含部署: 使用
--self-contained true包含 .NET 运行时,无需目标机器安装 .NET
构建完成后,请验证:
- 可执行文件能够正常启动
- 所有功能正常工作
- 没有缺少依赖项的错误
- 单元测试全部通过
PCL.Neo/
├── PCL.Neo/ # UI 层 - Avalonia 界面项目
├── PCL.Neo.Core/ # 核心层 - 业务逻辑和数据处理
│ ├── Models/ # 数据模型
│ ├── Services/ # 业务服务
│ ├── Utils/ # 工具类
│ └── ...
├── PCL.Neo.Tests/ # 测试项目
├── docs/ # 文档
└── README.md
遵循 Microsoft C# 命名约定:
- 类名: PascalCase(如
GameLauncher) - 方法名: PascalCase(如
LaunchAsync) - 属性名: PascalCase(如
RootDirectory) - 字段名: camelCase,私有字段使用下划线前缀(如
_isIndie) - 常量: PascalCase(如
DefaultTimeout) - 接口: 以
I开头的 PascalCase(如IGameLauncher)
项目使用 EditorConfig 统一代码风格,主要规范包括:
- 缩进: 4 个空格,不使用制表符
- 换行: 使用 CRLF(Windows 风格)
- 括号: 所有控制结构都使用大括号,大括号另起一行
- 空格: 在操作符前后添加空格,逗号后添加空格
- 文件编码: UTF-8
如果需要忽略异常请使用try-catch块捕获后在需要忽略的部分使用注释说明
不要随意捕获异常,没有必要的捕获请删除
如果需要日志记录请使用Utils中的Logger,并将异常也传入Logger的参数中
// 好的示例
public async Task<Process> LaunchAsync(GameProfile profile)
{
if (profile == null)
throw new ArgumentNullException(nameof(profile));
string mcDir = profile.Information.RootDirectory;
if (!Directory.Exists(mcDir))
{
throw new DirectoryNotFoundException($"Minecraft root directory not found. {mcDir}");
}
// ... 其他逻辑
}
public int Foo()
{
try
{
// do stuff...
}
catch (IOException ex)
{
Logger.Error("Catched exception.", ex);
// do stuff...
}
catch (Exception)
{
// 忽略异常
}
}将所有的有关空值的Warning视为Error,正确地处理空值,谨防NullRefreenceEexception
// 使用 null 条件运算符和 ArgumentNullException.ThrowIfNull
ArgumentNullException.ThrowIfNull(versionManifes.Libraries);
// 使用 null 合并运算符
var assetIndex = versionManifes.AssetIndex?.Id ?? "legacy";应使用IDisposable进行资源释放,尽量减少Finalizer的使用
// 使用 using 语句确保资源释放
using var fileStream = new FileStream(path, FileMode.Open);
// 或者
using (var process = new Process())
{
// 处理逻辑
}// 正确使用 async/await
public async Task<VersionManifes> GetVersionAsync(string versionId)
{
var result = await SomeAsyncOperation();
return result;
}
// 避免异步方法的阻塞调用
// 错误: SomeAsyncMethod().Result
// 正确: await SomeAsyncMethod()对于注释请使用//+空格+内容的形式
对于公开的方法请添加XML注释以便于后续使用,如果方法有抛出异常请使用exception块说明
/// <summary>
/// 启动游戏
/// </summary>
/// <param name="profile">游戏配置文件</param>
/// <returns>游戏进程</returns>
/// <exception cref="DirectoryNotFoundException">当游戏目录不存在时抛出</exception>
public async Task<Process> LaunchAsync(GameProfile profile)
{
// 实现逻辑
}如果是为完成的部分请添加// TODO块,便于IDE智能查找
// 确保目录存在
if (!Directory.Exists(mcDir))
{
throw new DirectoryNotFoundException($"Minecraft root directory not found. {mcDir}");
}
// TODO: 从配置文件加载版本信息
var launcherVersion = "1.0.0";测试使用NUnit 4.3.2框架 PCL.Neo.Core中的测试代码请放到Core文件夹中
- 测试覆盖率: 核心业务逻辑测试覆盖率应达到 80% 以上
- 测试命名: 使用
方法名_测试场景_期望结果格式 - 测试结构: 使用 AAA 模式(Arrange, Act, Assert)
[Test]
public async Task LaunchAsync_ValidProfile_ReturnsProcess()
{
// Arrange
var profile = new GameProfile
{
Information = new GameInfo
{
RootDirectory = @"C:\Test\.minecraft",
GameDirectory = @"C:\Test\.minecraft"
}
};
var launcher = new GameLauncher();
// Act
var result = await launcher.LaunchAsync(profile);
// Assert
Assert.IsNotNull(result);
Assert.IsInstanceOf<Process>(result);
}- 单元测试: 测试单个方法或类的功能
- 集成测试: 测试多个组件之间的交互
- UI 测试: 测试用户界面功能(使用 Avalonia 测试框架)
采用 Git Flow 分支模型:
- main: 主分支,包含生产就绪的代码
- develop: 开发分支,包含下一个发布版本的功能
- feature/: 功能分支,用于开发新功能
- hotfix/: 热修复分支,用于紧急修复
- release/: 发布分支,用于准备新版本发布
使用语义化提交信息格式:
<类型>[可选的作用域]: <描述>
[可选的正文]
[可选的脚注]
类型说明:
feat: 新功能fix: 修复 bugdocs: 文档更新style: 代码格式调整(不影响功能)refactor: 代码重构test: 添加或修改测试chore: 构建过程或辅助工具的变动
示例:
feat(launcher): 添加游戏启动前的版本检查功能
- 添加版本兼容性检查
- 优化启动参数构建逻辑
- 增加错误处理和用户提示
Closes #123
[类型] 简短描述
# 前言
简要描述这个 PR 的目的和背景。
# 引入的 NuGet 包
如果引入了新的 NuGet 包,请列出:
- 包名:简要说明用途
- 包名:简要说明用途
# 更改内容
详细列出主要更改:
- 添加了 XXX 功能
- 修复了 XXX 问题
- 重构了 XXX 模块
- 优化了 XXX 性能
# 使用说明
如果涉及 API 更改或新功能,请提供使用示例:
```csharp
// 示例代码
var launcher = new GameLauncher();
var process = await launcher.LaunchAsync(profile);说明如何测试这些更改:
- 单元测试已通过
- 集成测试已通过
- 手动测试已完成
如果有破坏性更改,请详细说明:
- 更改的 API
- 迁移指南
- 影响范围
关闭或关联的 Issue:
- Closes #123
- Relates to #456
最后,感谢你对 PCL.Neo 项目的贡献!欢迎多多提交PR!