// // 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.Client; using NosSmooth.Core.Stateful; using NosSmooth.Game.Data.Characters; using NosSmooth.Game.Data.Maps; using NosSmooth.Game.Data.Raids; 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 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 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 async Task CreateOrUpdateCharacterAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return await 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 async Task CreateMapAsync ( Func create, bool releaseSemaphore = true, CancellationToken ct = default ) { return await 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 async Task CreateOrUpdateMapAsync ( Func create, Func update, bool releaseSemaphore = true, CancellationToken ct = default ) { return await CreateOrUpdateAsync ( GameSemaphoreType.Map, () => CurrentMap, m => CurrentMap = 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); } } } }