using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using PhoenixLib.Caching; using WingsAPI.Communication.Bazaar; using WingsAPI.Data.Bazaar; using WingsAPI.Game.Extensions.Bazaar; using WingsAPI.Packets.Enums.Bazaar; using WingsEmu.Core.Extensions; using WingsEmu.Core.Generics; using WingsEmu.DTOs.Bazaar; namespace BazaarServer.Managers { public class BazaarManager { private readonly IBazaarItemDAO _bazaarItemDao; private readonly BazaarSearchManager _bazaarSearchManager; private readonly ILongKeyCachedRepository _itemsCache; private readonly ConcurrentDictionary> _itemsIdsByCharacterId = new(); private readonly SemaphoreSlim _semaphoreSlim = new(1, 1); public BazaarManager(IBazaarItemDAO bazaarItemDao, ILongKeyCachedRepository itemsCache, BazaarSearchManager bazaarSearchManager) { _bazaarItemDao = bazaarItemDao; _itemsCache = itemsCache; _bazaarSearchManager = bazaarSearchManager; } public async Task CacheAllItemsInDb() { IEnumerable items = await _bazaarItemDao.GetAllNonDeletedBazaarItems(); int count = 0; foreach (BazaarItemDTO item in items) { AddToCache(item); count++; } await _bazaarSearchManager.Initialize(items); return count; } private void AddToCache(BazaarItemDTO item) { _itemsCache.Set(item.Id, item); _itemsIdsByCharacterId.GetOrAdd(item.CharacterId, new ThreadSafeHashSet()).Add(item.Id); } private void RemoveFromCache(BazaarItemDTO item) { _itemsCache.Remove(item.Id); _itemsIdsByCharacterId.GetOrDefault(item.CharacterId)?.Remove(item.Id); } public async Task SaveAsync(BazaarItemDTO item) { await _semaphoreSlim.WaitAsync(); try { BazaarItemDTO dto = await _bazaarItemDao.SaveAsync(item); AddToCache(dto); _bazaarSearchManager.AddItem(dto); return dto; } finally { _semaphoreSlim.Release(); } } public async Task GetBazaarItemById(long bazaarItemId) { await _semaphoreSlim.WaitAsync(); try { return _itemsCache.Get(bazaarItemId); } finally { _semaphoreSlim.Release(); } } public ICollection GetItemsByCharacterId(long characterId) { return _itemsIdsByCharacterId.GetOrDefault(characterId)?.Select(s => _itemsCache.Get(s)).ToList(); } public async Task DeleteItemWithDto(BazaarItemDTO item, long requesterCharacterId) { await _semaphoreSlim.WaitAsync(); try { BazaarItemDTO cachedItem = _itemsCache.Get(item.Id); if (cachedItem == null || cachedItem.CharacterId != requesterCharacterId) { return null; } await _bazaarItemDao.DeleteByIdAsync(item.Id); RemoveFromCache(item); _bazaarSearchManager.RemoveItem(item); return cachedItem; } finally { _semaphoreSlim.Release(); } } public async Task ChangeItemPriceWithDto(BazaarItemDTO bazaarItemDto, long characterId, long price, long saleFee) { await _semaphoreSlim.WaitAsync(); try { BazaarItemDTO cachedItem = _itemsCache.Get(bazaarItemDto.Id); if (cachedItem == null || cachedItem.SoldAmount > 0 || characterId != cachedItem.CharacterId || cachedItem.GetBazaarItemStatus() != BazaarListedItemType.ForSale || BazaarExtensions.PriceOrAmountExceeds(bazaarItemDto.UsedMedal, price, bazaarItemDto.Amount)) { return null; } cachedItem.PricePerItem = price; cachedItem.SaleFee = saleFee; BazaarItemDTO savedItem = await _bazaarItemDao.SaveAsync(cachedItem); return savedItem; } finally { _semaphoreSlim.Release(); } } public IReadOnlyCollection SearchBazaarItems(BazaarSearchContext bazaarSearchContext) => _bazaarSearchManager.SearchBazaarItems(bazaarSearchContext); public async Task BuyItemWithExpectedValues(long bazaarItemId, long buyerCharacterId, short amount, long pricePerItem) { await _semaphoreSlim.WaitAsync(); try { BazaarItemDTO cachedItem = _itemsCache.Get(bazaarItemId); if (cachedItem == null || buyerCharacterId == cachedItem.CharacterId || amount < 1 || cachedItem.Amount - cachedItem.SoldAmount < amount || pricePerItem != cachedItem.PricePerItem || cachedItem.IsPackage && amount != cachedItem.Amount || cachedItem.GetBazaarItemStatus() != BazaarListedItemType.ForSale) { return null; } cachedItem.SoldAmount += amount; BazaarItemDTO savedItem = await _bazaarItemDao.SaveAsync(cachedItem); return savedItem; } finally { _semaphoreSlim.Release(); } } public async Task> UnlistItemsWithVnums(List items) { await _semaphoreSlim.WaitAsync(); try { foreach (BazaarItemDTO bazaarItemDto in items) { bazaarItemDto.ExpiryDate = DateTime.UtcNow.AddDays(-1); BazaarItemDTO dto = await _bazaarItemDao.SaveAsync(bazaarItemDto); AddToCache(dto); _bazaarSearchManager.AddItem(dto); } return items; } finally { _semaphoreSlim.Release(); } } } }