Skip to content

feat(replay-manager): add automatic replay saving#275

Open
undead2146 wants to merge 29 commits into
developmentfrom
feat/auto-save-replays
Open

feat(replay-manager): add automatic replay saving#275
undead2146 wants to merge 29 commits into
developmentfrom
feat/auto-save-replays

Conversation

@undead2146

@undead2146 undead2146 commented Feb 15, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds automatic replay saving infrastructure to the Replay Manager tool. When enabled on a game profile, the system monitors the active replay file (00000000.rep) during gameplay and automatically saves a copy with metadata-based naming (timestamp, map name, players) when the game ends.
  • Implements a full replay file parser for the GENREP binary format, extracting map name, player names, game date, duration, and version info from .rep files.
  • Adds per-profile "Automatically Save Replays" toggle in the General Settings UI with proper two-way binding through the MVVM stack.

Changes

New Files (10)

  • ReplayMonitorFileSystemWatcher-based monitor with file stability detection (3 consecutive checks at 2-second intervals) to detect when the game stops writing to the replay file
  • ReplayMonitoringService — Orchestrator managing per-profile monitoring sessions via ConcurrentDictionary, implements IReplayMonitorService
  • ReplayParserService — Binary parser for GENREP replay format: validates magic bytes, reads timestamps, extracts UTF-16 strings, parses match metadata (map name, players)
  • ReplaySaveService — Copies completed replays to a Saved/ subdirectory with generated filenames (yyyyMMdd_HHmmss_MapName_Players.rep), handles duplicates and filename sanitization
  • IReplayMonitorService — Core interface defining monitoring lifecycle and ReplayCompleted event
  • ReplayCompletedEventArgs / ReplayFileCompletedEventArgs — Event args in GenHub.Core/Models/Events/
  • ReplayManagerConstants additions — SavedReplaysDirectoryName, DefaultReplayFileName, stability check intervals, ReplayMagicBytes, minimum file size, date format

Modified Files (12)

  • GameProfile / CreateProfileRequest / UpdateProfileRequest — Added AutoSaveReplays property
  • GameProfileManager — Handles AutoSaveReplays in create/update flows
  • GameProfileSettingsViewModel (Properties, Initialization, Commands) — Binds AutoSaveReplays through the ViewModel
  • GameProfileGeneralSettingsView.axaml — Checkbox UI in Launch Configuration section
  • GameLauncher — Starts replay monitoring after successful game launch (when AutoSaveReplays is enabled and profile is not a tool profile)
  • LaunchRegistry — Stops replay monitoring when game process exits
  • ReplayManagerModule — DI registrations for parser, save, and monitoring services

Tests

  • Updated GameLauncherTests to include IReplayMonitorService mock
  • Updated SharedViewModelModuleTests to register replay manager services

Architecture

GameLauncher (starts monitoring)
    └─► ReplayMonitoringService (orchestrates per-profile sessions)
            ├─► ReplayMonitor (FileSystemWatcher + stability detection)
            ├─► ReplaySaveService (copy + rename)
            └─► ReplayParserService (GENREP binary parser)

LaunchRegistry (stops monitoring on process exit)
    └─► ReplayMonitoringService.StopMonitoringAsync()

Test plan

  • Build succeeds with 0 warnings, 0 errors
  • All 1140 tests pass (1 pre-existing OS-specific failure unrelated to changes)
  • Manual: Create a game profile, enable "Automatically Save Replays", launch a game, verify replay is saved to Saved/ directory after game ends
  • Manual: Verify replay filename contains correct timestamp, map name, and player names
  • Manual: Verify disabling the toggle stops automatic saving
  • Manual: Verify tool profiles do not trigger replay monitoring

🤖 Generated with Claude Code


🤖 OpenClaw Changes

Files changed: 3
Lines added: 60
Lines removed: 0
Branch: feat/auto-save-replays
Commit: bf6a7aa5

Last updated: 2026-03-16T14:30:24.103Z

Greptile Summary

This PR adds a complete automatic replay-saving pipeline to the Replay Manager: a FileSystemWatcher-based ReplayMonitor, a ReplayMonitoringService orchestrator, a binary GENREP header parser, and a copy-and-rename save service — all wired into the game launch/exit lifecycle and exposed through a new per-profile UI toggle. A ReplayViewerWindow is also introduced to display parsed replay metadata on demand.

A significant number of issues from previous review rounds have been addressed in this iteration, including:

  • Pre-existing file false-trigger on StartMonitoring removed
  • NotifyFilters.LastWrite dropped in favour of size-only tracking
  • _watcher resource leak on EnableRaisingEvents failure fixed
  • Watcher restart failure now transitions to a clean stopped state
  • AddOrUpdate null-insertion race replaced with atomic TryRemove(KVP) pattern
  • Dispose loop now continues past individual monitor failures
  • finally block in OnReplayFileCompletedAsync now uses the session-ID-aware stop overload
  • MaxPlayersInSavedFileName / MaxFileNameLength promoted to named constants; dead MaxFileNameLengthBeforeExtension removed
  • Duplicate ParseReplayCore build-breaking method removed; ParseReplayAsync now wraps in Task.Run
  • Raw match data demoted from LogInformation to LogDebug
  • Sanitized map name guarded against empty; player names filtered before join
  • ReplayViewerWindow DataContext subscription leak fixed with _currentViewModel tracking
  • Design-time DataContext removed from ReplayViewerWindow.axaml
  • Tool profile now hides the checkbox via IsToolProfile binding

Remaining concerns:

  • StopMonitoringInternal(profileId, sessionId) in the success path of OnReplayFileCompletedAsync is placed after ReplayCompleted?.Invoke(...), so a throwing event subscriber leaves the session permanently orphaned in _activeSessions until the LaunchRegistry grace-period fires.
  • The save-failure early-return path similarly skips session cleanup (acknowledged from a previous round, no resolution yet).
  • Raw GENREP M key value (a full file path, e.g. maps/alpine_assault/alpine_assault.map) is stored verbatim as MapName, producing noisy filenames and UI display (acknowledged, no resolution yet).
  • Outer colon-split in ParsePlayerSlots uses RemoveEmptyEntries, which can shift StartPosition indices for sparse slot records (acknowledged, no resolution yet).

Confidence Score: 3/5

  • The PR is functional and builds cleanly, but several error-handling edge cases leave sessions orphaned in _activeSessions under failure or exception conditions.
  • A large number of issues raised in previous review rounds have been thoughtfully addressed in this iteration — the core correctness of the happy path is solid. However, multiple acknowledged-but-unresolved items remain (orphaned sessions on save failure, raw map path in filenames, positional index shift in sparse slot parsing), and a newly identified path exists where a throwing ReplayCompleted subscriber skips the session cleanup step. None of these cause data loss or security issues, but they represent observable incorrect behavior (stale IsMonitoring state, confusing filenames) that users could encounter in practice.
  • Pay close attention to ReplayMonitoringService.cs (session lifecycle cleanup), ReplayParserService.cs (map name extraction and slot parsing), and ReplaySaveService.cs (error handling in CopyWithRetry).

Important Files Changed

Filename Overview
GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplayMonitoringService.cs Core orchestrator for per-profile monitoring sessions. Several previous issues have been addressed (null! in AddOrUpdate, finally block stopping wrong session, Dispose loop aborting on exception). One new unresolved issue: StopMonitoringInternal is skipped if a ReplayCompleted event handler throws, orphaning the session in _activeSessions.
GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplayMonitor.cs FileSystemWatcher-based replay file monitor with stability detection. Previous issues addressed: NotifyFilters.LastWrite removed (now Size-only), watcher leaked on EnableRaisingEvents failure fixed, pre-existing file false-trigger removed, watcher restart failure now calls StopMonitoring(), infinite timer for sub-minimum-size stable files fixed. Code is now cleaner and more robust.
GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplayParserService.cs Binary GENREP parser. Multiple previous issues addressed: duplicate ParseReplayCore removed, async wraps in Task.Run, stream position drain added, raw match data demoted to LogDebug, robust encoding comparison via CodePage, duration guard for zero beginTimestamp. Remaining open issues from previous rounds: raw map path from M key stored verbatim, outer colon-split uses RemoveEmptyEntries.
GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplaySaveService.cs Replay copy and naming service. Previous issues addressed: MaxPlayersInSavedFileName/MaxFileNameLength constants added, MaxFileNameLengthBeforeExtension dead constant removed, MaxUniquePathAttempts bounds added, sanitized map name checked for empty, player names filtered before joining. Remaining open items: off-by-one at loop bound edge case, partial destination file on CopyTo IOException, IOException catch too broad.
GenHub/GenHub/Features/Tools/ReplayManager/ViewModels/ReplayManagerViewModel.cs Adds ParseReplayAsync command and injects concrete ReplayParserService. Main window unavailability now shows warning notifications (addressed). IsBusy stays true for entire dialog lifetime while viewer is open (remains from previous rounds).
GenHub/GenHub/Features/Launching/LaunchRegistry.cs Starts grace-period stop of replay monitoring on process exit using session-ID-aware StopMonitoringAsync to avoid cancelling a newer relaunch session. Grace period (14s) may be tight if OS flushes buffers late, but is structurally sound.

Sequence Diagram

sequenceDiagram
    participant GL as GameLauncher
    participant RMS as ReplayMonitoringService
    participant RM as ReplayMonitor
    participant FSW as FileSystemWatcher
    participant RSS as ReplaySaveService
    participant RPS as ReplayParserService
    participant LR as LaunchRegistry

    GL->>RMS: StartMonitoringAsync(profileId, gameType)
    RMS->>RM: new ReplayMonitor()
    RMS->>RM: StartMonitoring(replayFilePath)
    RM->>FSW: EnableRaisingEvents = true (Size filter only)
    Note over RM,FSW: Waits for game to write to 00000000.rep

    FSW-->>RM: OnFileChanged (size changed)
    RM->>RM: StartStabilityCheck() (2s timer)
    loop 3 consecutive stable checks
        RM->>RM: CheckFileStability()
    end
    RM->>RM: StopMonitoring()
    RM-->>RMS: FileCompleted event

    RMS->>RSS: SaveReplayAsync(filePath, gameType)
    RSS->>RPS: ParseReplayAsync(filePath, gameType)
    RPS-->>RSS: ReplayMetadata
    RSS->>RSS: GenerateFileName(metadata)
    RSS->>RSS: CopyWithRetry(src, dst)
    RSS-->>RMS: (savedFilePath, metadata)

    RMS-->>RMS: ReplayCompleted event raised
    RMS->>RMS: StopMonitoringInternal(profileId, sessionId)

    Note over LR: On process exit (T=0)
    LR->>LR: Task.Delay(14s grace period)
    LR->>RMS: StopMonitoringAsync(profileId, sessionId)
Loading

Comments Outside Diff (4)

  1. GenHub/GenHub/Features/Info/ViewModels/DemoViewModelFactory.cs, line 241-244 (link)

    null! returned from catch bypasses null-safety

    The catch block silently swallows all exceptions and returns null!, explicitly suppressing the compiler's null-safety analysis for a non-nullable return type. Any caller that dereferences the result without a null-check will encounter a NullReferenceException at runtime with no compile-time warning.

    All other demo factory methods in this class fall back to a real default instance instead — for example, CreateDemoGameSettingsViewModel creates a fresh GameSettingsViewModel in its catch. CreateDemoReplayManager should follow the same pattern:

    catch
    {
        // Fail safe: return a minimal but non-null ViewModel
        var fallbackDir = new MockReplayDirectoryService();
        var fallbackNotify = notificationService ?? new MockNotificationService();
        var fallbackLogger = new MockLogger<ReplayManagerViewModel>();
        var fallbackParserLogger = new MockLogger<ReplayParserService>();
        return new ReplayManagerViewModel(
            fallbackDir,
            new MockReplayImportService(),
            new MockReplayExportService(),
            new MockUploadHistoryService(),
            new ReplayParserService(fallbackParserLogger),
            fallbackNotify,
            fallbackLogger);
    }
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: GenHub/GenHub/Features/Info/ViewModels/DemoViewModelFactory.cs
    Line: 241-244
    
    Comment:
    **`null!` returned from catch bypasses null-safety**
    
    The catch block silently swallows all exceptions and returns `null!`, explicitly suppressing the compiler's null-safety analysis for a non-nullable return type. Any caller that dereferences the result without a null-check will encounter a `NullReferenceException` at runtime with no compile-time warning.
    
    All other demo factory methods in this class fall back to a real default instance instead — for example, `CreateDemoGameSettingsViewModel` creates a fresh `GameSettingsViewModel` in its catch. `CreateDemoReplayManager` should follow the same pattern:
    
    ```csharp
    catch
    {
        // Fail safe: return a minimal but non-null ViewModel
        var fallbackDir = new MockReplayDirectoryService();
        var fallbackNotify = notificationService ?? new MockNotificationService();
        var fallbackLogger = new MockLogger<ReplayManagerViewModel>();
        var fallbackParserLogger = new MockLogger<ReplayParserService>();
        return new ReplayManagerViewModel(
            fallbackDir,
            new MockReplayImportService(),
            new MockReplayExportService(),
            new MockUploadHistoryService(),
            new ReplayParserService(fallbackParserLogger),
            fallbackNotify,
            fallbackLogger);
    }
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. GenHub/GenHub.Core/Interfaces/Tools/ReplayManager/IReplayMonitorService.cs, line 98-99 (link)

    ReplayCompleted event has no subscribers — auto-save is silent

    ReplayCompleted is raised in ReplayMonitoringService.OnReplayFileCompletedAsync when a replay is successfully saved, but no component in this PR subscribes to it. Neither GameLauncher nor LaunchRegistry registers a handler after calling StartMonitoringAsync. As a result, the feature ships with zero user feedback: replays are saved silently to Saved/, and the user has no way to confirm it worked (or that it happened at all) without manually inspecting the folder.

    The canonical fix is to subscribe to the event after starting monitoring and show a toast notification on completion:

    // In GameLauncher, after StartMonitoringAsync:
    replayMonitorService.ReplayCompleted += OnReplayCompleted;
    
    // Handler:
    private void OnReplayCompleted(object? sender, ReplayCompletedEventArgs e)
    {
        notificationService.ShowSuccess("Replay Saved", $"Replay saved to: {Path.GetFileName(e.SavedFilePath)}");
    }

    At minimum, the lack of any subscriber for an event defined on a public interface should be called out before merge, as it leaves the feature incomplete from a UX perspective.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: GenHub/GenHub.Core/Interfaces/Tools/ReplayManager/IReplayMonitorService.cs
    Line: 98-99
    
    Comment:
    **`ReplayCompleted` event has no subscribers — auto-save is silent**
    
    `ReplayCompleted` is raised in `ReplayMonitoringService.OnReplayFileCompletedAsync` when a replay is successfully saved, but no component in this PR subscribes to it. Neither `GameLauncher` nor `LaunchRegistry` registers a handler after calling `StartMonitoringAsync`. As a result, the feature ships with zero user feedback: replays are saved silently to `Saved/`, and the user has no way to confirm it worked (or that it happened at all) without manually inspecting the folder.
    
    The canonical fix is to subscribe to the event after starting monitoring and show a toast notification on completion:
    
    ```csharp
    // In GameLauncher, after StartMonitoringAsync:
    replayMonitorService.ReplayCompleted += OnReplayCompleted;
    
    // Handler:
    private void OnReplayCompleted(object? sender, ReplayCompletedEventArgs e)
    {
        notificationService.ShowSuccess("Replay Saved", $"Replay saved to: {Path.GetFileName(e.SavedFilePath)}");
    }
    ```
    
    At minimum, the lack of any subscriber for an event defined on a public interface should be called out before merge, as it leaves the feature incomplete from a UX perspective.
    
    How can I resolve this? If you propose a fix, please make it concise.
  3. GenHub/GenHub/Features/Info/ViewModels/DemoViewModelFactory.cs, line 248-254 (link)

    Missing ReplayParserService in fallback constructor

    The fallback ReplayManagerViewModel instantiation inside the catch block is missing the ReplayParserService argument. The primary constructor requires 7 parameters:

    1. IReplayDirectoryService
    2. IReplayImportService
    3. IReplayExportService
    4. IUploadHistoryService
    5. ReplayParserService ← missing here
    6. INotificationService
    7. ILogger<ReplayManagerViewModel>

    The call as written passes new MockNotificationService() at position 5 (which requires ReplayParserService) and new MockLogger<>() at position 6 (which requires INotificationService), with the logger argument omitted entirely. This produces a compile error (CS1503 / CS7036) that would prevent the project from building.

  4. GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplayMonitoringService.cs, line 242-255 (link)

    Orphaned session if ReplayCompleted event handler throws

    If any subscriber to ReplayCompleted throws a synchronous exception during Invoke, the exception propagates up and StopMonitoringInternal(profileId, sessionId) at line 255 is never reached. The session remains in _activeSessions with IsMonitoring(profileId) returning true, even though the underlying ReplayMonitor has already self-stopped via StopMonitoring() inside CheckFileStability.

    This mirrors the acknowledged orphaned-session risk from the null-save-result early return, but from a different code path. The LaunchRegistry grace-period would eventually clean it up, but there's a window where the next relaunch would incorrectly attempt to stop an already-completed session.

    Moving StopMonitoringInternal before the event raise (or wrapping it in a try/finally) ensures cleanup regardless of subscriber behaviour:

    // Cleanup first so a throwing subscriber can't leave a stale session
    StopMonitoringInternal(profileId, sessionId);
    
    ReplayCompleted?.Invoke(this, new ReplayCompletedEventArgs
    {
        ProfileId = profileId,
        SavedFilePath = savedFilePath,
        Metadata = metadata,
    });
Prompt To Fix All With AI
This is a comment left during a code review.
Path: GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplayMonitoringService.cs
Line: 242-255

Comment:
**Orphaned session if `ReplayCompleted` event handler throws**

If any subscriber to `ReplayCompleted` throws a synchronous exception during `Invoke`, the exception propagates up and `StopMonitoringInternal(profileId, sessionId)` at line 255 is never reached. The session remains in `_activeSessions` with `IsMonitoring(profileId)` returning `true`, even though the underlying `ReplayMonitor` has already self-stopped via `StopMonitoring()` inside `CheckFileStability`.

This mirrors the acknowledged orphaned-session risk from the `null`-save-result early return, but from a different code path. The LaunchRegistry grace-period would eventually clean it up, but there's a window where the next relaunch would incorrectly attempt to stop an already-completed session.

Moving `StopMonitoringInternal` before the event raise (or wrapping it in a `try/finally`) ensures cleanup regardless of subscriber behaviour:

```csharp
// Cleanup first so a throwing subscriber can't leave a stale session
StopMonitoringInternal(profileId, sessionId);

ReplayCompleted?.Invoke(this, new ReplayCompletedEventArgs
{
    ProfileId = profileId,
    SavedFilePath = savedFilePath,
    Metadata = metadata,
});
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: GenHub/GenHub/Features/Tools/ReplayManager/ViewModels/ReplayManagerViewModel.cs
Line: 844-870

Comment:
**`IsBusy` blocks parent UI for the entire dialog lifetime**

`IsBusy` is set to `true` at line 844 and only reset in the `finally` block at line 892, after `await window.ShowDialog(mainWindow)` returns. While the `ReplayViewerWindow` is open, `IsBusy` remains `true`, causing the Replay Manager's loading overlay to stay visible and all toolbar buttons to remain disabled — even though the parse is already complete and the user is actively reading the viewer.

Resetting `IsBusy` and `StatusMessage` before `ShowDialog` keeps the parent UI interactive during viewing:

```csharp
IsBusy = false;
StatusMessage = "Ready";
await window.ShowDialog(mainWindow);
```

The `finally` block's `IsBusy = false` can stay as a safety net for the failure paths.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 8f76b26

coderabbitai[bot]

This comment was marked as duplicate.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from f437d04 to 7c00d78 Compare February 15, 2026 03:40
Comment thread GenHub/GenHub/Features/Launching/LaunchRegistry.cs
coderabbitai[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from 7c00d78 to e3ad922 Compare February 15, 2026 03:55
coderabbitai[bot]

This comment was marked as outdated.

Comment thread GenHub/GenHub/Features/Launching/LaunchRegistry.cs Outdated
@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from e3ad922 to c0dfc65 Compare February 15, 2026 04:43
coderabbitai[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from c0dfc65 to a3f85ba Compare February 15, 2026 04:58
greptile-apps[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch 2 times, most recently from 5db7437 to 368fcbb Compare February 15, 2026 05:54
coderabbitai[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from 368fcbb to c449ece Compare February 15, 2026 06:04
coderabbitai[bot]

This comment was marked as outdated.

greptile-apps[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from c449ece to 72e3d76 Compare February 15, 2026 06:25
greptile-apps[bot]

This comment was marked as outdated.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from 72e3d76 to 9400ab0 Compare February 15, 2026 06:37
greptile-apps[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as duplicate.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from 9400ab0 to 4e4aa51 Compare February 15, 2026 10:29
Comment thread GenHub/GenHub/Features/Tools/ReplayManager/Services/ReplaySaveService.cs Outdated
greptile-apps[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as abuse.

@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from 4e4aa51 to e0df3d7 Compare February 15, 2026 15:36
coderabbitai[bot]

This comment was marked as duplicate.

undead2146 added 14 commits March 16, 2026 18:19
- Remove blank lines after opening braces (SA1505)
- Add trailing commas to multi-line initializers (SA1413)
- Remove multiple blank lines in a row (SA1507)
- Fix multi-line parameter formatting (SA1116, SA1117)
- Move static methods before instance methods (SA1204)
- Change log level from Information to Debug for raw match data
- Fix FileShare.Read to FileShare.ReadWrite to handle concurrent access
- Fix filename truncation to account for counter suffix length
- Replace File.Copy with atomic FileMode.CreateNew to prevent TOCTOU races
- Move all static helper methods before public/instance methods
- Remove duplicate blank line (SA1507)
- Remove duplicate static method definitions
- Fixes StyleCop SA1204 warning
- Add try/catch in StartMonitoring to clean up watcher if EnableRaisingEvents throws
- Move watcher cleanup before IsMonitoring check in StopMonitoring to prevent leaks
- Add try/catch in Dispose loop to ensure all monitors are disposed even if one fails
- Addresses Greptile review comments about resource leaks
- Constant was never used and could mislead developers
- Actual truncation logic correctly calculates bounds dynamically
- Prevents potential PathTooLongException from incorrect usage
- Addresses Greptile review comment
- Call StopMonitoring() when watcher restart fails in OnWatcherError
- Ensures IsMonitoring accurately reflects monitor capability
- Prevents silent event loss when watcher becomes deaf
- Allows grace-period cleanup or caller retry to handle the failure
- Addresses Greptile review comment
- Use replay.GameVersion instead of SelectedTab
- Ensures correct parsing when UI tab doesn't match replay's game type
- Addresses kilo-code-bot critical review comment
- Change FileShare.Read to FileShare.ReadWrite in both copy attempts
- Matches ReplayParserService behavior for concurrent file access
- Prevents IOException when game still has replay file open
- Addresses kilo-code-bot critical review comments
- Wrap StartMonitoring in try/catch to remove session on failure
- Prevents orphaned sessions with IsMonitoring=false in _activeSessions
- Separate source file open from destination collision retry logic
- Source file errors now propagate immediately instead of retrying
- Reset stream position for retry attempts
- Improves error diagnostics and prevents unnecessary retries
- Addresses Greptile and kilo-code-bot review comments
- Add beginTimestamp > 0 guard to duration calculation
- Prevents invalid durations like 485277h when beginTimestamp=0
- Mirrors the gameDate guard logic
…fix stream position drift in ReplayParserService
…r error notifications, use size-only file monitoring
…ling, FileShare.Read, cleanup on failures, stop monitoring only on success
@undead2146 undead2146 force-pushed the feat/auto-save-replays branch from f688168 to 0852f89 Compare March 16, 2026 18:19
@undead2146

Copy link
Copy Markdown
Member Author

https://github.com/openclaw implement extensible genhub:// URI protocol handler for deep linking into GenHub features.

Requirements:

Design a flexible URI routing system that supports multiple actions and resources
Initial implementation should support: genhub://replay/download?url=<replay_url>
Architecture must be extensible for future features:
genhub://profile/share?id=<profile_id>
genhub://publisher/subscribe?name=<publisher_name>
genhub://replay/live?match_id=<match_id>
genhub://mod/install?id=<mod_id>
URI Structure: genhub:///?

Implementation:

Create a router pattern that maps resource/action combinations to handlers
Each handler should be a separate service implementing a common interface
Show Material Design confirmation dialogs before executing actions
Use existing services (ReplaySaveService, etc.) for actual operations
Register protocol with Windows registry and Linux .desktop file
Parse and validate all URI parameters with proper error messages
Add comprehensive logging for debugging
Follow GenHub's MVVM, DI, and feature-based architecture
Include unit tests for router and each handler
Files to create:

GenHub.Core/Interfaces/IUriProtocolHandler.cs (main handler interface)
GenHub.Core/Interfaces/IUriActionHandler.cs (individual action handler interface)
GenHub.Windows/Features/UriProtocol/Services/UriProtocolRouter.cs (main router)
GenHub.Windows/Features/UriProtocol/Services/Handlers/ReplayDownloadHandler.cs
GenHub.Windows/Features/UriProtocol/ViewModels/UriConfirmationViewModel.cs
GenHub.Windows/Features/UriProtocol/Views/UriConfirmationDialog.axaml
GenHub.Windows/Infrastructure/UriProtocol/UriProtocolRegistration.cs
App.axaml.cs modifications for startup URI handling
Comprehensive tests for router and handlers
Make it easy to add new URI handlers in the future without modifying the core routing logic

@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working right Enhancement New feature or request openclaw refactoring Testing Topic related to (unit) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant