// // Game.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Extensions.Options; using NosSmooth.Core.Stateful; using NosSmooth.Game.Data; using NosSmooth.Game.Data.Characters; using NosSmooth.Game.Data.Chat; using NosSmooth.Game.Data.Inventory; using NosSmooth.Game.Data.Maps; using NosSmooth.Game.Data.Mates; using NosSmooth.Game.Data.Raids; using NosSmooth.Game.Data.Social; namespace NosSmooth.Game; /// /// Represents base nostale game class with the character, current map, friends and current raid. /// public class Game : IStatefulEntity { private readonly GameOptions _options; private Map? _currentMap; /// /// Initializes a new instance of the class. /// /// The options for the game. public Game(IOptions options) { Semaphores = new GameSemaphores(); _options = options.Value; } /// /// Gets the game semaphores. /// internal GameSemaphores Semaphores { get; } /// /// Gets the playing character of the client. /// public Character? Character { get; internal set; } /// /// Gets the mates of the current character. /// public Mates? Mates { get; internal set; } /// /// Gets or sets the inventory of the character. /// public Inventory? Inventory { get; internal set; } /// /// Get or sets the friends of the character. /// public IReadOnlyList? Friends { get; internal set; } /// /// Gets or sets the skills of the player. /// public Skills? Skills { get; internal set; } /// /// Gets or sets the family. /// public Family? Family { get; internal set; } /// /// Gets or sets the group the player is in. /// public Group? Group { get; internal set; } /// /// Gets the current map of the client. /// /// /// Will be null until current map packet is received. /// public Map? CurrentMap { get => _currentMap; internal set { _currentMap = value; } } /// /// Gets the active raid the client is currently on. /// /// /// May be null if there is no raid in progress. /// public Raid? CurrentRaid { get; internal set; } /// /// Creates the mates if they are null, or updates the current mates. /// /// The function for creating the mates. /// The function for updating the mates. /// Whether to release the semaphore used for changing the mates. /// The cancellation token for cancelling the operation. /// The updated mates. internal Task CreateOrUpdateMatesAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Mates, () => Mates, s => Mates = s, create, update, releaseSemaphore, ct ); } /// /// Creates the skills if they are null, or updates the current skills. /// /// The function for creating the skills. /// The function for updating the skills. /// Whether to release the semaphore used for changing the skills. /// The cancellation token for cancelling the operation. /// The updated skills. internal Task CreateOrUpdateSkillsAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Skills, () => Skills, s => Skills = s, create, update, releaseSemaphore, ct ); } /// /// Creates the inventory if it is null, or updates the current inventory. /// /// The function for creating the inventory. /// The function for updating the inventory. /// Whether to release the semaphore used for changing the inventory. /// The cancellation token for cancelling the operation. /// The updated inventory. internal Task CreateOrUpdateInventoryAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Inventory, () => Inventory, i => Inventory = i, create, update, releaseSemaphore, ct ); } /// /// Creates the family if it is null, or updates the current family. /// /// The function for creating the family. /// The function for updating the family. /// Whether to release the semaphore used for changing the family. /// The cancellation token for cancelling the operation. /// The updated family. internal async Task CreateOrUpdateFamilyAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { var family = await CreateOrUpdateAsync ( GameSemaphoreType.Family, () => Family, c => Family = c, create, update, releaseSemaphore, ct ); await CreateOrUpdateCharacterAsync ( () => new Character { Family = family }, c => { c.Family = family; return c; }, ct: ct ); return family; } /// /// Creates the friends if it is null, or updates the current friends. /// /// The function for creating the friends. /// The function for updating the friends. /// Whether to release the semaphore used for changing the friends. /// The cancellation token for cancelling the operation. /// The updated friends. internal Task?> CreateOrUpdateFriendsAsync ( Func?> create, Func, IReadOnlyList?> update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Friends, () => Friends, c => Friends = c, create, update, releaseSemaphore, ct ); } /// /// Creates the group if it is null, or updates the current group. /// /// The function for creating the group. /// The function for updating the group. /// Whether to release the semaphore used for changing the group. /// The cancellation token for cancelling the operation. /// The updated group. internal Task CreateOrUpdateGroupAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Group, () => Group, c => Group = c, create, update, releaseSemaphore, ct ); } /// /// Creates the character if it is null, or updates the current character. /// /// The function for creating the character. /// The function for updating the character. /// Whether to release the semaphore used for changing the character. /// The cancellation token for cancelling the operation. /// The updated character. internal Task CreateOrUpdateCharacterAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Character, () => Character, c => Character = c, create, update, releaseSemaphore, ct ); } /// /// Creates the map if it is null, or updates the current map. /// /// The function for creating the map. /// Whether to release the semaphore used for changing the map. /// The cancellation token for cancelling the operation. /// The updated character. internal Task CreateMapAsync ( Func create, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateAsync ( GameSemaphoreType.Map, m => CurrentMap = m, create, releaseSemaphore, ct ); } /// /// Creates the map if it is null, or updates the current map. /// /// The function for creating the map. /// The function for updating the map. /// Whether to release the semaphore used for changing the map. /// The cancellation token for cancelling the operation. /// The updated character. internal Task CreateOrUpdateMapAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Map, () => CurrentMap, m => CurrentMap = m, create, update, releaseSemaphore, ct ); } /// /// Updates the current raid, if it is not null. /// /// The function for updating the raid. /// Whether to release the semaphore used for changing the raid. /// The cancellation token for cancelling the operation. /// The updated raid. internal Task UpdateRaidAsync ( Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Raid, () => CurrentRaid, m => CurrentRaid = m, () => null, update, releaseSemaphore, ct ); } /// /// Creates the raid if it is null, or updates the current raid. /// /// The function for creating the raid. /// The function for updating the raid. /// Whether to release the semaphore used for changing the raid. /// The cancellation token for cancelling the operation. /// The updated raid. internal Task CreateOrUpdateRaidAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return CreateOrUpdateAsync ( GameSemaphoreType.Raid, () => CurrentRaid, m => CurrentRaid = m, create, update, releaseSemaphore, ct ); } private async Task CreateAsync ( GameSemaphoreType type, Action set, Func create, bool releaseSemaphore = true, CancellationToken ct = default ) { await Semaphores.AcquireSemaphore(type, ct); try { var current = create(); set(current); return current; } finally { if (releaseSemaphore) { Semaphores.ReleaseSemaphore(type); } } } private async Task CreateOrUpdateAsync ( GameSemaphoreType type, Func get, Action set, Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { await Semaphores.AcquireSemaphore(type, ct); try { var current = get(); if (current is null) { current = create(); } else { current = update(current); } set(current); return current; } finally { if (releaseSemaphore) { Semaphores.ReleaseSemaphore(type); } } } }