Skip to content

Commit a17639c

Browse files
authored
Merge pull request PCL-Community#140 from whitecat346/docs/pr_guild
Docs 贡献指南
2 parents e5d9a28 + 1e8db7a commit a17639c

File tree

2 files changed

+360
-1
lines changed

2 files changed

+360
-1
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
</div>
1414

15-
基于 Avalonia 的下一代跨平台启动器,由 [PCL2](https://github.com/Hex-Dragon/PCL2)(作者:龙腾猫跃) 项目重构。\
15+
基于 Avalonia 的下一代跨平台启动器,由 [PCL2](https://github.com/Meloong-Git/PCL)(作者:龙腾猫跃) 项目重构。\
1616
使用最新的 .NET 9 强力驱动,支持 Windows、Linux 和 macOS。\
1717
原生运行,自然快!
1818

@@ -22,5 +22,8 @@
2222
- **原生运行**:PCL.Neo 是原生自绘控件,自然快!
2323
- **无付费功能**:PCL.Neo 的任何功能都不需要**付费**
2424

25+
## 贡献指南
26+
如果你想为此项目做出贡献,请参考[这里](docs/pcl_neo_contribution_guide.md)
27+
2528
## 协议声明
2629
PCL.Neo.Core 使用 **MIT license**,而主项目使用与主线相同的协议。因此你可以放心使用 PCL.Neo.Core 用于你的项目。

docs/pcl_neo_contribution_guide.md

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
# PCL.Neo 贡献指南与开发规范
2+
3+
## 项目概述
4+
5+
PCL.Neo 是一个基于 .NET 9 和 Avalonia 框架开发的 Minecraft 启动器项目。项目采用分层架构设计,包含以下主要模块:
6+
7+
- **PCL.Neo**: UI 层,基于 Avalonia 框架的用户界面
8+
- **PCL.Neo.Core**: 核心业务逻辑层,包含游戏启动、版本管理等核心功能
9+
- **PCL.Neo.Tests**: 单元测试项目,确保代码质量和稳定性
10+
11+
## 开发环境要求
12+
13+
### 系统要求
14+
- .NET 9 SDK 或更高版本
15+
- Visual Studio 2022 或 JetBrains Rider 或 VS Code
16+
- Git 版本控制工具
17+
18+
### 推荐工具
19+
- GitHub Desktop 或其他 Git 客户端
20+
- NuGet Package Manager
21+
- 代码分析工具(如 SonarLint)
22+
23+
## 构建指令
24+
25+
### 命令行构建
26+
27+
项目支持通过命令行进行构建,以下是标准的构建命令:
28+
29+
#### Debug 构建
30+
```bash
31+
# 恢复 NuGet 包
32+
dotnet restore
33+
34+
# Debug 构建(单文件发布,关闭代码裁剪)
35+
dotnet publish PCL.Neo/PCL.Neo.csproj -c Debug -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/debug
36+
37+
# 运行测试
38+
dotnet test PCL.Neo.Tests/
39+
```
40+
41+
#### Release 构建
42+
```bash
43+
# 恢复 NuGet 包
44+
dotnet restore
45+
46+
# Release 构建(单文件发布,关闭代码裁剪)
47+
dotnet publish PCL.Neo/PCL.Neo.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PublishTrimmed=false -o ./publish/release
48+
49+
# 运行测试
50+
dotnet test PCL.Neo.Tests/ -c Release
51+
```
52+
53+
#### 跨平台构建
54+
```bash
55+
# Windows x64
56+
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
57+
58+
# Linux x64
59+
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
60+
61+
# macOS x64
62+
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
63+
```
64+
65+
### 构建要求
66+
- **单文件发布**: 使用 `PublishSingleFile=true` 将应用程序打包为单个可执行文件
67+
- **关闭代码裁剪**: 使用 `PublishTrimmed=false` 确保所有依赖项都包含在内,避免运行时缺少程序集
68+
- **自包含部署**: 使用 `--self-contained true` 包含 .NET 运行时,无需目标机器安装 .NET
69+
70+
### 验证构建
71+
构建完成后,请验证:
72+
- [ ] 可执行文件能够正常启动
73+
- [ ] 所有功能正常工作
74+
- [ ] 没有缺少依赖项的错误
75+
- [ ] 单元测试全部通过
76+
77+
## 项目结构
78+
79+
```
80+
PCL.Neo/
81+
├── PCL.Neo/ # UI 层 - Avalonia 界面项目
82+
├── PCL.Neo.Core/ # 核心层 - 业务逻辑和数据处理
83+
│ ├── Models/ # 数据模型
84+
│ ├── Services/ # 业务服务
85+
│ ├── Utils/ # 工具类
86+
│ └── ...
87+
├── PCL.Neo.Tests/ # 测试项目
88+
├── docs/ # 文档
89+
└── README.md
90+
```
91+
92+
## 代码规范
93+
94+
### 命名约定
95+
96+
遵循 Microsoft C# 命名约定:
97+
98+
- **类名**: PascalCase(如 `GameLauncher`
99+
- **方法名**: PascalCase(如 `LaunchAsync`
100+
- **属性名**: PascalCase(如 `RootDirectory`
101+
- **字段名**: camelCase,私有字段使用下划线前缀(如 `_isIndie`
102+
- **常量**: PascalCase(如 `DefaultTimeout`
103+
- **接口**: 以 `I` 开头的 PascalCase(如 `IGameLauncher`
104+
105+
### 代码风格
106+
107+
项目使用 EditorConfig 统一代码风格,主要规范包括:
108+
109+
- **缩进**: 4 个空格,不使用制表符
110+
- **换行**: 使用 CRLF(Windows 风格)
111+
- **括号**: 所有控制结构都使用大括号,大括号另起一行
112+
- **空格**: 在操作符前后添加空格,逗号后添加空格
113+
- **文件编码**: UTF-8
114+
115+
### 代码质量要求
116+
117+
#### 1. 异常处理
118+
如果需要忽略异常请使用`try-catch`块捕获后在需要忽略的部分使用注释说明
119+
不要随意捕获异常,没有必要的捕获请删除
120+
如果需要日志记录请使用`Utils`中的`Logger`,并将异常也传入`Logger`的参数中
121+
```csharp
122+
// 好的示例
123+
public async Task<Process> LaunchAsync(GameProfile profile)
124+
{
125+
if (profile == null)
126+
throw new ArgumentNullException(nameof(profile));
127+
128+
string mcDir = profile.Information.RootDirectory;
129+
if (!Directory.Exists(mcDir))
130+
{
131+
throw new DirectoryNotFoundException($"Minecraft root directory not found. {mcDir}");
132+
}
133+
134+
// ... 其他逻辑
135+
}
136+
137+
public int Foo()
138+
{
139+
try
140+
{
141+
// do stuff...
142+
}
143+
catch (IOException ex)
144+
{
145+
Logger.Error("Catched exception.", ex);
146+
// do stuff...
147+
}
148+
catch (Exception)
149+
{
150+
// 忽略异常
151+
}
152+
}
153+
```
154+
155+
#### 2. 空值检查
156+
将所有的有关空值的Warning视为Error,正确地处理空值,谨防`NullRefreenceEexception`
157+
```csharp
158+
// 使用 null 条件运算符和 ArgumentNullException.ThrowIfNull
159+
ArgumentNullException.ThrowIfNull(versionManifes.Libraries);
160+
161+
// 使用 null 合并运算符
162+
var assetIndex = versionManifes.AssetIndex?.Id ?? "legacy";
163+
```
164+
165+
#### 3. 资源管理
166+
应使用`IDisposable`进行资源释放,尽量减少`Finalizer`的使用
167+
```csharp
168+
// 使用 using 语句确保资源释放
169+
using var fileStream = new FileStream(path, FileMode.Open);
170+
// 或者
171+
using (var process = new Process())
172+
{
173+
// 处理逻辑
174+
}
175+
```
176+
177+
#### 4. 异步编程
178+
```csharp
179+
// 正确使用 async/await
180+
public async Task<VersionManifes> GetVersionAsync(string versionId)
181+
{
182+
var result = await SomeAsyncOperation();
183+
return result;
184+
}
185+
186+
// 避免异步方法的阻塞调用
187+
// 错误: SomeAsyncMethod().Result
188+
// 正确: await SomeAsyncMethod()
189+
```
190+
191+
### 注释规范
192+
对于注释请使用`//+空格+内容`的形式
193+
#### 1. XML 文档注释
194+
对于公开的方法请添加XML注释以便于后续使用,如果方法有抛出异常请使用`exception`块说明
195+
```csharp
196+
/// <summary>
197+
/// 启动游戏
198+
/// </summary>
199+
/// <param name="profile">游戏配置文件</param>
200+
/// <returns>游戏进程</returns>
201+
/// <exception cref="DirectoryNotFoundException">当游戏目录不存在时抛出</exception>
202+
public async Task<Process> LaunchAsync(GameProfile profile)
203+
{
204+
// 实现逻辑
205+
}
206+
```
207+
208+
#### 2. 行内注释
209+
如果是为完成的部分请添加`// TODO`块,便于IDE智能查找
210+
```csharp
211+
// 确保目录存在
212+
if (!Directory.Exists(mcDir))
213+
{
214+
throw new DirectoryNotFoundException($"Minecraft root directory not found. {mcDir}");
215+
}
216+
217+
// TODO: 从配置文件加载版本信息
218+
var launcherVersion = "1.0.0";
219+
```
220+
221+
## 测试规范
222+
测试使用NUnit 4.3.2框架
223+
PCL.Neo.Core中的测试代码请放到Core文件夹中
224+
### 单元测试要求
225+
226+
1. **测试覆盖率**: 核心业务逻辑测试覆盖率应达到 80% 以上
227+
2. **测试命名**: 使用 `方法名_测试场景_期望结果` 格式
228+
3. **测试结构**: 使用 AAA 模式(Arrange, Act, Assert)
229+
230+
```csharp
231+
[Test]
232+
public async Task LaunchAsync_ValidProfile_ReturnsProcess()
233+
{
234+
// Arrange
235+
var profile = new GameProfile
236+
{
237+
Information = new GameInfo
238+
{
239+
RootDirectory = @"C:\Test\.minecraft",
240+
GameDirectory = @"C:\Test\.minecraft"
241+
}
242+
};
243+
var launcher = new GameLauncher();
244+
245+
// Act
246+
var result = await launcher.LaunchAsync(profile);
247+
248+
// Assert
249+
Assert.IsNotNull(result);
250+
Assert.IsInstanceOf<Process>(result);
251+
}
252+
```
253+
254+
### 测试分类
255+
256+
- **单元测试**: 测试单个方法或类的功能
257+
- **集成测试**: 测试多个组件之间的交互
258+
- **UI 测试**: 测试用户界面功能(使用 Avalonia 测试框架)
259+
260+
## Git 工作流程
261+
262+
### 分支策略
263+
264+
采用 Git Flow 分支模型:
265+
266+
- **main**: 主分支,包含生产就绪的代码
267+
- **develop**: 开发分支,包含下一个发布版本的功能
268+
- **feature/**: 功能分支,用于开发新功能
269+
- **hotfix/**: 热修复分支,用于紧急修复
270+
- **release/**: 发布分支,用于准备新版本发布
271+
272+
### 提交规范
273+
274+
使用语义化提交信息格式:
275+
276+
```
277+
<类型>[可选的作用域]: <描述>
278+
279+
[可选的正文]
280+
281+
[可选的脚注]
282+
```
283+
284+
**类型说明**:
285+
- `feat`: 新功能
286+
- `fix`: 修复 bug
287+
- `docs`: 文档更新
288+
- `style`: 代码格式调整(不影响功能)
289+
- `refactor`: 代码重构
290+
- `test`: 添加或修改测试
291+
- `chore`: 构建过程或辅助工具的变动
292+
293+
**示例**:
294+
```
295+
feat(launcher): 添加游戏启动前的版本检查功能
296+
297+
- 添加版本兼容性检查
298+
- 优化启动参数构建逻辑
299+
- 增加错误处理和用户提示
300+
301+
Closes #123
302+
```
303+
304+
## Pull Request 规范
305+
306+
### PR 标题格式
307+
```
308+
[类型] 简短描述
309+
```
310+
311+
### PR 描述模板
312+
313+
```markdown
314+
# 前言
315+
简要描述这个 PR 的目的和背景。
316+
317+
# 引入的 NuGet 包
318+
如果引入了新的 NuGet 包,请列出:
319+
- 包名:简要说明用途
320+
- 包名:简要说明用途
321+
322+
# 更改内容
323+
详细列出主要更改:
324+
- 添加了 XXX 功能
325+
- 修复了 XXX 问题
326+
- 重构了 XXX 模块
327+
- 优化了 XXX 性能
328+
329+
# 使用说明
330+
如果涉及 API 更改或新功能,请提供使用示例:
331+
332+
```csharp
333+
// 示例代码
334+
var launcher = new GameLauncher();
335+
var process = await launcher.LaunchAsync(profile);
336+
```
337+
338+
# 测试
339+
说明如何测试这些更改:
340+
- [ ] 单元测试已通过
341+
- [ ] 集成测试已通过
342+
- [ ] 手动测试已完成
343+
344+
# 破坏性更改
345+
如果有破坏性更改,请详细说明:
346+
- 更改的 API
347+
- 迁移指南
348+
- 影响范围
349+
350+
# 相关 Issue
351+
关闭或关联的 Issue:
352+
- Closes #123
353+
- Relates to #456
354+
355+
```
356+
最后,感谢你对 PCL.Neo 项目的贡献!欢迎多多提交PR!

0 commit comments

Comments
 (0)