diff --git a/SharpHook.Test/SharpHook.Test.csproj b/SharpHook.Test/SharpHook.Test.csproj index 973df835..1156b45d 100644 --- a/SharpHook.Test/SharpHook.Test.csproj +++ b/SharpHook.Test/SharpHook.Test.csproj @@ -8,7 +8,7 @@ - + diff --git a/SightKeeper.Application/Profile/Creating/NewProfileDataDTO.cs b/SightKeeper.Application/Profile/Creating/NewProfileDataDTO.cs index 63f77213..d24b1c45 100644 --- a/SightKeeper.Application/Profile/Creating/NewProfileDataDTO.cs +++ b/SightKeeper.Application/Profile/Creating/NewProfileDataDTO.cs @@ -10,15 +10,17 @@ public sealed class NewProfileDataDTO : NewProfileData public string Description { get; } public float DetectionThreshold { get; } public float MouseSensitivity { get; } + public TimeSpan PostProcessDelay { get; } public Weights Weights { get; } public IReadOnlyList ItemClasses { get; } - public NewProfileDataDTO(string name, string description, float detectionThreshold, float mouseSensitivity, Weights weights, IEnumerable itemClasses) + public NewProfileDataDTO(string name, string description, float detectionThreshold, float mouseSensitivity, TimeSpan postProcessDelay, Weights weights, IEnumerable itemClasses) { Name = name; Description = description; DetectionThreshold = detectionThreshold; MouseSensitivity = mouseSensitivity; + PostProcessDelay = postProcessDelay; Weights = weights; ItemClasses = itemClasses.ToImmutableList(); } diff --git a/SightKeeper.Application/Profile/Creating/ProfileCreator.cs b/SightKeeper.Application/Profile/Creating/ProfileCreator.cs index f7fdc3af..7513fd8a 100644 --- a/SightKeeper.Application/Profile/Creating/ProfileCreator.cs +++ b/SightKeeper.Application/Profile/Creating/ProfileCreator.cs @@ -15,7 +15,7 @@ public ProfileCreator(IValidator validator, ProfilesDataAccess p public async Task CreateProfile(NewProfileDataDTO data) { await _validator.ValidateAndThrowAsync(data); - Profile profile = new(data.Name, data.Description, data.DetectionThreshold, data.MouseSensitivity, data.Weights); + Profile profile = new(data.Name, data.Description, data.DetectionThreshold, data.MouseSensitivity, data.PostProcessDelay, data.Weights); foreach (var itemClass in data.ItemClasses) profile.AddItemClass(itemClass); await _profilesDataAccess.AddProfile(profile); diff --git a/SightKeeper.Application/Profile/Editing/EditedProfileDataDTO.cs b/SightKeeper.Application/Profile/Editing/EditedProfileDataDTO.cs index 638dc2f1..91becebc 100644 --- a/SightKeeper.Application/Profile/Editing/EditedProfileDataDTO.cs +++ b/SightKeeper.Application/Profile/Editing/EditedProfileDataDTO.cs @@ -11,16 +11,18 @@ public sealed class EditedProfileDataDTO : EditedProfileData public string Description { get; } public float DetectionThreshold { get; } public float MouseSensitivity { get; } + public TimeSpan PostProcessDelay { get; } public Weights Weights { get; } public IReadOnlyList ItemClasses { get; } - public EditedProfileDataDTO(Profile profile, string name, string description, float detectionThreshold, float mouseSensitivity, Weights weights, IEnumerable itemClasses) + public EditedProfileDataDTO(Profile profile, string name, string description, float detectionThreshold, float mouseSensitivity, TimeSpan postProcessDelay, Weights weights, IEnumerable itemClasses) { Profile = profile; Name = name; Description = description; DetectionThreshold = detectionThreshold; MouseSensitivity = mouseSensitivity; + PostProcessDelay = postProcessDelay; Weights = weights; ItemClasses = itemClasses.ToImmutableList(); } diff --git a/SightKeeper.Application/Profile/Editing/ProfileEditor.cs b/SightKeeper.Application/Profile/Editing/ProfileEditor.cs index 0c648a47..6050ec17 100644 --- a/SightKeeper.Application/Profile/Editing/ProfileEditor.cs +++ b/SightKeeper.Application/Profile/Editing/ProfileEditor.cs @@ -24,6 +24,7 @@ public async Task ApplyChanges(EditedProfileDataDTO data) profile.Description = data.Description; profile.DetectionThreshold = data.DetectionThreshold; profile.MouseSensitivity = data.MouseSensitivity; + profile.PostProcessDelay = data.PostProcessDelay; profile.Weights = data.Weights; profile.ClearItemClasses(); foreach (var itemClass in data.ItemClasses) diff --git a/SightKeeper.Application/Profile/ProfileData.cs b/SightKeeper.Application/Profile/ProfileData.cs index e16020e6..76b9ffc7 100644 --- a/SightKeeper.Application/Profile/ProfileData.cs +++ b/SightKeeper.Application/Profile/ProfileData.cs @@ -9,6 +9,7 @@ public interface ProfileData string Description { get; } float DetectionThreshold { get; } float MouseSensitivity { get; } + TimeSpan PostProcessDelay { get; } Weights? Weights { get; } IReadOnlyList ItemClasses { get; } } \ No newline at end of file diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/AbstractProfileEditorVIewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/AbstractProfileEditorVIewModel.cs index 956a8d70..cdaa9f1a 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/AbstractProfileEditorVIewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/AbstractProfileEditorVIewModel.cs @@ -55,6 +55,18 @@ public float MouseSensitivity set => SetProperty(ref _mouseSensitivity, value); } + public TimeSpan PostProcessDelay + { + get => _postProcessDelay; + set => SetProperty(ref _postProcessDelay, value); + } + + ushort ProfileEditorViewModel.PostProcessDelay + { + get => (ushort)_postProcessDelay.TotalMilliseconds; + set => SetProperty(PostProcessDelay.TotalMilliseconds, value, newValue => _postProcessDelay = TimeSpan.FromMilliseconds(newValue)); + } + public DataSet? DataSet { get => _dataSet; @@ -112,7 +124,7 @@ protected AbstractProfileEditorVIewModel(IValidator validator, Dat private IReadOnlyCollection _availableWeights = Array.Empty(); [ObservableProperty, NotifyCanExecuteChangedFor(nameof(AddItemClassCommand))] private ItemClass? _itemClassToAdd; - + private TimeSpan _postProcessDelay; ICommand ProfileEditorViewModel.AddItemClassCommand => AddItemClassCommand; [RelayCommand(CanExecute = nameof(CanAddItemClass))] diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ExistingProfileEditorViewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ExistingProfileEditorViewModel.cs index 95c998e5..7a2b071e 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ExistingProfileEditorViewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ExistingProfileEditorViewModel.cs @@ -21,6 +21,7 @@ public void SetData(Profile profile) Description = profile.Description; DetectionThreshold = profile.DetectionThreshold; MouseSensitivity = profile.MouseSensitivity; + PostProcessDelay = profile.PostProcessDelay; DataSet = profile.Weights.Library.DataSet; Weights = profile.Weights; _itemClasses.Clear(); diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/FakeProfileEditorViewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/FakeProfileEditorViewModel.cs index 0bd82c31..5e3fde4a 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/FakeProfileEditorViewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/FakeProfileEditorViewModel.cs @@ -17,6 +17,7 @@ public sealed partial class FakeProfileEditorViewModel : ViewModel, ProfileEdito public string Description { get; set; } = "Some description.. lorem ipsum and all that stuff"; [ObservableProperty] private float _detectionThreshold = 0.6f; [ObservableProperty] private float _mouseSensitivity = 1.5f; + public ushort PostProcessDelay { get; set; } public DataSet? DataSet { get; set; } public Weights? Weights { get; set; } public IReadOnlyList ItemClasses { get; } diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ProfileEditorViewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ProfileEditorViewModel.cs index 99a4224e..a528c35c 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ProfileEditorViewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Editor/ProfileEditorViewModel.cs @@ -15,6 +15,7 @@ public interface ProfileEditorViewModel string Description { get; set; } float DetectionThreshold { get; set; } float MouseSensitivity { get; set; } + ushort PostProcessDelay { get; set; } DataSet? DataSet { get; set; } Weights? Weights { get; set; } IReadOnlyList ItemClasses { get; } diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/FakeProfilesViewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/FakeProfilesViewModel.cs index aedb2860..3780d68e 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/FakeProfilesViewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/FakeProfilesViewModel.cs @@ -26,8 +26,8 @@ public FakeProfilesViewModel() dataSet.Game = game; var weights = dataSet.WeightsLibrary.CreateWeights(Array.Empty(), Array.Empty(), ModelSize.Small, 100, 0.5f, 0.4f, 0.3f, Enumerable.Empty()); - Profile profile1 = new("Profile", string.Empty, 0.5f, 2f, weights); - Profile profile2 = new("Profile with long name!", string.Empty, 0.3f, 3f, weights); + Profile profile1 = new("Profile", string.Empty, 0.5f, 2f, TimeSpan.FromMilliseconds(10), weights); + Profile profile2 = new("Profile with long name!", string.Empty, 0.3f, 3f, TimeSpan.FromMilliseconds(15), weights); Profiles = new ProfileViewModel[] { new(profile1), diff --git a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/ProfilesViewModel.cs b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/ProfilesViewModel.cs index eaa3bad2..20f467f2 100644 --- a/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/ProfilesViewModel.cs +++ b/SightKeeper.Avalonia/ViewModels/Tabs/Profiles/Tab/ProfilesViewModel.cs @@ -74,7 +74,7 @@ private async Task CreateProfile() if (result == ProfileEditorResult.Apply) { Guard.IsNotNull(viewModel.Weights); - NewProfileDataDTO data = new(viewModel.Name, viewModel.Description, viewModel.DetectionThreshold, viewModel.MouseSensitivity, viewModel.Weights, viewModel.ItemClasses); + NewProfileDataDTO data = new(viewModel.Name, viewModel.Description, viewModel.DetectionThreshold, viewModel.MouseSensitivity, viewModel.PostProcessDelay, viewModel.Weights, viewModel.ItemClasses); await _profileCreator.CreateProfile(data); } } @@ -91,7 +91,7 @@ private async Task EditProfile(ProfileViewModel profileViewModel) if (result == ProfileEditorResult.Apply) { Guard.IsNotNull(viewModel.Weights); - EditedProfileDataDTO data = new(profileViewModel.Profile, viewModel.Name, viewModel.Description, viewModel.DetectionThreshold, viewModel.MouseSensitivity, viewModel.Weights, viewModel.ItemClasses); + EditedProfileDataDTO data = new(profileViewModel.Profile, viewModel.Name, viewModel.Description, viewModel.DetectionThreshold, viewModel.MouseSensitivity, viewModel.PostProcessDelay, viewModel.Weights, viewModel.ItemClasses); await _profileEditor.ApplyChanges(data); } else if (result == ProfileEditorResult.Delete) diff --git a/SightKeeper.Avalonia/Views/Profiles/ProfileEditor.axaml b/SightKeeper.Avalonia/Views/Profiles/ProfileEditor.axaml index 036849f7..7d00e40f 100644 --- a/SightKeeper.Avalonia/Views/Profiles/ProfileEditor.axaml +++ b/SightKeeper.Avalonia/Views/Profiles/ProfileEditor.axaml @@ -54,6 +54,23 @@ Maximum="5" Value="{Binding MouseSensitivity}"/> + + + + diff --git a/SightKeeper.Data/Configuration/ProfileConfiguration.cs b/SightKeeper.Data/Configuration/ProfileConfiguration.cs index 755838e0..e079a1f0 100644 --- a/SightKeeper.Data/Configuration/ProfileConfiguration.cs +++ b/SightKeeper.Data/Configuration/ProfileConfiguration.cs @@ -13,5 +13,8 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(profile => profile.Name).IsUnique(); builder.Navigation(profile => profile.Weights).AutoInclude(); builder.Navigation(profile => profile.ItemClasses).AutoInclude(); + builder.Property(profile => profile.PostProcessDelay).HasConversion( + timeSpan => (ushort)timeSpan.TotalMilliseconds, + number => TimeSpan.FromMilliseconds(number)); } } \ No newline at end of file diff --git a/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.Designer.cs b/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.Designer.cs new file mode 100644 index 00000000..2151e908 --- /dev/null +++ b/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.Designer.cs @@ -0,0 +1,619 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SightKeeper.Data; + +#nullable disable + +namespace SightKeeper.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20230926083614_AddProfilePostProcessDelay")] + partial class AddProfilePostProcessDelay + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0-preview.6.23329.4"); + + modelBuilder.Entity("AssetWeights", b => + { + b.Property("AssetId") + .HasColumnType("INTEGER"); + + b.Property("WeightsId") + .HasColumnType("INTEGER"); + + b.HasKey("AssetId", "WeightsId"); + + b.HasIndex("WeightsId"); + + b.ToTable("WeightsAssets", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Asset", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("DataSetId") + .HasColumnType("INTEGER"); + + b.Property("Usage") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DataSetId"); + + b.ToTable("Assets", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ProcessName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Image", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Content") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id"); + + b.ToTable("Images", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.ItemClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Color") + .HasColumnType("INTEGER"); + + b.Property("DataSetId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DataSetId"); + + b.ToTable("ItemClasses", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.DataSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GameId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Resolution") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("DataSets"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Detector.DetectorItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AssetId") + .HasColumnType("INTEGER"); + + b.Property("ItemClassId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("AssetId"); + + b.HasIndex("ItemClassId"); + + b.ToTable("DetectorItems", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DetectionThreshold") + .HasColumnType("REAL"); + + b.Property("MouseSensitivity") + .HasColumnType("REAL"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PostProcessDelay") + .HasColumnType("INTEGER"); + + b.Property("WeightsId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("WeightsId"); + + b.ToTable("Profiles"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ProfileItemClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("ItemClassId") + .HasColumnType("INTEGER"); + + b.Property("ProfileId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ItemClassId"); + + b.HasIndex("ProfileId", "Index") + .IsUnique(); + + b.HasIndex("ProfileId", "ItemClassId") + .IsUnique(); + + b.ToTable("ProfileItemClasses", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Screenshot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("Screenshots", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ScreenshotsLibrary", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("HasAnyScreenshots") + .HasColumnType("INTEGER"); + + b.Property("MaxQuantity") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("ScreenshotsLibraries", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Weights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BoundingLoss") + .HasColumnType("REAL"); + + b.Property("ClassificationLoss") + .HasColumnType("REAL"); + + b.Property("DeformationLoss") + .HasColumnType("REAL"); + + b.Property("Epoch") + .HasColumnType("INTEGER"); + + b.Property("LibraryId") + .HasColumnType("INTEGER"); + + b.Property("Size") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId"); + + b.ToTable("Weights"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.WeightsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Content") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id"); + + b.ToTable("WeightsData"); + + b.UseTptMappingStrategy(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.WeightsLibrary", b => + { + b.Property("Id") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("WeightsLibraries", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ONNXData", b => + { + b.HasBaseType("SightKeeper.Domain.Model.WeightsData"); + + b.Property("WeightsId") + .HasColumnType("INTEGER"); + + b.HasIndex("WeightsId") + .IsUnique(); + + b.ToTable("ONNXData", (string)null); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.PTData", b => + { + b.HasBaseType("SightKeeper.Domain.Model.WeightsData"); + + b.Property("WeightsId") + .HasColumnType("INTEGER"); + + b.HasIndex("WeightsId") + .IsUnique(); + + b.ToTable("PTData", (string)null); + }); + + modelBuilder.Entity("AssetWeights", b => + { + b.HasOne("SightKeeper.Domain.Model.Common.Asset", null) + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Weights", null) + .WithMany() + .HasForeignKey("WeightsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Asset", b => + { + b.HasOne("SightKeeper.Domain.Model.DataSet", "DataSet") + .WithMany("Assets") + .HasForeignKey("DataSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Screenshot", "Screenshot") + .WithOne("Asset") + .HasForeignKey("SightKeeper.Domain.Model.Common.Asset", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DataSet"); + + b.Navigation("Screenshot"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Image", b => + { + b.HasOne("SightKeeper.Domain.Model.Screenshot", null) + .WithOne("Image") + .HasForeignKey("SightKeeper.Domain.Model.Common.Image", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.ItemClass", b => + { + b.HasOne("SightKeeper.Domain.Model.DataSet", "DataSet") + .WithMany("ItemClasses") + .HasForeignKey("DataSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DataSet"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.DataSet", b => + { + b.HasOne("SightKeeper.Domain.Model.Common.Game", "Game") + .WithMany() + .HasForeignKey("GameId"); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Detector.DetectorItem", b => + { + b.HasOne("SightKeeper.Domain.Model.Common.Asset", "Asset") + .WithMany("Items") + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Common.ItemClass", "ItemClass") + .WithMany("Items") + .HasForeignKey("ItemClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("SightKeeper.Domain.Model.Detector.Bounding", "Bounding", b1 => + { + b1.Property("DetectorItemId") + .HasColumnType("INTEGER"); + + b1.Property("Bottom") + .HasColumnType("REAL") + .HasColumnName("BoundingBottom"); + + b1.Property("Left") + .HasColumnType("REAL") + .HasColumnName("BoundingLeft"); + + b1.Property("Right") + .HasColumnType("REAL") + .HasColumnName("BoundingRight"); + + b1.Property("Top") + .HasColumnType("REAL") + .HasColumnName("BoundingTop"); + + b1.HasKey("DetectorItemId"); + + b1.ToTable("DetectorItems"); + + b1.WithOwner() + .HasForeignKey("DetectorItemId"); + }); + + b.Navigation("Asset"); + + b.Navigation("Bounding") + .IsRequired(); + + b.Navigation("ItemClass"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Profile", b => + { + b.HasOne("SightKeeper.Domain.Model.Weights", "Weights") + .WithMany() + .HasForeignKey("WeightsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Weights"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ProfileItemClass", b => + { + b.HasOne("SightKeeper.Domain.Model.Common.ItemClass", "ItemClass") + .WithMany() + .HasForeignKey("ItemClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Profile", null) + .WithMany("ItemClasses") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemClass"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Screenshot", b => + { + b.HasOne("SightKeeper.Domain.Model.ScreenshotsLibrary", "Library") + .WithMany("Screenshots") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ScreenshotsLibrary", b => + { + b.HasOne("SightKeeper.Domain.Model.DataSet", "DataSet") + .WithOne("ScreenshotsLibrary") + .HasForeignKey("SightKeeper.Domain.Model.ScreenshotsLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DataSet"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Weights", b => + { + b.HasOne("SightKeeper.Domain.Model.WeightsLibrary", "Library") + .WithMany("Weights") + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Library"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.WeightsLibrary", b => + { + b.HasOne("SightKeeper.Domain.Model.DataSet", "DataSet") + .WithOne("WeightsLibrary") + .HasForeignKey("SightKeeper.Domain.Model.WeightsLibrary", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DataSet"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ONNXData", b => + { + b.HasOne("SightKeeper.Domain.Model.WeightsData", null) + .WithOne() + .HasForeignKey("SightKeeper.Domain.Model.ONNXData", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Weights", null) + .WithOne("ONNXData") + .HasForeignKey("SightKeeper.Domain.Model.ONNXData", "WeightsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.PTData", b => + { + b.HasOne("SightKeeper.Domain.Model.WeightsData", null) + .WithOne() + .HasForeignKey("SightKeeper.Domain.Model.PTData", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SightKeeper.Domain.Model.Weights", null) + .WithOne("PTData") + .HasForeignKey("SightKeeper.Domain.Model.PTData", "WeightsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.Asset", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Common.ItemClass", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.DataSet", b => + { + b.Navigation("Assets"); + + b.Navigation("ItemClasses"); + + b.Navigation("ScreenshotsLibrary") + .IsRequired(); + + b.Navigation("WeightsLibrary") + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Profile", b => + { + b.Navigation("ItemClasses"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Screenshot", b => + { + b.Navigation("Asset"); + + b.Navigation("Image") + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.ScreenshotsLibrary", b => + { + b.Navigation("Screenshots"); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.Weights", b => + { + b.Navigation("ONNXData") + .IsRequired(); + + b.Navigation("PTData") + .IsRequired(); + }); + + modelBuilder.Entity("SightKeeper.Domain.Model.WeightsLibrary", b => + { + b.Navigation("Weights"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.cs b/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.cs new file mode 100644 index 00000000..3c1dbee5 --- /dev/null +++ b/SightKeeper.Data/Migrations/20230926083614_AddProfilePostProcessDelay.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SightKeeper.Data.Migrations +{ + /// + public partial class AddProfilePostProcessDelay : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PostProcessDelay", + table: "Profiles", + type: "INTEGER", + nullable: false, + defaultValue: (ushort)0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PostProcessDelay", + table: "Profiles"); + } + } +} diff --git a/SightKeeper.Data/Migrations/AppDbContextModelSnapshot.cs b/SightKeeper.Data/Migrations/AppDbContextModelSnapshot.cs index 968aff41..2401e115 100644 --- a/SightKeeper.Data/Migrations/AppDbContextModelSnapshot.cs +++ b/SightKeeper.Data/Migrations/AppDbContextModelSnapshot.cs @@ -177,6 +177,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("PostProcessDelay") + .HasColumnType("INTEGER"); + b.Property("WeightsId") .HasColumnType("INTEGER"); diff --git a/SightKeeper.Domain.Model/Profile/Profile.cs b/SightKeeper.Domain.Model/Profile/Profile.cs index c1084d77..8d0de110 100644 --- a/SightKeeper.Domain.Model/Profile/Profile.cs +++ b/SightKeeper.Domain.Model/Profile/Profile.cs @@ -28,6 +28,16 @@ public float MouseSensitivity } } + public TimeSpan PostProcessDelay + { + get => _postProcessDelay; + set + { + Guard.IsGreaterThanOrEqualTo(value, TimeSpan.Zero); + _postProcessDelay = value; + } + } + public Weights Weights { get => _weights; @@ -43,12 +53,13 @@ public Weights Weights public IReadOnlyList ItemClasses => _itemClasses; - public Profile(string name, string description, float detectionThreshold, float mouseSensitivity, Weights weights) + public Profile(string name, string description, float detectionThreshold, float mouseSensitivity, TimeSpan postProcessDelay, Weights weights) { Name = name; Description = description; DetectionThreshold = detectionThreshold; MouseSensitivity = mouseSensitivity; + _postProcessDelay = postProcessDelay; _weights = weights; _itemClasses = new List(); } @@ -95,6 +106,7 @@ public override string ToString() => private readonly List _itemClasses; private float _detectionThreshold; private float _mouseSensitivity; + private TimeSpan _postProcessDelay; private Profile() { diff --git a/SightKeeper.Services/Scoring/HotKeyProfileRunner.cs b/SightKeeper.Services/Scoring/HotKeyProfileRunner.cs index a5e6a176..5a9083cc 100644 --- a/SightKeeper.Services/Scoring/HotKeyProfileRunner.cs +++ b/SightKeeper.Services/Scoring/HotKeyProfileRunner.cs @@ -54,13 +54,17 @@ public byte MaximumFPS } } - private TimeSpan _screenshotingDelay = TimeSpan.FromSeconds(1f / _defaultMaximumFPS); + private TimeSpan _screenshotingDelay = TimeSpan.FromSeconds(1f / DefaultMaximumFPS); private DateTime _lastScreenshotTime = DateTime.UtcNow; + private const byte DefaultMaximumFPS = 1; - private const byte _defaultMaximumFPS = 1; - - public HotKeyProfileRunner(StreamDetector streamDetector, MouseMover mouseMover, SharpHookHotKeyManager hotKeyManager, ProfileEditor profileEditor, ScreenshotsDataAccess screenshotsDataAccess) + public HotKeyProfileRunner( + StreamDetector streamDetector, + MouseMover mouseMover, + SharpHookHotKeyManager hotKeyManager, + ProfileEditor profileEditor, + ScreenshotsDataAccess screenshotsDataAccess) { _streamDetector = streamDetector; _mouseMover = mouseMover; @@ -114,6 +118,19 @@ private void HandleDetection(byte[] imageData, ImmutableList item MoveTo(mostSuitableItem.Bounding); } TryMakeScreenshot(imageData, items); + if (_currentProfile.PostProcessDelay != TimeSpan.Zero) + { + if (_currentProfile.PostProcessDelay.TotalMilliseconds > 20) + Thread.Sleep(_currentProfile.PostProcessDelay); + else + BurnTime(_currentProfile.PostProcessDelay); + } + } + + private static void BurnTime(TimeSpan time) + { + var endTime = DateTime.UtcNow + time; + while (DateTime.UtcNow < endTime) { } } private async void TryMakeScreenshot(byte[] imageData, ImmutableList items) @@ -188,7 +205,7 @@ public void Stop() private Dictionary? _itemClassesIndexes; private float _minimumProbability = 0.2f; private float _maximumProbability = 0.5f; - private byte _maximumFPS = _defaultMaximumFPS; + private byte _maximumFPS = DefaultMaximumFPS; private void UpdateDetectionThreshold() { diff --git a/SightKeeper.Services/SharpHookExtensions.cs b/SightKeeper.Services/SharpHookExtensions.cs index c769338b..d30e3dcd 100644 --- a/SightKeeper.Services/SharpHookExtensions.cs +++ b/SightKeeper.Services/SharpHookExtensions.cs @@ -7,8 +7,7 @@ public static class SharpHookExtensions public static string ToStringEx(this KeyCode key) { var result = key.ToString(); - if (key != KeyCode.CharUndefined) - result = result[2..]; + result = result[2..]; return result; } } \ No newline at end of file diff --git a/SightKeeper.Services/SightKeeper.Services.csproj b/SightKeeper.Services/SightKeeper.Services.csproj index b5f40f4a..d2a8b80c 100644 --- a/SightKeeper.Services/SightKeeper.Services.csproj +++ b/SightKeeper.Services/SightKeeper.Services.csproj @@ -15,8 +15,8 @@ - - + +