baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
30
src/Persistence/InMemory/ConfigurationInMemoryContext.cs
Normal file
30
src/Persistence/InMemory/ConfigurationInMemoryContext.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// <copyright file="ConfigurationInMemoryContext.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using System.Threading;
|
||||
using MUnique.OpenMU.Persistence.BasicModel;
|
||||
|
||||
/// <summary>
|
||||
/// A context which is used to access the configuration data in-memory.
|
||||
/// </summary>
|
||||
public class ConfigurationInMemoryContext : InMemoryContext, IConfigurationContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationInMemoryContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The manager which holds the memory repositories.</param>
|
||||
public ConfigurationInMemoryContext(InMemoryRepositoryProvider provider)
|
||||
: base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<Guid?> GetDefaultGameConfigurationIdAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var allConfigs = await this.Provider.GetRepository<GameConfiguration>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allConfigs.FirstOrDefault()?.Id;
|
||||
}
|
||||
}
|
||||
86
src/Persistence/InMemory/FriendServerInMemoryContext.cs
Normal file
86
src/Persistence/InMemory/FriendServerInMemoryContext.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// <copyright file="FriendServerInMemoryContext.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Persistence.BasicModel;
|
||||
using Friend = MUnique.OpenMU.Interfaces.Friend;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory context implementation for <see cref="IFriendServerContext"/>.
|
||||
/// </summary>
|
||||
public class FriendServerInMemoryContext : InMemoryContext, IFriendServerContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FriendServerInMemoryContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The manager which holds the memory repositories.</param>
|
||||
public FriendServerInMemoryContext(InMemoryRepositoryProvider provider)
|
||||
: base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<Friend> CreateNewFriendAsync(string characterName, string friendName)
|
||||
{
|
||||
var friend = this.CreateNew<MUnique.OpenMU.Interfaces.Friend>();
|
||||
friend.FriendId = (await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false)).FirstOrDefault(character => character.Name == friendName)?.Id ?? Guid.Empty;
|
||||
friend.CharacterId = (await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false)).FirstOrDefault(character => character.Name == characterName)?.Id ?? Guid.Empty;
|
||||
return friend;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<Friend?> GetFriendByNamesAsync(string characterName, string friendName)
|
||||
{
|
||||
var friendId = (await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false)).FirstOrDefault(character => character.Name == friendName)?.Id;
|
||||
var characterId = (await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false)).FirstOrDefault(character => character.Name == characterName)?.Id;
|
||||
|
||||
return (await this.Provider.GetRepository<Friend>().GetAllAsync().ConfigureAwait(false)).FirstOrDefault(f => f.FriendId == friendId && f.CharacterId == characterId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IEnumerable<FriendViewItem>> GetFriendsAsync(Guid characterId)
|
||||
{
|
||||
var characters = await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false);
|
||||
return (await this.Provider.GetRepository<Friend>().GetAllAsync().ConfigureAwait(false)).Where(f => f.CharacterId == characterId)
|
||||
.Select(f => (Friend: f, CharacterName: characters.FirstOrDefault(c => c.Id == f.CharacterId)?.Name, FriendName: characters.FirstOrDefault(c => c.Id == f.FriendId)?.Name))
|
||||
.Where(f => f.CharacterName is not null && f.FriendName is not null)
|
||||
.Select(f => new FriendViewItem(f.CharacterName!, f.FriendName!)
|
||||
{
|
||||
Accepted = f!.Friend.Accepted,
|
||||
CharacterId = f.Friend.CharacterId,
|
||||
FriendId = f.Friend.FriendId,
|
||||
Id = f.Friend.Id,
|
||||
RequestOpen = f.Friend.RequestOpen,
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<IEnumerable<string>> GetFriendNamesAsync(Guid characterId)
|
||||
{
|
||||
var characters = await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false);
|
||||
return (await this.Provider.GetRepository<Friend>().GetAllAsync().ConfigureAwait(false)).Where(f => f.CharacterId == characterId)
|
||||
.Select(f => characters.FirstOrDefault(c => c.Id == f.FriendId)?.Name!)
|
||||
.Where(name => name is not null);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask DeleteAsync(string characterName, string friendName)
|
||||
{
|
||||
if (await this.GetFriendByNamesAsync(characterName, friendName).ConfigureAwait(false) is { } item)
|
||||
{
|
||||
await this.DeleteAsync(item).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IEnumerable<string>> GetOpenFriendRequesterNamesAsync(Guid characterId)
|
||||
{
|
||||
var characters = await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false);
|
||||
return (await this.Provider.GetRepository<Friend>().GetAllAsync().ConfigureAwait(false)).Where(f => f.FriendId == characterId && f.RequestOpen)
|
||||
.Select(f => characters.FirstOrDefault(c => c.Id == f.CharacterId)?.Name!)
|
||||
.Where(name => name is not null);
|
||||
}
|
||||
}
|
||||
50
src/Persistence/InMemory/GuildServerInMemoryContext.cs
Normal file
50
src/Persistence/InMemory/GuildServerInMemoryContext.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
// <copyright file="GuildServerInMemoryContext.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using MUnique.OpenMU.Persistence.BasicModel;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory context implementation for <see cref="IGuildServerContext"/>.
|
||||
/// </summary>
|
||||
public class GuildServerInMemoryContext : InMemoryContext, IGuildServerContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GuildServerInMemoryContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The manager which holds the memory repositories.</param>
|
||||
public GuildServerInMemoryContext(InMemoryRepositoryProvider provider)
|
||||
: base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> GuildWithNameExistsAsync(string name)
|
||||
{
|
||||
return (await this.Provider.GetRepository<DataModel.Entities.Guild>().GetAllAsync().ConfigureAwait(false)).Any(g => g.Name == name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IReadOnlyDictionary<Guid, string>> GetMemberNamesAsync(Guid guildId)
|
||||
{
|
||||
var members = (await this.Provider.GetRepository<GuildMember>().GetAllAsync().ConfigureAwait(false))
|
||||
.Where(member => member.GuildId == guildId);
|
||||
var characters = await this.Provider.GetRepository<Character>().GetAllAsync().ConfigureAwait(false);
|
||||
return members
|
||||
.Select(m => (m.Id, Name: characters.FirstOrDefault(c => c.Id == m.Id)?.Name!))
|
||||
.Where(m => m.Name is not null)
|
||||
.ToDictionary(m => m.Id, m => m.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IReadOnlyList<DataModel.Entities.Guild>> GetAlliancesAsync(Guid guildId)
|
||||
{
|
||||
return (await this.Provider.GetRepository<DataModel.Entities.Guild>()
|
||||
.GetAllAsync()
|
||||
.ConfigureAwait(false))
|
||||
.Where(g => g.AllianceGuild?.GetId() == guildId)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
29
src/Persistence/InMemory/IMemoryRepository.cs
Normal file
29
src/Persistence/InMemory/IMemoryRepository.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// <copyright file="IMemoryRepository.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a memory repository, which allows to modify its stored items.
|
||||
/// </summary>
|
||||
public interface IMemoryRepository : IRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the specified object with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="obj">The object.</param>
|
||||
void Add(Guid key, object obj);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the object with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
ValueTask RemoveAsync(Guid key);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the context saves the changes.
|
||||
/// </summary>
|
||||
void OnSaveChanges();
|
||||
}
|
||||
177
src/Persistence/InMemory/InMemoryContext.cs
Normal file
177
src/Persistence/InMemory/InMemoryContext.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
// <copyright file="InMemoryContext.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
using Nito.AsyncEx.Synchronous;
|
||||
using Nito.Disposables;
|
||||
|
||||
/// <summary>
|
||||
/// An in-memory context which get it's data from the repositories of the <see cref="InMemoryPersistenceContextProvider"/>.
|
||||
/// </summary>
|
||||
public class InMemoryContext : IContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InMemoryContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The manager which holds the memory repositories.</param>
|
||||
public InMemoryContext(InMemoryRepositoryProvider provider)
|
||||
{
|
||||
this.Provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when changes have been "saved".
|
||||
/// </summary>
|
||||
public event EventHandler? SavedChanges;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasChanges => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manager which holds the memory repositories.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The manager.
|
||||
/// </value>
|
||||
protected InMemoryRepositoryProvider Provider { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.SavedChanges = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the changes of the context.
|
||||
/// </summary>
|
||||
/// <returns><c>True</c>, if the saving was successful; <c>false</c>, otherwise.</returns>
|
||||
public bool SaveChanges()
|
||||
{
|
||||
foreach (var repository in this.Provider.MemoryRepositories)
|
||||
{
|
||||
repository.OnSaveChanges();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = this.SaveChanges();
|
||||
if (result)
|
||||
{
|
||||
this.SavedChanges?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable SuspendChangeNotifications()
|
||||
{
|
||||
return new Disposable(() => { });
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Detach(object item)
|
||||
{
|
||||
if (item is IIdentifiable identifiable)
|
||||
{
|
||||
var repository = this.Provider.GetRepository(item.GetType()) as IMemoryRepository;
|
||||
repository?.RemoveAsync(identifiable.Id).AsTask().WaitWithoutException();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Attach(object item)
|
||||
{
|
||||
if (item is IIdentifiable identifiable)
|
||||
{
|
||||
var repository = this.Provider.GetRepository(item.GetType()) as IMemoryRepository;
|
||||
repository?.Add(identifiable.Id, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T CreateNew<T>(params object?[] args)
|
||||
where T : class
|
||||
{
|
||||
var newObject = typeof(Persistence.BasicModel.GameConfiguration).Assembly.CreateNew<T>(args);
|
||||
if (newObject is IIdentifiable identifiable)
|
||||
{
|
||||
if (identifiable.Id == Guid.Empty)
|
||||
{
|
||||
identifiable.Id = GuidV7.NewGuid();
|
||||
}
|
||||
|
||||
var repository = this.Provider.GetRepository<T>() as IMemoryRepository;
|
||||
repository?.Add(identifiable.Id, newObject);
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object CreateNew(Type type, params object?[] args)
|
||||
{
|
||||
var newObject = typeof(Persistence.BasicModel.GameConfiguration).Assembly.CreateNew(type, args);
|
||||
if (newObject is IIdentifiable identifiable)
|
||||
{
|
||||
if (identifiable.Id == Guid.Empty)
|
||||
{
|
||||
identifiable.Id = GuidV7.NewGuid();
|
||||
}
|
||||
|
||||
var repository = this.Provider.GetRepository(type) as IMemoryRepository;
|
||||
repository?.Add(identifiable.Id, newObject);
|
||||
}
|
||||
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<bool> DeleteAsync<T>(T obj)
|
||||
where T : class
|
||||
{
|
||||
return this.Provider.GetRepository<T>().DeleteAsync(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<T?> GetByIdAsync<T>(Guid id, CancellationToken cancellationToken)
|
||||
where T : class
|
||||
{
|
||||
return await this.Provider.GetRepository<T>().GetByIdAsync(id, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<object?> GetByIdAsync(Guid id, Type type, CancellationToken cancellationToken)
|
||||
{
|
||||
return await this.Provider.GetRepository(type).GetByIdAsync(id, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<IEnumerable<T>> GetAsync<T>(CancellationToken cancellationToken)
|
||||
where T : class
|
||||
{
|
||||
return this.Provider.GetRepository<T>().GetAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<IEnumerable> GetAsync(Type type, CancellationToken cancellationToken)
|
||||
{
|
||||
return this.Provider.GetRepository(type).GetAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSupporting(Type type)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
171
src/Persistence/InMemory/InMemoryPersistenceContextProvider.cs
Normal file
171
src/Persistence/InMemory/InMemoryPersistenceContextProvider.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
// <copyright file="InMemoryPersistenceContextProvider.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using System.Threading;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
using Nito.Disposables;
|
||||
|
||||
/// <summary>
|
||||
/// A context provider which uses in-memory repositories to hold its data, e.g. for testing or demo purposes.
|
||||
/// Changes in one context directly have effect in other contexts! Calling SaveChanges or not doesn't matter.
|
||||
/// </summary>
|
||||
public class InMemoryPersistenceContextProvider : IMigratableDatabaseContextProvider
|
||||
{
|
||||
private InMemoryRepositoryProvider _repositoryProvider = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InMemoryPersistenceContextProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="changePublisher">The change publisher.</param>
|
||||
public InMemoryPersistenceContextProvider(IConfigurationChangePublisher? changePublisher = null)
|
||||
{
|
||||
this.ChangePublisher = changePublisher;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRepositoryProvider RepositoryProvider => this._repositoryProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the publisher for configuration changes.
|
||||
/// </summary>
|
||||
public IConfigurationChangePublisher? ChangePublisher { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IContext CreateNewContext()
|
||||
{
|
||||
var context = new InMemoryContext(this._repositoryProvider);
|
||||
this.AttachChangePublisher(context, typeof(PlugInConfiguration));
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IContext CreateNewContext(GameConfiguration gameConfiguration)
|
||||
{
|
||||
var context = new InMemoryContext(this._repositoryProvider);
|
||||
this.AttachChangePublisher(context, typeof(PlugInConfiguration));
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IContext CreateNewTradeContext()
|
||||
{
|
||||
return new InMemoryContext(this._repositoryProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IPlayerContext CreateNewPlayerContext(GameConfiguration gameConfiguration)
|
||||
{
|
||||
return new PlayerInMemoryContext(this._repositoryProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IConfigurationContext CreateNewConfigurationContext()
|
||||
{
|
||||
return new ConfigurationInMemoryContext(this._repositoryProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFriendServerContext CreateNewFriendServerContext()
|
||||
{
|
||||
return new FriendServerInMemoryContext(this._repositoryProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IGuildServerContext CreateNewGuildContext()
|
||||
{
|
||||
return new GuildServerInMemoryContext(this._repositoryProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IContext CreateNewTypedContext(Type editType, bool useCache, GameConfiguration? gameConfiguration = null)
|
||||
{
|
||||
var context = new InMemoryContext(this._repositoryProvider);
|
||||
this.AttachChangePublisher(context, editType);
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> DatabaseExistsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> IsDatabaseUpToDateAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ApplyAllPendingUpdatesAsync()
|
||||
{
|
||||
// we don't need to do anything.
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task WaitForUpdatedDatabaseAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> CanConnectToDatabaseAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> ShouldDoAutoSchemaUpdateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IDisposable> ReCreateDatabaseAsync()
|
||||
{
|
||||
this._repositoryProvider = new();
|
||||
return Task.FromResult<IDisposable>(new Disposable(() => { }));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetCache()
|
||||
{
|
||||
// do nothing here
|
||||
}
|
||||
|
||||
private void AttachChangePublisher(InMemoryContext context, Type editType)
|
||||
{
|
||||
if (this.ChangePublisher is { } changePublisher)
|
||||
{
|
||||
#pragma warning disable VSTHRD100
|
||||
async void OnContextOnSavedChanges(object? o, EventArgs e)
|
||||
#pragma warning restore VSTHRD100
|
||||
{
|
||||
await this.PublishConfigurationChangesAsync(context, changePublisher, editType).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
context.SavedChanges += OnContextOnSavedChanges;
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask PublishConfigurationChangesAsync(InMemoryContext context, IConfigurationChangePublisher changePublisher, Type editType)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var obj in await context.GetAsync(editType, default).ConfigureAwait(false))
|
||||
{
|
||||
await changePublisher.ConfigurationChangedAsync(editType, obj.GetId(), obj).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore all errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/Persistence/InMemory/InMemoryRepositoryAdapter.cs
Normal file
43
src/Persistence/InMemory/InMemoryRepositoryAdapter.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// <copyright file="InMemoryRepositoryAdapter.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="RepositoryAdapter{T}"/> which implements <see cref="IMemoryRepository"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the saved objects.</typeparam>
|
||||
public class InMemoryRepositoryAdapter<T> : RepositoryAdapter<T>, IMemoryRepository
|
||||
where T : class
|
||||
{
|
||||
private readonly IMemoryRepository _repository;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InMemoryRepositoryAdapter{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository.</param>
|
||||
public InMemoryRepositoryAdapter(IMemoryRepository repository)
|
||||
: base(repository)
|
||||
{
|
||||
this._repository = repository;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Add(Guid key, object obj)
|
||||
{
|
||||
this._repository.Add(key, obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask RemoveAsync(Guid key)
|
||||
{
|
||||
return this._repository.RemoveAsync(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnSaveChanges()
|
||||
{
|
||||
this._repository.OnSaveChanges();
|
||||
}
|
||||
}
|
||||
53
src/Persistence/InMemory/InMemoryRepositoryProvider.cs
Normal file
53
src/Persistence/InMemory/InMemoryRepositoryProvider.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// <copyright file="InMemoryRepositoryProvider.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using MUnique.OpenMU.Persistence.BasicModel;
|
||||
|
||||
/// <summary>
|
||||
/// A repository provider which creates new in-memory repositories on-demand.
|
||||
/// </summary>
|
||||
public class InMemoryRepositoryProvider : BaseRepositoryProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all <see cref="IMemoryRepository"/> which were added to this manager.
|
||||
/// </summary>
|
||||
internal IEnumerable<IMemoryRepository> MemoryRepositories => base.Repositories.Values.OfType<IMemoryRepository>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the memory repository, and creates it if it wasn't created yet.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the business object.</typeparam>
|
||||
/// <returns>The memory repository.</returns>
|
||||
public new IRepository<T> GetRepository<T>()
|
||||
where T : class
|
||||
{
|
||||
var repository = this.InternalGetRepository(typeof(T)) ?? this.CreateAndRegisterMemoryRepository(typeof(T));
|
||||
return new InMemoryRepositoryAdapter<T>((IMemoryRepository)repository);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repository of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <returns>The repository of the specified type.</returns>
|
||||
public new IRepository GetRepository(Type objectType)
|
||||
{
|
||||
var repository = this.InternalGetRepository(objectType) ?? this.CreateAndRegisterMemoryRepository(objectType);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
private IRepository CreateAndRegisterMemoryRepository(Type type)
|
||||
{
|
||||
var baseModelAssembly = typeof(GameConfiguration).Assembly;
|
||||
var persistentType = baseModelAssembly.GetPersistentTypeOf(type) ?? type;
|
||||
var repositoryType = typeof(MemoryRepository<>).MakeGenericType(persistentType);
|
||||
var repository = (IRepository)Activator.CreateInstance(repositoryType)!;
|
||||
var baseType = type.Assembly == baseModelAssembly ? type.BaseType ?? type : type;
|
||||
this.RegisterRepository(baseType, repository!);
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<WarningsAsErrors>nullable;CS4014;VSTHRD110;VSTHRD100</WarningsAsErrors>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\..\..\bin\Debug\</OutputPath>
|
||||
<DocumentationFile>..\..\..\bin\Debug\MUnique.OpenMU.Persistence.InMemory.xml</DocumentationFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<OutputPath>..\..\bin\Release\</OutputPath>
|
||||
<DocumentationFile>..\..\bin\Release\MUnique.OpenMU.Persistence.InMemory.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\DataModel\MUnique.OpenMU.DataModel.csproj" />
|
||||
<ProjectReference Include="..\..\Interfaces\MUnique.OpenMU.Interfaces.csproj" />
|
||||
<ProjectReference Include="..\MUnique.OpenMU.Persistence.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
111
src/Persistence/InMemory/MemoryRepository{TValue}.cs
Normal file
111
src/Persistence/InMemory/MemoryRepository{TValue}.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
// <copyright file="MemoryRepository{TValue}.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using System.Collections;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// A repository which lives on memory only.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
public class MemoryRepository<TValue> : IRepository<TValue>, IMemoryRepository
|
||||
where TValue : class
|
||||
{
|
||||
private readonly List<Guid> _createdObjects = new();
|
||||
private readonly IDictionary<Guid, TValue> _values = new Dictionary<Guid, TValue>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the repository.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="obj">The item.</param>
|
||||
public void Add(Guid key, TValue obj)
|
||||
{
|
||||
this._values.TryAdd(key, obj);
|
||||
this._createdObjects.Add(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Add(Guid key, object obj)
|
||||
{
|
||||
if (obj is TValue value)
|
||||
{
|
||||
this.Add(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Given object is not of type {typeof(TValue)}", nameof(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask RemoveAsync(Guid key)
|
||||
{
|
||||
await this.DeleteAsync(key).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<TValue?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
this._values.TryGetValue(id, out var obj);
|
||||
return ValueTask.FromResult(obj);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnSaveChanges()
|
||||
{
|
||||
foreach (var oldObjId in this._createdObjects)
|
||||
{
|
||||
if (this._values.TryGetValue(oldObjId, out var obj)
|
||||
&& obj.GetId() is var currentId
|
||||
&& currentId != oldObjId)
|
||||
{
|
||||
this._values.Remove(oldObjId);
|
||||
if (!this._values.TryAdd(currentId, obj))
|
||||
{
|
||||
throw new InvalidOperationException($"Duplicate ID {currentId}. Existing: {this._values[currentId]}, Wanted to add: {obj}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._createdObjects.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
async ValueTask<object?> IRepository.GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await this.GetByIdAsync(id, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<bool> DeleteAsync(object obj)
|
||||
{
|
||||
var key = this._values.Where(kvp => kvp.Value.Equals(obj)).Select(kvp => kvp.Key).FirstOrDefault();
|
||||
return ValueTask.FromResult(this._values.Remove(key));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<bool> DeleteAsync(Guid id)
|
||||
{
|
||||
return ValueTask.FromResult(this._values.Remove(id));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IEnumerable<TValue>> GetAllAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return this._values.Values.Cast<TValue>();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
async ValueTask<IEnumerable> IRepository.GetAllAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return this._values.Values;
|
||||
}
|
||||
}
|
||||
72
src/Persistence/InMemory/PlayerInMemoryContext.cs
Normal file
72
src/Persistence/InMemory/PlayerInMemoryContext.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
// <copyright file="PlayerInMemoryContext.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.InMemory;
|
||||
|
||||
using System.Threading;
|
||||
using MUnique.OpenMU.Persistence.BasicModel;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory context implementation for <see cref="IPlayerContext"/>.
|
||||
/// </summary>
|
||||
public class PlayerInMemoryContext : InMemoryContext, IPlayerContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerInMemoryContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The manager which holds the memory repositories.</param>
|
||||
public PlayerInMemoryContext(InMemoryRepositoryProvider provider)
|
||||
: base(provider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<MUnique.OpenMU.DataModel.Entities.LetterBody?> GetLetterBodyByHeaderIdAsync(Guid headerId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allLetters = await this.Provider.GetRepository<LetterBody>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allLetters.FirstOrDefault(body => body.Header.Id == headerId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<MUnique.OpenMU.DataModel.Entities.AccountState?> AuthenticateAsync(string loginName, string password, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allAccounts = await this.Provider.GetRepository<Account>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
var account = allAccounts.FirstOrDefault(a => a.LoginName == loginName && BCrypt.Net.BCrypt.Verify(password, a.PasswordHash));
|
||||
return account?.State;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<MUnique.OpenMU.DataModel.Entities.Account?> GetAccountByLoginNameAsync(string loginName, string password, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allAccounts = await this.Provider.GetRepository<Account>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allAccounts.FirstOrDefault(account => account.LoginName == loginName && BCrypt.Net.BCrypt.Verify(password, account.PasswordHash));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<MUnique.OpenMU.DataModel.Entities.Account?> GetAccountByLoginNameAsync(string loginName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allAccounts = await this.Provider.GetRepository<Account>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allAccounts.FirstOrDefault(account => account.LoginName == loginName);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IEnumerable<MUnique.OpenMU.DataModel.Entities.Account>> GetAccountsOrderedByLoginNameAsync(int skip, int count, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allAccounts = await this.Provider.GetRepository<Account>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allAccounts.OrderBy(a => a.LoginName).Skip(skip).Take(count);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<bool> CanSaveLetterAsync(Interfaces.LetterHeader letterHeader, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<DataModel.Entities.Account?> GetAccountByCharacterNameAsync(string characterName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allAccounts = await this.Provider.GetRepository<Account>().GetAllAsync(cancellationToken).ConfigureAwait(false);
|
||||
return allAccounts.FirstOrDefault(account => account.Characters.Any(c => c.Name == characterName));
|
||||
}
|
||||
}
|
||||
10
src/Persistence/InMemory/Properties/AssemblyInfo.cs
Normal file
10
src/Persistence/InMemory/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// <copyright file="AssemblyInfo.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MUnique.OpenMU.Persistence.InMemory")]
|
||||
Reference in New Issue
Block a user