Skip to content

feat(#4): Create new empty Mod project#51

Open
ModerRAS wants to merge 1 commit intomasterfrom
feature/issue-4-create-new-mod
Open

feat(#4): Create new empty Mod project#51
ModerRAS wants to merge 1 commit intomasterfrom
feature/issue-4-create-new-mod

Conversation

@ModerRAS
Copy link
Copy Markdown
Contributor

Summary

  • Create IModProjectService interface
  • Implement ModProjectService with BUTR template support
  • Generate SubModule.xml, .csproj, and ModuleData folder
  • Validate mod names and generate mod IDs
  • Add comprehensive unit tests

Changes

  • BannerlordModEditor.Common/Services/ModProjectService.cs - Implementation
  • BannerlordModEditor.Common.Tests/Services/ModProjectServiceTests.cs - Unit tests

Closes #4

- Create IModProjectService interface
- Implement ModProjectService with BUTR template support
- Generate SubModule.xml, .csproj, and ModuleData folder
- Validate mod names and generate mod IDs
- Add comprehensive unit tests

Closes #4
Copilot AI review requested due to automatic review settings March 20, 2026 01:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new service to generate an “empty mod project” skeleton (SubModule.xml, .csproj, ModuleData, etc.) with basic validation and corresponding unit tests.

Changes:

  • Introduces IModProjectService plus ModProjectService implementation to scaffold a mod project on disk.
  • Adds name validation + mod id generation logic and writes template files/folders (including optional BUTR package references).
  • Adds xUnit tests covering validation and key project creation paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
BannerlordModEditor.Common/Services/ModProjectService.cs Implements mod project scaffolding, validation, mod-id generation, and csproj templating.
BannerlordModEditor.Common.Tests/Services/ModProjectServiceTests.cs Adds xUnit coverage for validation, project creation, and SubModule.xml generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (!ValidateModName(options.ModName))
{
result.Success = false;
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, and underscores.";
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The validation error message is inconsistent with the actual validation rules: ValidateModName currently allows -, but the message says “underscores” only (and it also doesn’t mention the length constraints). Update the message to reflect the real rules or adjust the validator to match the message.

Suggested change
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, and underscores.";
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, underscores, and hyphens, and ensure it meets the required length constraints.";

Copilot uses AI. Check for mistakes.
if (modName.Length < 2 || modName.Length > 50)
return false;

return modName.All(c => char.IsLetterOrDigit(c) || c == '_' || c == '-');
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The validation error message is inconsistent with the actual validation rules: ValidateModName currently allows -, but the message says “underscores” only (and it also doesn’t mention the length constraints). Update the message to reflect the real rules or adjust the validator to match the message.

Suggested change
return modName.All(c => char.IsLetterOrDigit(c) || c == '_' || c == '-');
return modName.All(c => char.IsLetterOrDigit(c) || c == '_');

Copilot uses AI. Check for mistakes.
{
public bool Success { get; set; }
public string ProjectPath { get; set; } = string.Empty;
public List<string> CreatedFiles { get; set; } = new();
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

CreatedFiles is currently used to store both file paths and directory paths (e.g., ModuleData). This makes the API ambiguous for callers. Consider either (a) renaming to something like CreatedPaths, or (b) splitting into CreatedFiles and CreatedDirectories (or similar) so consumers can reliably interpret the result.

Suggested change
public List<string> CreatedFiles { get; set; } = new();
public List<string> CreatedPaths { get; set; } = new();
[Obsolete("Use CreatedPaths instead. This property will be removed in a future version.")]
public List<string> CreatedFiles
{
get => CreatedPaths;
set => CreatedPaths = value ?? new();
}

Copilot uses AI. Check for mistakes.
{
var moduleDataPath = Path.Combine(projectPath, "ModuleData");
Directory.CreateDirectory(moduleDataPath);
result.CreatedFiles.Add(moduleDataPath);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

CreatedFiles is currently used to store both file paths and directory paths (e.g., ModuleData). This makes the API ambiguous for callers. Consider either (a) renaming to something like CreatedPaths, or (b) splitting into CreatedFiles and CreatedDirectories (or similar) so consumers can reliably interpret the result.

Suggested change
result.CreatedFiles.Add(moduleDataPath);

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +120
var binPath = Path.Combine(projectPath, "bin");
Directory.CreateDirectory(binPath);

var objPath = Path.Combine(projectPath, "obj");
Directory.CreateDirectory(objPath);

Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Pre-creating bin/ and obj/ in a project template is generally undesirable because these are build output directories and can confuse tooling/users (and may create noise in scaffolding results). Prefer not creating them at scaffold time; keep the .gitignore entries and let the build generate these folders.

Suggested change
var binPath = Path.Combine(projectPath, "bin");
Directory.CreateDirectory(binPath);
var objPath = Path.Combine(projectPath, "obj");
Directory.CreateDirectory(objPath);

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +51
public async Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return await Task.Run(() => CreateModProject(options));
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

Wrapping the synchronous, IO-heavy scaffolding in Task.Run will consume a thread-pool thread for the entire duration and doesn’t provide true async IO. Prefer either (1) making the method synchronous-only, or (2) implementing a genuinely async version that uses async file APIs (e.g., WriteAllTextAsync / async stream usage) and avoids Task.Run.

Suggested change
public async Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return await Task.Run(() => CreateModProject(options));
public Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return Task.FromResult(CreateModProject(options));

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +165
var packageReferences = useButrTemplate
? @"
<PackageReference Include=""Bannerlord.ButterLib"" Version=""2.*"" />
<PackageReference Include=""Bannerlord.Harmony"" Version=""2.*"" />"
: "";
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

There’s a new behavioral branch controlled by UseButrTemplate that changes the generated .csproj content, but the tests don’t currently assert that these conditional package references are present/absent. Add a test that creates a project with UseButrTemplate = false and verifies the generated .csproj does not contain the BUTR references (and optionally one that asserts they are present when true).

Copilot uses AI. Check for mistakes.
var options = new ModProjectOptions
{
ModName = "A",
OutputDirectory = "/tmp"
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The test hard-codes "/tmp" as the output directory, which will fail on Windows environments. Use Path.GetTempPath() (optionally combined with a GUID subfolder) to keep the test cross-platform and consistent with the other tests in this file.

Suggested change
OutputDirectory = "/tmp"
OutputDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 实现新建空Mod的功能

2 participants