diff --git a/GamesDat.Demo.Wpf/ViewModels/FileWatcherTabViewModel.cs b/GamesDat.Demo.Wpf/ViewModels/FileWatcherTabViewModel.cs index c2a7a58..1b0d028 100644 --- a/GamesDat.Demo.Wpf/ViewModels/FileWatcherTabViewModel.cs +++ b/GamesDat.Demo.Wpf/ViewModels/FileWatcherTabViewModel.cs @@ -15,6 +15,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; +using GamesDat.Core.Telemetry.Sources.AgeOfEmpires4; namespace GamesDat.Demo.Wpf.ViewModels; @@ -323,6 +324,33 @@ private void InitializeBuiltInSources() null); Sources.Add(brawlhallaSource); } + + // Add AoE IV source + try + { + var aoe4Path = AgeOfEmpires4ReplayFileSource.GetDefaultReplayPath(); + System.Diagnostics.Debug.WriteLine($"AoE IV replay path: {aoe4Path}"); + var aoe4Source = new FileWatcherSourceViewModel( + "Age of Empires IV", + aoe4Path, + "*.*", + () => new AgeOfEmpires4ReplayFileSource()); + aoe4Source.DetectedFiles.CollectionChanged += OnSourceFilesChanged; + Sources.Add(aoe4Source); + } + catch (DirectoryNotFoundException) + { + // Game not installed - add disabled source + var defaultPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + "Age of Empires IV", "playback"); + var aoe4Source = new FileWatcherSourceViewModel( + "Age of Empires IV (Not Installed)", + defaultPath, + "*.*", + null); + Sources.Add(aoe4Source); + } } private void OnSourceFilesChanged(object? sender, NotifyCollectionChangedEventArgs e) diff --git a/GamesDat.Tests/Helpers/FileWatcherTestData.cs b/GamesDat.Tests/Helpers/FileWatcherTestData.cs index 525cfdc..689bc54 100644 --- a/GamesDat.Tests/Helpers/FileWatcherTestData.cs +++ b/GamesDat.Tests/Helpers/FileWatcherTestData.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using GamesDat.Core.Telemetry.Sources; +using GamesDat.Core.Telemetry.Sources.AgeOfEmpires4; using GamesDat.Core.Telemetry.Sources.Tekken8; namespace GamesDat.Tests.Helpers; @@ -12,6 +13,16 @@ namespace GamesDat.Tests.Helpers; /// public class FileWatcherTestData { + + /// + /// Sources we currently explicitly ignore during testing ebcause they use an + /// overly broad pattern ("*.*") and require special handling to avoid test pollution. + /// + public static Type[] IgnoredSources = [ + typeof(Tekken8ReplayFileSource), + typeof(AgeOfEmpires4ReplayFileSource) + ]; + /// /// Returns all discovered file watcher sources as test case data. /// Each test case includes: Type sourceType, string[] patterns @@ -23,9 +34,11 @@ public static IEnumerable AllSources() foreach (var sourceType in sources) { - // Skip Tekken8 (uses wildcard pattern "*.*" with subdirectories - needs special handling) - if (sourceType == typeof(Tekken8ReplayFileSource)) + if (IgnoredSources.Contains(sourceType)) + { + Console.WriteLine($"Skipping source {sourceType.Name} in AllSources test data due to broad pattern and special handling requirements."); continue; + } var patterns = FileWatcherSourceDiscovery.GetExpectedPatterns(sourceType); diff --git a/GamesDat/Telemetry/Sources/AgeOfEmpires4/AgeOfEmpires4ReplayFileSource.cs b/GamesDat/Telemetry/Sources/AgeOfEmpires4/AgeOfEmpires4ReplayFileSource.cs new file mode 100644 index 0000000..6a9ea4d --- /dev/null +++ b/GamesDat/Telemetry/Sources/AgeOfEmpires4/AgeOfEmpires4ReplayFileSource.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GamesDat.Core.Telemetry.Sources.AgeOfEmpires4 +{ + public class AgeOfEmpires4ReplayFileSource : FileWatcherSourceBase + { + public AgeOfEmpires4ReplayFileSource(FileWatcherOptions options) : base(ApplyDefaults(options)) + { + } + + public AgeOfEmpires4ReplayFileSource() + : base( + path: GetDefaultReplayPath(), + pattern: "*.*", + includeSubdirectories: false, + debounceDelay: TimeSpan.FromSeconds(2) + ) + { + } + + public static string GetDefaultReplayPath() + { + var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments, Environment.SpecialFolderOption.DoNotVerify); + return System.IO.Path.Combine(documentsFolder, "My Games", "Age of Empires IV", "playback"); + } + + /// + /// Applies default configuration options if not specified. + /// + /// The input options. + /// Options with defaults applied. + private static FileWatcherOptions ApplyDefaults(FileWatcherOptions options) + { + return new FileWatcherOptions + { + Path = string.IsNullOrEmpty(options.Path) ? GetDefaultReplayPath() : options.Path, + Patterns = options.Patterns == null || options.Patterns.Length == 0 + ? ["*.*"] + : options.Patterns, + IncludeSubdirectories = options.IncludeSubdirectories, + DebounceDelay = options.DebounceDelay == default + ? TimeSpan.FromSeconds(2) + : options.DebounceDelay + }; + } + } +} diff --git a/README.md b/README.md index 607f36b..d4f5526 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Out of the box: | Game | Replay/Demo files | Realtime data | Tested | | -------------------------- | ------------------------- | ------------------------------- | ------ | +| Age of Empires IV | ✅ | ❌ | ⏳ | | Assetto Corsa Competizione | ❌ | ✅ Physics, Graphics, Telemetry | ⏳ | | Brawlhalla | ✅ | ❌ | ✅ | | Rocket League | ✅ | ❌ | ✅ |