Skip to content

feat(tools): integrate ModBuilder suite for mod project management and asset building - #376

Open
undead2146 wants to merge 76 commits into
developmentfrom
feat/modbuilder
Open

feat(tools): integrate ModBuilder suite for mod project management and asset building#376
undead2146 wants to merge 76 commits into
developmentfrom
feat/modbuilder

Conversation

@undead2146

@undead2146 undead2146 commented Aug 16, 2026

Copy link
Copy Markdown
Member

Overview

This PR integrates ModBuilder into GenHub as a tool plugin suite for creating, configuring, converting, and packaging Command & Conquer Generals / Zero Hour mods and custom content.

In addition, this branch has been updated and synchronized with upstream/development, resolving merge conflicts and adapting service dependencies to the latest architectural standards.


Changes

1. ModBuilder Core Architecture & Services

  • Project Configuration (IProjectConfigService):
    • Full lifecycle management for .mbproj project files, project structure generation, recent projects history, and templates.
    • Injected IConfigurationProviderService to ensure storage location and user-relocated data directory conventions are honored.
  • Build Engine & Pipeline (IBuildEngineService):
    • Multi-threaded, incremental build pipeline with cancellation support, progress reporting, and detailed stage metrics.
    • Pre-build and post-build task execution, asset dependency analysis, and packaging orchestration.
  • Asset Processing & Conversion:
    • ImageConversionService: TGA/PNG/DDS texture conversions, compression, mipmap generation, and format validation using Magick.NET.
    • StringTableConversionService: CSF string table compiling, decompiling, encoding validation, and parsing.
    • TextProcessingService & FileConversionService: INI and script processing, macro replacement, and format transformation.
    • ArchiveService: BIG archive creation, packing, verification, and compression.
  • External Tool Execution & Caching:
    • ExternalToolService: Safe, throttled external process execution (crunch, gametextcompiler, blender) with cross-platform shell and binary invocation.
    • BuildCacheService & FileHashRegistryService: Content hash tracking, incremental build cache validation, and cache invalidation.
  • Plugin System & Dependency Injection:
    • ModBuilderToolPlugin: Implements IToolPlugin for dynamic tool discovery in the GenHub tools registry.
    • ModBuilderModule: Service registration module for core services, view models, and conversion providers.

2. UI & ViewModels (Avalonia & MVVM)

  • Views & Panels:
    • ModBuilderView: Main host view with project dashboard, file explorer, pack editor, and settings.
    • ProjectDashboardView: Project overview, metrics, quick actions, and recent projects launcher.
    • FileManagerPanel & FileTreeItem: Hierarchical asset explorer with file type icons, status indicators, and context actions.
    • BundlePackEditorDialog & ConfigEditorDialog: Visual editors for mod bundle items and pack configurations.
    • BuildProgressOverlay, ProgressCard, and MetricDisplay: Interactive build progress visualization and live telemetry.
  • ViewModels:
    • ModBuilderViewModel, ProjectDashboardViewModel, FileManagerViewModel, BundlePackEditorViewModel, BundleItemEditorViewModel, ConfigEditorViewModel, BuildProgressViewModel, SettingsPanelViewModel.

3. Upstream Synchronization & Conflict Resolution

  • Merged upstream development branch changes into feat/modbuilder.
  • Resolved project configuration conflicts in GenHub.sln to include GenHub.MacOS, GenHub.Benchmarks, GenHub.Tests.MacOS, and GenHub.Tests.Performance.
  • Resolved XML doc comment merge conflicts in GameProfileSettingsViewModel.cs.
  • Updated ExternalToolServiceTests with cross-platform script creation to execute cleanly across Windows, Linux, and macOS.

Technical Details

  • Built for .NET 8, Avalonia UI, and CommunityToolkit.Mvvm.
  • Adheres to C# 12 primary constructors, clean architecture separation, and StyleCop analyzers.
  • Comprehensive cancellation token propagation and async/await task handling throughout build and conversion pipelines.

Testing

  • GenHub.Tests.Core: 1,728 tests passed (0 failed, 1 skipped).
  • GenHub.Tests.Linux: All platform tests passed.
  • Unit & Integration Coverage:
    • ProjectConfigServiceTests: Project creation, saving, loading, recent projects tracking, and template initialization.
    • ExternalToolServiceTests: Tool validation, argument passing, working directory execution, cancellation, and failure handling.
    • Tested asset conversion pipelines (CSF string tables, textures, INI files, BIG archive generation).

Greptile Summary

The PR adds the ModBuilder tool suite, including project configuration, incremental build orchestration, conversions, external tools, archive packaging, plugin registration, Avalonia UI, and extensive tests and documentation. The build and packaging paths currently contain failure-state, cached-state, cancellation, and artifact-replacement defects that should be corrected before merge.

  • Adds ModBuilder contracts, models, services, dependency injection, and tool-plugin hosting.
  • Adds project, bundle, file-management, settings, and build-progress UI.
  • Adds image/string-table/text conversions, external process execution, caching, and archive creation.
  • Adds benchmark, unit, integration, performance, sample-project, and user-documentation coverage.

Confidence Score: 1/5

The PR is not safe to merge until failed assets correctly fail builds, per-run steps stop leaking through cached state, cancellation terminates child tools, and archive replacement preserves valid output on failure.

The new build pipeline can report incomplete output as successful, execute stages retained from previous runs, leave cancelled external processes running, and destroy the last valid archive during a failed replacement.

Files Needing Attention: GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs, GenHub/GenHub/Features/Tools/ModBuilder/Services/ExternalToolService.cs, GenHub/GenHub/Features/Tools/ModBuilder/Services/ArchiveService.cs

Important Files Changed

Filename Overview
GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs Adds the central cached, multi-stage build pipeline, but failed files do not fail the build and cached step flags leak between executions.
GenHub/GenHub/Features/Tools/ModBuilder/Services/ExternalToolService.cs Adds pooled external process execution, but mid-process cancellation leaves the child process running.
GenHub/GenHub/Features/Tools/ModBuilder/Services/ArchiveService.cs Adds ZIP/TAR/BIG archive creation, but replacement archives are written non-atomically after deleting the last valid artifact.
GenHub/GenHub/Features/Tools/ModBuilder/Services/ProjectConfigService.cs Adds project lifecycle and recent-project persistence with relocated application-data support; no blocking project-path defect was accepted.
GenHub/GenHub/Infrastructure/DependencyInjection/ModBuilderModule.cs Registers the ModBuilder services and view models with the application container.
GenHub/GenHub/Features/Tools/ModBuilder/ViewModels/ModBuilderViewModel.cs Adds the main project and build workflow UI; its success notification exposes the build engine's incorrect success result.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    UI[ModBuilder UI] --> Project[Project configuration]
    UI --> Engine[Build engine]
    Project --> Setup[Cached build structure]
    Setup --> Engine
    Engine --> Pre[Pre-build]
    Engine --> Clean[Clean]
    Engine --> Assets[Asset conversion]
    Assets --> Images[Image conversion]
    Assets --> Strings[String-table conversion]
    Assets --> Tools[External tools]
    Engine --> Archives[Bundle and release archives]
    Engine --> Install[Install and run]
    Engine --> Result[Build result and progress]
    Result --> UI
Loading
Prompt To Fix All With AI
### Issue 1
GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs:470
**Failed files still pass builds**

When an asset conversion or archive operation fails, `BuildStageAsync` increments `_filesFailed` but still returns `true`, causing the UI to announce success, update the last-build time, and expose incomplete build output.

### Issue 2
GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs:166-179
**Cached build steps leak state**

When the same cached build structure is reused with different requested steps, dependency expansion mutates `setup.Step` in place, causing stages enabled by an earlier run to execute again; for example, a later clean-only request can unexpectedly rebuild the project.

### Issue 3
GenHub/GenHub/Features/Tools/ModBuilder/Services/ExternalToolService.cs:112
**Cancellation leaves child processes running**

When a build is cancelled while an external tool is running, `WaitForExitAsync` throws but the broad catch converts cancellation into a failure result without terminating the process, so the tool continues consuming resources and writing output after the build has stopped.

### Issue 4
GenHub/GenHub/Features/Tools/ModBuilder/Services/ArchiveService.cs:95-99
**Failed rebuild destroys valid archive**

When rebuilding an existing archive encounters cancellation or an I/O error, this code deletes the previous artifact and writes directly to the final path without cleanup or rollback, leaving the last valid archive lost and potentially replacing it with a truncated file.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "test(modbuilder): align parallel benchma..." | Re-trigger Greptile

Greptile also left 4 inline comments on this PR.

Context used:

Note

Add ModBuilder suite for mod project management and asset building

  • Introduces a full ModBuilder tool with views, view models, and services for creating/managing mod projects, running multi-stage builds, and editing bundle configurations.
  • Adds core services: BuildEngineService (pipeline orchestration), ArchiveService (BIG/ZIP creation), BuildCacheService (MD5-based incremental builds), ImageConversionService (PSD/TGA/DDS with alpha detection), FileConversionService (routing by file type), ExternalToolService (process pool execution), and several supporting services.
  • Registers all services and the ModBuilderToolPlugin via a new ModBuilderModule DI extension called from AppServices.
  • Adds Avalonia UI: main ModBuilderView, ProjectDashboardView, FileManagerPanel, SettingsPanel, BuildProgressOverlay, ConfigEditorDialog, BundlePackEditorDialog, and reusable controls (BuildLogEntry, FileTreeItem, ProgressCard, MetricDisplay) with a dark-themed design system and custom converters.
  • Adds unit, integration, and performance regression tests for all new services and view models, plus BenchmarkDotNet benchmarks.
  • Risk: DefaultFileBufferSize in IoConstants changes from 4096 to 65536, affecting all file operations that rely on this constant; compiled bindings are also disabled globally (AvaloniaUseCompiledBindingsByDefault=false).

Macroscope summarized e0a6c14.

@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 16, 2026
@undead2146
undead2146 force-pushed the feat/modbuilder branch 4 times, most recently from f28d270 to fd7f4c6 Compare August 16, 2026 08:17
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 16, 2026
@undead2146
undead2146 marked this pull request as ready for review August 16, 2026 21:07
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/ExternalToolService.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/ArchiveService.cs Outdated
kilo-code-bot[bot]

This comment was marked as resolved.

@kilo-code-bot

This comment was marked as resolved.

@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 16, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 16, 2026
@undead2146

This comment was marked as outdated.

@coderabbitai

This comment was marked as duplicate.

@community-outpost community-outpost deleted a comment from coderabbitai Bot Aug 16, 2026
greptile-apps[bot]

This comment was marked as resolved.

Comment thread GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/ArchiveService.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs Outdated
Comment thread GenHub/GenHub/Features/GameProfiles/ViewModels/GameProfileSettingsViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/BuildEngineService.cs Outdated
Comment thread GenHub/GenHub.Core/Models/Tools/ModBuilder/BundlePack.cs
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
Comment thread GenHub/GenHub.Core/Interfaces/Tools/ModBuilder/ConversionOperationResult{T}.cs Outdated
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 17, 2026
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/StringTableConversionService.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/ViewModels/FileManagerViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/ViewModels/ConfigEditorViewModel.cs Outdated
Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Services/CrunchImageConversionService.cs Outdated
Comment thread GenHub/GenHub.Core/GlobalSuppressions.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderDirectRunner.cs Outdated
Comment thread GenHub/GenHub.Benchmarks/ModBuilder/ModBuilderBenchmarks.cs Outdated
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 18, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 18, 2026
@community-outpost community-outpost deleted a comment from deepsource-io Bot Aug 18, 2026

@kilo-code-bot kilo-code-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

test

Comment thread GenHub/GenHub/Features/Tools/ModBuilder/Views/ModBuilderView.axaml
IsBig = itemVm.IsBig,
BigSuffix = itemVm.BigSuffix,
SetGameLanguageOnInstall = itemVm.SetGameLanguageOnInstall,
Files = ParseItemFiles(itemVm, existingItem),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: ParseItemFiles silently drops every BundleFile property except AbsSourceFile on save

PopulateBundleItems always seeds SourcePattern (joined AbsSourceFile list or the default glob), so the existingItem?.Files branch here is effectively dead: every save rebuilds file entries as new BundleFile { AbsSourceFile = ... }, discarding AbsSourceParent, RelTargetFile, Params, ExcludeMarkersList, and RegistryDef. BuildEngineService reads file.RelTargetFile for build outputs and ConfigurationLoaderService depends on AbsSourceParent, so a load→save round-trip through this editor corrupts the project's target mappings and per-file conversion parameters. Preserve existingItem.Files (or round-trip the full objects) instead of re-parsing the pattern string.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

var files = new List<BundleFile>();
if (!string.IsNullOrWhiteSpace(itemVm.SourcePattern))
{
var patterns = itemVm.SourcePattern.Split([';', ','], StringSplitOptions.RemoveEmptyEntries);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Pattern is joined with "; " but split on both ';' and ','

Absolute paths legitimately containing commas (e.g. C:\My Mods, WIP\file.tga) get truncated mid-path and produce corrupted BundleFile entries on save. Join and split must use the exact same separator set, and a separator that cannot appear in paths is safer for this round-trip encoding.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return;
}

BundleItems.Remove(SelectedBundleItem);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: PackItemSelections is not refreshed after removing a bundle item

The checklist for the currently selected pack still shows the removed item, and re-checking it re-adds the dangling name to SelectedBundlePack.ItemNames, which then gets persisted. Call UpdatePackItemSelections() here (mirroring AddBundleItem).


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return ConvertPsdToStandardImage(sourcePath, targetPath, targetExt, parameters);
}

using var image = await Image.LoadAsync(sourcePath, cancellationToken).ConfigureAwait(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: CPU-bound conversion is no longer offloaded to the thread pool

The previous implementation wrapped decode + ApplyResizeParameters (including the full channel-split ResizeRgbaChannelsSeparately pass) in Task.Run. Now the heavy decode/resize runs on the caller's thread; when ConvertImageAsync is invoked from a UI context this blocks the UI thread for the duration of the resize. Keep the Task.Run offload (as ImageConversionService still does) or document the thread-context requirement.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

ms.Position = 0;
using var loaded = Image.Load(ms);
var resized = ImageProcessingHelper.ApplyResizeParameters(loaded, parameters);
ImageProcessingHelper.SaveImageToTargetAsync(resized, targetPath, targetExt).GetAwaiter().GetResult();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Sync-over-async blocking on SaveImageToTargetAsync(...).GetAwaiter().GetResult()

This refactor introduces blocking waits inside an otherwise async pipeline (repeated at line 529). Make ConvertPsdToStandardImage async and await the save; blocking on async I/O burns a thread-pool thread and is deadlock-prone if a synchronization context is ever introduced.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

FilesPerSecond = 0;

_stopwatch.Restart();
_cancellationTokenSource?.Dispose();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Previous CancellationTokenSource is disposed without being cancelled first

If StartBuild is called while a prior build's UpdateElapsedTimeAsync loop is still running, that loop holds the old token: it never observes cancellation (disposed sources are not cancelled), keeps ticking alongside the new loop (duplicate elapsed-time updates), and its next Task.Delay(1000, token) registration on the disposed source throws ObjectDisposedException in a fire-and-forget task. Call Cancel() on the old source before disposing it.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

/// </summary>
internal static class ImageProcessingHelper
{
public static readonly Dictionary<string, ResamplingMode> ResamplingModes = new(StringComparer.OrdinalIgnoreCase)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: ResamplingModes is a publicly mutable static Dictionary

Any caller can mutate this shared lookup table at runtime, corrupting resize behavior globally. Expose it as IReadOnlyDictionary<string, ResamplingMode> (backed by a private dictionary) or an ImmutableDictionary.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

…s, and security hotspots

- Extract shared ImageProcessingHelper for image resizing and channel manipulation, eliminating duplicate code in ImageConversionService and CrunchImageConversionService
- Refactor StringTableConversionService to reuse IExternalToolService
- Refactor ConfigEditorViewModel into focused helper methods to reduce cognitive complexity
- Replace insecure temp file creation with Path.GetRandomFileName in BuildEngineService
- Add suppression and justification for legacy C&C game MD5 checksums and switch FileManagerViewModel to SHA-256
- Add regex evaluation timeout in TextProcessingService
- Implement IDisposable and cancellation token disposal in BuildProgressViewModel and propagate cancellation tokens
- Fix automatic variable conflict in build-check.ps1 and conditional syntax in build-check.sh
- Add missing assertions in BuildEngineServiceTests
…ts, and code smells

- Delegate MD5 release checksum computation to Md5HashProvider and add GlobalSuppressions entry
- Eliminate unreachable branches in ConfigEditorViewModel with FirstOrDefault
- Mark BuildProgressViewModel sealed and introduce StageStatusPending constant
- Refactor RunGameAsync into helper methods to reduce cognitive complexity
@qodo-code-review

Copy link
Copy Markdown

ⓘ Your Qodo trial ends soon. Ask your workspace admin to set up billing to keep reviews running after the trial. Manage billing

@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant