From 3ec64e87b2dbce66b6b4f637b628d2a225d52ac4 Mon Sep 17 00:00:00 2001 From: mkbka <99542134+mkbka@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:42:23 +0800 Subject: [PATCH] implement gacha --- SCHALE.Common/Database/dbs.cs | 4 +- SCHALE.Common/NetworkProtocol/protos.cs | 4 +- SCHALE.Common/Utils/AccDict.cs | 17 ++ .../Controllers/Api/ProtocolHandlers/Shop.cs | 146 ++++++++++++++---- 4 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 SCHALE.Common/Utils/AccDict.cs diff --git a/SCHALE.Common/Database/dbs.cs b/SCHALE.Common/Database/dbs.cs index e5e4560..2fb03d5 100644 --- a/SCHALE.Common/Database/dbs.cs +++ b/SCHALE.Common/Database/dbs.cs @@ -771,8 +771,8 @@ namespace SCHALE.Common.Database public int LeaderSkillLevel { get; set; } = 1; [NotMapped] - public bool IsNew { get; set; } - public bool IsLocked { get; set; } + public bool IsNew { get; set; } = true; + public bool IsLocked { get; set; } = true; public bool IsFavorite { get; set; } public List EquipmentServerIds { get; set; } = []; public Dictionary PotentialStats { get; set; } = []; diff --git a/SCHALE.Common/NetworkProtocol/protos.cs b/SCHALE.Common/NetworkProtocol/protos.cs index 33a691b..a2979ac 100644 --- a/SCHALE.Common/NetworkProtocol/protos.cs +++ b/SCHALE.Common/NetworkProtocol/protos.cs @@ -9025,8 +9025,8 @@ namespace SCHALE.Common.NetworkProtocol public class GachaResult { public long CharacterId { get; set; } - public CharacterDB Character { get; set; } - public ItemDB Stone { get; set; } + public CharacterDB? Character { get; set; } + public ItemDB? Stone { get; set; } public GachaResult(long id) { this.CharacterId = id; diff --git a/SCHALE.Common/Utils/AccDict.cs b/SCHALE.Common/Utils/AccDict.cs new file mode 100644 index 0000000..4451bfe --- /dev/null +++ b/SCHALE.Common/Utils/AccDict.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SCHALE.Common.Utils +{ + public class AccDict : Dictionary where T : notnull + { + public new int this[T key] + { + get => TryGetValue(key, out var value) ? value : 0; + set => base[key] = value; + } + } +} diff --git a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Shop.cs b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Shop.cs index f85c3ad..7998bc1 100644 --- a/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Shop.cs +++ b/SCHALE.GameServer/Controllers/Api/ProtocolHandlers/Shop.cs @@ -1,6 +1,8 @@ -using SCHALE.Common.Database; +using Microsoft.EntityFrameworkCore; +using SCHALE.Common.Database; using SCHALE.Common.Database.ModelExtensions; using SCHALE.Common.NetworkProtocol; +using SCHALE.Common.Utils; using SCHALE.GameServer.Services; namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers @@ -10,15 +12,17 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers private readonly ISessionKeyService _sessionKeyService; private readonly SCHALEContext _context; private readonly SharedDataCacheService _sharedData; + private readonly ILogger _logger; // TODO: temp storage until gacha management public List SavedGachaResults { get; set; } = []; - public Shop(IProtocolHandlerFactory protocolHandlerFactory, ISessionKeyService sessionKeyService, SCHALEContext context, SharedDataCacheService sharedData) : base(protocolHandlerFactory) + public Shop(IProtocolHandlerFactory protocolHandlerFactory, ISessionKeyService sessionKeyService, SCHALEContext context, SharedDataCacheService sharedData, ILogger logger) : base(protocolHandlerFactory) { _sessionKeyService = sessionKeyService; _context = context; _sharedData = sharedData; + _logger = logger; } [ProtocolHandler(Protocol.Shop_BeforehandGachaGet)] @@ -96,10 +100,12 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers public ResponsePacket ShopBuyGacha3ResponseHandler(ShopBuyGacha3Request req) { var account = _sessionKeyService.GetAccount(req.SessionKey); - var accountCharacters = account.Characters.Select(x => x.UniqueId).ToHashSet(); + var accountChSet = account.Characters.Select(x => x.UniqueId).ToHashSet(); // TODO: Implement FES Gacha // TODO: Check Gacha currency + // TODO: SR pickup + // TODO: pickup stone count // TODO: even more... // Type Rate Acc.R // ------------------------- @@ -108,10 +114,14 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers // SR 18.5% 21.5% // R 78.5% 100.% + const int gpStoneID = 90070086; + const int chUniStoneID = 23; + var rateUpChId = 10094; // 10094, 10095 var rateUpIsNormalStudent = false; var gachaList = new List(10); - var newChList = new List(); + var itemDict = new AccDict(); + itemDict[gpStoneID] = 10; for (int i = 0; i < 10; ++i) { @@ -119,19 +129,26 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers if (randomNumber < 7) { // always 3 star - var isNew = accountCharacters.Add(rateUpChId); + var isNew = accountChSet.Add(rateUpChId); gachaList.Add(new(rateUpChId) { - Character = new() + Character = !isNew ? null : new() { AccountServerId = account.ServerId, UniqueId = rateUpChId, StarGrade = 3, - IsNew = isNew, - IsLocked = true, + }, + Stone = isNew ? null : new() + { + UniqueId = chUniStoneID, + StackCount = 50, } }); - if (isNew) newChList.Add(gachaList[i].Character); + if (!isNew) + { + itemDict[chUniStoneID] += 50; + itemDict[rateUpChId] += 100; + } } else if (randomNumber < 30) { @@ -143,19 +160,26 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers if (normalSSRList[randomPoolIdx].Id == rateUpChId) randomPoolIdx++; var chId = normalSSRList[randomPoolIdx].Id; - var isNew = accountCharacters.Add(chId); + var isNew = accountChSet.Add(chId); gachaList.Add(new(chId) { - Character = new() + Character = !isNew ? null : new() { AccountServerId = account.ServerId, UniqueId = chId, StarGrade = 3, - IsNew = isNew, - IsLocked = true, + }, + Stone = isNew ? null : new() + { + UniqueId = chUniStoneID, + StackCount = 50, } }); - if (isNew) newChList.Add(gachaList[i].Character); + if (!isNew) + { + itemDict[chUniStoneID] += 50; + itemDict[rateUpChId] += 30; + } } else if (randomNumber < 215 || (i == 9 && gachaList.All(x => x.Character.StarGrade == 1))) @@ -163,49 +187,113 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers var normalSRList = _sharedData.CharaListSRNormal; var randomPoolIdx = (int)Random.Shared.NextInt64(normalSRList.Count); var chId = normalSRList[randomPoolIdx].Id; - var isNew = accountCharacters.Add(chId); + var isNew = accountChSet.Add(chId); gachaList.Add(new(chId) { - Character = new() + Character = !isNew ? null : new() { AccountServerId = account.ServerId, UniqueId = chId, StarGrade = 2, - IsNew = isNew, - IsLocked = true, + }, + Stone = isNew ? null : new() + { + UniqueId = chUniStoneID, + StackCount = 10, } }); - if (isNew) newChList.Add(gachaList[i].Character); + if (!isNew) + { + itemDict[chUniStoneID] += 10; + itemDict[rateUpChId] += 5; + } } else { var normalRList = _sharedData.CharaListRNormal; var randomPoolIdx = (int)Random.Shared.NextInt64(normalRList.Count); var chId = normalRList[randomPoolIdx].Id; - var isNew = accountCharacters.Add(chId); + var isNew = accountChSet.Add(chId); gachaList.Add(new(chId) { - Character = new() + Character = !isNew ? null : new() { AccountServerId = account.ServerId, UniqueId = chId, StarGrade = 1, - IsNew = isNew, - IsLocked = true, + }, + Stone = isNew ? null : new() + { + UniqueId = chUniStoneID, + StackCount = 1, } }); - if (isNew) newChList.Add(gachaList[i].Character); + if (!isNew) + { + itemDict[chUniStoneID] += 1; + itemDict[rateUpChId] += 1; + } } } - foreach (var ch in newChList) + using var transaction = _context.Database.BeginTransaction(); + + try { - ch.AccountServerId = account.ServerId; + // add characters + _context.Characters.AddRange( + gachaList.Where(x => x.Character != null) + .Select(x => x.Character)!); + + // create if item does not exist + foreach (var id in itemDict.Keys) + { + var itemExists = _context.Items + .Any(x => x.AccountServerId == account.ServerId && x.UniqueId == id); + if (!itemExists) + { + _context.Items.Add(new ItemDB() + { + IsNew = true, + UniqueId = id, + StackCount = 0, + AccountServerId = account.ServerId, + }); + } + } + _context.SaveChanges(); + + // perform item count update + foreach (var (id, count) in itemDict) + { + _context.Items + .Where(x => x.AccountServerId == account.ServerId && x.UniqueId == id) + .ExecuteUpdate(setters => setters.SetProperty( + item => item.StackCount, item => item.StackCount + count)); + } + + _context.SaveChanges(); + + transaction.Commit(); + } + catch (Exception ex) + { + _logger.LogError("Transaction failed: {Message}", ex.Message); + throw; + } + + var itemDbList = itemDict.Keys + .Select(id => _context.Items.AsNoTracking().First(x => x.AccountServerId == account.ServerId && x.UniqueId == id)) + .ToList(); + foreach (var gacha in gachaList) + { + if (gacha.Stone != null) + { + gacha.Stone.ServerId = itemDbList.First(x => x.UniqueId == gacha.Stone.UniqueId).ServerId; + } } - _context.Characters.AddRange(newChList); - _context.SaveChanges(); return new ShopBuyGacha3Response() { @@ -213,7 +301,7 @@ namespace SCHALE.GameServer.Controllers.Api.ProtocolHandlers UpdateTime = DateTime.UtcNow, GemBonusRemain = int.MaxValue, ConsumedItems = [], - AcquiredItems = [], + AcquiredItems = itemDbList, MissionProgressDBs = [], }; }