Skip to content

Commit

Permalink
periodic autosaving
Browse files Browse the repository at this point in the history
  • Loading branch information
Neakita committed Oct 24, 2024
1 parent 406c990 commit 50cb43d
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 68 deletions.
17 changes: 14 additions & 3 deletions SightKeeper.Application/Screenshotting/ScreenshotsDataAccess.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reactive.Linq;
using System.Collections.Immutable;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using CommunityToolkit.HighPerformance;
using SightKeeper.Domain.Model;
Expand All @@ -21,7 +22,7 @@ public Screenshot CreateScreenshot(
DateTimeOffset creationDate)
{
Vector2<ushort> resolution = new((ushort)imageData.Width, (ushort)imageData.Height);
var screenshot = library.CreateScreenshot(creationDate, resolution, out var removedScreenshots);
var screenshot = CreateScreenshotInLibrary(library, creationDate, resolution, out var removedScreenshots);
foreach (var removedScreenshot in removedScreenshots)
{
DeleteScreenshotData(removedScreenshot);
Expand All @@ -32,6 +33,11 @@ public Screenshot CreateScreenshot(
return screenshot;
}

protected virtual Screenshot CreateScreenshotInLibrary(ScreenshotsLibrary library, DateTimeOffset creationDate, Vector2<ushort> resolution, out ImmutableArray<Screenshot> removedScreenshots)
{
return library.CreateScreenshot(creationDate, resolution, out removedScreenshots);
}

public Screenshot<TAsset> CreateScreenshot<TAsset>(
ScreenshotsLibrary<TAsset> library,
ReadOnlySpan2D<Rgba32> imageData,
Expand All @@ -49,7 +55,7 @@ public Screenshot<TAsset> CreateScreenshot<TAsset>(

public void DeleteScreenshot(Screenshot screenshot)
{
screenshot.DeleteFromLibrary();
DeleteScreenshotFromLibrary(screenshot);
DeleteScreenshotData(screenshot);
_removed.OnNext(screenshot);
}
Expand All @@ -60,6 +66,11 @@ public void Dispose()
_removed.Dispose();
}

protected virtual void DeleteScreenshotFromLibrary(Screenshot screenshot)
{
screenshot.DeleteFromLibrary();
}

protected abstract void SaveScreenshotData(Screenshot screenshot, ReadOnlySpan2D<Rgba32> image);
protected abstract void DeleteScreenshotData(Screenshot screenshot);

Expand Down
47 changes: 43 additions & 4 deletions SightKeeper.Application/WeightsDataAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public PlainWeights<TTag> CreateWeights<TTag>(
IEnumerable<TTag> tags,
Composition? composition) where TTag : Tag, MinimumTagsCount
{
var weights = library.CreateWeights(creationDate, modelSize, metrics, resolution, tags, composition);
var weights = CreateWeightsInLibrary(library, creationDate, modelSize, metrics, resolution, tags, composition);
SaveWeightsData(weights, data);
return weights;
}
Expand All @@ -36,27 +36,66 @@ public PoserWeights<TTag, TKeyPointTag> CreateWeights<TTag, TKeyPointTag>(
where TTag : PoserTag
where TKeyPointTag : KeyPointTag<TTag>
{
var weights = library.CreateWeights(creationDate, modelSize, metrics, resolution, tags, keyPointTags, composition);
var weights = CreateWeightsInLibrary(library, creationDate, modelSize, metrics, resolution, tags, keyPointTags, composition);
SaveWeightsData(weights, data);
return weights;
}

public void RemoveWeights<TTag>(PlainWeights<TTag> weights) where TTag : Tag, MinimumTagsCount
{
weights.Library.RemoveWeights(weights);
DeleteWeightsFromLibrary(weights);
RemoveWeightsData(weights);
}

public void RemoveWeights<TTag, TKeyPointTag>(PoserWeights<TTag, TKeyPointTag> weights)
where TTag : PoserTag
where TKeyPointTag : KeyPointTag<TTag>
{
weights.Library.RemoveWeights(weights);
DeleteWeightsFromLibrary(weights);
RemoveWeightsData(weights);
}

public abstract byte[] LoadWeightsData(Weights weights);

protected virtual PlainWeights<TTag> CreateWeightsInLibrary<TTag>(
WeightsLibrary<TTag> library,
DateTime creationDate,
ModelSize modelSize,
WeightsMetrics metrics,
Vector2<ushort> resolution,
IEnumerable<TTag> tags,
Composition? composition)
where TTag : Tag, MinimumTagsCount
{
return library.CreateWeights(creationDate, modelSize, metrics, resolution, tags, composition);
}

protected virtual PoserWeights<TTag, TKeyPointTag> CreateWeightsInLibrary<TTag, TKeyPointTag>(
WeightsLibrary<TTag, TKeyPointTag> library,
DateTime creationDate,
ModelSize modelSize,
WeightsMetrics metrics,
Vector2<ushort> resolution,
IEnumerable<TTag> tags,
IEnumerable<TKeyPointTag> keyPointTags,
Composition? composition)
where TTag : PoserTag where TKeyPointTag : KeyPointTag<TTag>
{
return library.CreateWeights(creationDate, modelSize, metrics, resolution, tags, keyPointTags, composition);
}

protected virtual void DeleteWeightsFromLibrary<TTag>(PlainWeights<TTag> weights)
where TTag : Tag, MinimumTagsCount
{
weights.Library.RemoveWeights(weights);
}

protected virtual void DeleteWeightsFromLibrary<TTag, TKeyPointTag>(PoserWeights<TTag, TKeyPointTag> weights)
where TTag : PoserTag where TKeyPointTag : KeyPointTag<TTag>
{
weights.Library.RemoveWeights(weights);
}

protected abstract void SaveWeightsData(Weights weights, byte[] data);
protected abstract void RemoveWeightsData(Weights weights);
}
20 changes: 13 additions & 7 deletions SightKeeper.Avalonia/Setup/ServicesBootstrapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Autofac;
using System;
using Autofac;
using FluentValidation;
using MemoryPack;
using SightKeeper.Application;
Expand All @@ -22,13 +23,11 @@ internal static class ServicesBootstrapper
public static void Setup(ContainerBuilder builder)
{
SetupBinarySerialization(builder);
builder.RegisterType<Data.Binary.Services.GamesDataAccess>().As<GamesDataAccess>().SingleInstance();
builder.RegisterType<ProcessesAvailableGamesProvider>();
builder.RegisterType<GameDataValidator>().As<IValidator<GameData>>();
builder.RegisterType<GameCreator>();
builder.RegisterType<DialogManager>().SingleInstance();
builder.RegisterGeneric(typeof(ObservableRepository<>)).SingleInstance();
builder.RegisterType<DataSetsDataAccess>().As<ObservableDataAccess<DataSet>>().As<ReadDataAccess<DataSet>>().As<WriteDataAccess<DataSet>>().SingleInstance();
builder.RegisterType<DataSetCreator>();
builder.RegisterType<DataSetEditor>().SingleInstance();
builder.RegisterType<SharpHookScreenBoundsProvider>().As<ScreenBoundsProvider>();
Expand All @@ -40,13 +39,20 @@ public static void Setup(ContainerBuilder builder)

private static void SetupBinarySerialization(ContainerBuilder builder)
{
FileSystemScreenshotsDataAccess screenshotsDataAccess = new();
FileSystemWeightsDataAccess weightsDataAccess = new();
MemoryPackFormatterProvider.Register(new AppDataFormatter(screenshotsDataAccess));
AppDataAccess appDataAccess = new();
object locker = new();
FileSystemScreenshotsDataAccess screenshotsDataAccess = new(appDataAccess, locker);
FileSystemWeightsDataAccess weightsDataAccess = new(appDataAccess, locker);
MemoryPackFormatterProvider.Register(new AppDataFormatter(screenshotsDataAccess, locker));
appDataAccess.Load();
builder.RegisterInstance(screenshotsDataAccess).AsSelf().As<ScreenshotsDataAccess>().As<ObservableDataAccess<Screenshot>>();
builder.RegisterInstance(weightsDataAccess).As<WeightsDataAccess>();
builder.RegisterInstance(appDataAccess).AsSelf().As<ApplicationSettingsProvider>().OnRelease(dataAccess => dataAccess.Save());
builder.RegisterInstance(appDataAccess).AsSelf().As<ApplicationSettingsProvider>();
DataSetsDataAccess dataSetsDataAccess = new(appDataAccess, locker, screenshotsDataAccess);
builder.RegisterInstance(dataSetsDataAccess).As<ObservableDataAccess<DataSet>>().As<ReadDataAccess<DataSet>>().As<WriteDataAccess<DataSet>>();
Data.Binary.Services.GamesDataAccess gamesDataAccess = new(appDataAccess, locker);
builder.RegisterInstance(gamesDataAccess).As<GamesDataAccess>();
PeriodicAppDataSaver periodicAppDataSaver = new(appDataAccess);
builder.RegisterInstance(periodicAppDataSaver).As<IDisposable>();
}
}
5 changes: 3 additions & 2 deletions SightKeeper.Data.Tests/BinarySerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ private static ReadOnlySpan2D<Rgba32> SampleImageData
public void ShouldSaveAndLoadAppData()
{
AppDataAccess dataAccess = new();
FileSystemScreenshotsDataAccess screenshotsDataAccess = new();
MemoryPackFormatterProvider.Register(new AppDataFormatter(screenshotsDataAccess));
object locker = new();
FileSystemScreenshotsDataAccess screenshotsDataAccess = new(dataAccess, locker);
MemoryPackFormatterProvider.Register(new AppDataFormatter(screenshotsDataAccess, locker));
Game game = new("PayDay 2", "payday2");
dataAccess.Data.Games.Add(game);
foreach (var dataSet in CreateDataSets(screenshotsDataAccess, game))
Expand Down
41 changes: 13 additions & 28 deletions SightKeeper.Data/Binary/AppDataFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
using System.Collections.Immutable;
using MemoryPack;
using MemoryPack;
using SightKeeper.Data.Binary.Conversion;
using SightKeeper.Data.Binary.Conversion.DataSets;
using SightKeeper.Data.Binary.Conversion.Profiles;
using SightKeeper.Data.Binary.Replication;
using SightKeeper.Data.Binary.Replication.DataSets;
using SightKeeper.Data.Binary.Replication.Profiles;
using SightKeeper.Data.Binary.Services;

namespace SightKeeper.Data.Binary;

public sealed class AppDataFormatter : MemoryPackFormatter<AppData>
{
public AppDataFormatter(FileSystemScreenshotsDataAccess screenshotsDataAccess)
public AppDataFormatter(FileSystemScreenshotsDataAccess screenshotsDataAccess, object conversionLock)
{
_screenshotsDataAccess = screenshotsDataAccess;
_conversionLock = conversionLock;
_converter = new AppDataConverter(screenshotsDataAccess);
_replicator = new AppDataReplicator(screenshotsDataAccess);
}

public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref AppData? value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
ConversionSession session = new();
var games = GamesConverter.Convert(value.Games, session);
MultiDataSetConverter dataSetConverter = new(_screenshotsDataAccess, session);
var dataSets = dataSetConverter.Convert(value.DataSets);
ProfileConverter profileConverter = new(session);
var profiles = profileConverter.Convert(value.Profiles).ToImmutableArray();
PackableAppData packed = new(
games,
dataSets,
profiles,
value.ApplicationSettings);
PackableAppData packed;
lock (_conversionLock)
packed = _converter.Convert(value);
writer.WritePackable(packed);
}

Expand All @@ -52,14 +41,10 @@ public override void Deserialize(ref MemoryPackReader reader, scoped ref AppData
value = null;
return;
}
ReplicationSession session = new();

var games = GameReplicator.Replicate(packed.Games, session);
MultiDataSetReplicator dataSetReplicator = new(_screenshotsDataAccess, session);
var dataSets = dataSetReplicator.Replicate(packed.DataSets);
var profiles = new ProfileReplicator(session).Replicate(packed.Profiles).ToHashSet();
value = new AppData(games, dataSets, profiles, packed.ApplicationSettings);
value = _replicator.Replicate(packed);
}

private readonly FileSystemScreenshotsDataAccess _screenshotsDataAccess;
private readonly object _conversionLock;
private readonly AppDataConverter _converter;
private readonly AppDataReplicator _replicator;
}
27 changes: 27 additions & 0 deletions SightKeeper.Data/Binary/Conversion/AppDataConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Immutable;
using SightKeeper.Data.Binary.Conversion.DataSets;
using SightKeeper.Data.Binary.Conversion.Profiles;
using SightKeeper.Data.Binary.Services;

namespace SightKeeper.Data.Binary.Conversion;

internal sealed class AppDataConverter
{
public AppDataConverter(FileSystemScreenshotsDataAccess screenshotsDataAccess)
{
_screenshotsDataAccess = screenshotsDataAccess;
}

public PackableAppData Convert(AppData data)
{
ConversionSession session = new();
var games = GamesConverter.Convert(data.Games, session);
MultiDataSetConverter dataSetConverter = new(_screenshotsDataAccess, session);
var dataSets = dataSetConverter.Convert(data.DataSets);
ProfileConverter profileConverter = new(session);
var profiles = profileConverter.Convert(data.Profiles).ToImmutableArray();
return new PackableAppData(games, dataSets, profiles, data.ApplicationSettings);
}

private readonly FileSystemScreenshotsDataAccess _screenshotsDataAccess;
}
25 changes: 25 additions & 0 deletions SightKeeper.Data/Binary/Replication/AppDataReplicator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using SightKeeper.Data.Binary.Replication.DataSets;
using SightKeeper.Data.Binary.Replication.Profiles;
using SightKeeper.Data.Binary.Services;

namespace SightKeeper.Data.Binary.Replication;

internal class AppDataReplicator
{
public AppDataReplicator(FileSystemScreenshotsDataAccess screenshotsDataAccess)
{
_screenshotsDataAccess = screenshotsDataAccess;
}

public AppData Replicate(PackableAppData packed)
{
ReplicationSession session = new();
var games = GameReplicator.Replicate(packed.Games, session);
MultiDataSetReplicator dataSetReplicator = new(_screenshotsDataAccess, session);
var dataSets = dataSetReplicator.Replicate(packed.DataSets);
var profiles = new ProfileReplicator(session).Replicate(packed.Profiles).ToHashSet();
return new AppData(games, dataSets, profiles, packed.ApplicationSettings);
}

private readonly FileSystemScreenshotsDataAccess _screenshotsDataAccess;
}
10 changes: 10 additions & 0 deletions SightKeeper.Data/Binary/Services/AppDataAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,17 @@ public void Load()

public void Save()
{
if (!_dataChangedSinceLastSave)
return;
var serializedData = MemoryPackSerializer.Serialize(Data);
File.WriteAllBytes(FilePath, serializedData);
_dataChangedSinceLastSave = false;
}

internal void SetDataChanged()
{
_dataChangedSinceLastSave = true;
}

private bool _dataChangedSinceLastSave;
}
22 changes: 18 additions & 4 deletions SightKeeper.Data/Binary/Services/DataSetsDataAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,44 @@

namespace SightKeeper.Data.Binary.Services;

public sealed class DataSetsDataAccess : ReadDataAccess<DataSet>, ObservableDataAccess<DataSet>, WriteDataAccess<DataSet>
public sealed class DataSetsDataAccess : ReadDataAccess<DataSet>, ObservableDataAccess<DataSet>,
WriteDataAccess<DataSet>
{
public IReadOnlyCollection<DataSet> Items => _appDataAccess.Data.DataSets;
public IObservable<DataSet> Added => _added.AsObservable();
public IObservable<DataSet> Removed => _removed.AsObservable();

public DataSetsDataAccess(AppDataAccess appDataAccess)
public DataSetsDataAccess(AppDataAccess appDataAccess, object locker, FileSystemScreenshotsDataAccess screenshotsDataAccess)
{
_appDataAccess = appDataAccess;
_locker = locker;
_screenshotsDataAccess = screenshotsDataAccess;
}

public void Add(DataSet dataSet)
{
Guard.IsTrue(_appDataAccess.Data.DataSets.Add(dataSet));
bool isAdded;
lock (_locker)
isAdded = _appDataAccess.Data.DataSets.Add(dataSet);
Guard.IsTrue(isAdded);
_appDataAccess.SetDataChanged();
_added.OnNext(dataSet);
}

public void Remove(DataSet dataSet)
{
Guard.IsTrue(_appDataAccess.Data.DataSets.Remove(dataSet));
bool isRemoved;
lock (_locker)
isRemoved = _appDataAccess.Data.DataSets.Remove(dataSet);
_screenshotsDataAccess.DeleteAllScreenshotsData(dataSet.ScreenshotsLibrary);
Guard.IsTrue(isRemoved);
_appDataAccess.SetDataChanged();
_removed.OnNext(dataSet);
}

private readonly AppDataAccess _appDataAccess;
private readonly object _locker;
private readonly FileSystemScreenshotsDataAccess _screenshotsDataAccess;
private readonly Subject<DataSet> _added = new();
private readonly Subject<DataSet> _removed = new();
}
Loading

0 comments on commit 50cb43d

Please sign in to comment.