From 8d808170a5f485f67e1aab6fbba8d7d426ee88a9 Mon Sep 17 00:00:00 2001 From: rfi Date: Thu, 19 Oct 2023 14:07:48 +0700 Subject: [PATCH] DB for starting tutorial --- AscNet.Common/Common.cs | 4 ++ AscNet.Common/Database/Character.cs | 50 ++++++++++++- AscNet.Common/Database/DatabaseExtensions.cs | 19 +++++ AscNet.Common/Database/Player.cs | 45 +++++++++++- AscNet.Common/Database/Stage.cs | 71 +++++++++++++++++++ AscNet.Common/MsgPack/Types.cs | 9 ++- AscNet.GameServer/Handlers/AccountModule.cs | 43 ++++++++++- AscNet.GameServer/Session.cs | 2 + .../Controllers/AccountController.cs | 2 +- AscNet.SDKServer/SDKServer.cs | 4 ++ 10 files changed, 239 insertions(+), 10 deletions(-) create mode 100644 AscNet.Common/Database/DatabaseExtensions.cs create mode 100644 AscNet.Common/Database/Stage.cs diff --git a/AscNet.Common/Common.cs b/AscNet.Common/Common.cs index 019767c9..c41a5724 100644 --- a/AscNet.Common/Common.cs +++ b/AscNet.Common/Common.cs @@ -1,6 +1,9 @@ using System.Reflection; +using AscNet.Common.Database; using MongoDB.Driver; using Config.Net; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Options; using Newtonsoft.Json; namespace AscNet.Common @@ -14,6 +17,7 @@ namespace AscNet.Common static Common() { config = new ConfigurationBuilder().UseJsonFile("Configs/config.json").Build(); + mongoClient = new( new MongoClientSettings { diff --git a/AscNet.Common/Database/Character.cs b/AscNet.Common/Database/Character.cs index 3a6d9ebe..2a45ed48 100644 --- a/AscNet.Common/Database/Character.cs +++ b/AscNet.Common/Database/Character.cs @@ -12,6 +12,8 @@ namespace AscNet.Common.Database { public static readonly IMongoCollection collection = Common.db.GetCollection("characters"); + private uint NextEquipId => Equips.MaxBy(x => x.Id)?.Id + 1 ?? 1; + public static Character FromUid(long uid) { return collection.AsQueryable().FirstOrDefault(x => x.Uid == uid) ?? Create(uid); @@ -22,7 +24,9 @@ namespace AscNet.Common.Database Character character = new() { Uid = uid, - Characters = new() + Characters = new(), + Equips = new(), + Fashions = new() }; // Lucia havers by default character.AddCharacter(1021001); @@ -36,8 +40,12 @@ namespace AscNet.Common.Database { CharacterTable? character = CharacterTableReader.Instance.FromId((int)id); CharacterSkillTable? characterSkill = CharacterSkillTableReader.Instance.FromCharacterId((int)id); + if (character is null || characterSkill is null) - throw new ArgumentException("Invlid character id!", nameof(id)); + throw new ArgumentException("Invalid character id!", nameof(id)); + if (Characters.FirstOrDefault(x => x.Id == character.Id) is not null) + throw new ArgumentException("Character already obtained!", nameof(id)); + NotifyCharacterDataList.NotifyCharacterDataListCharacterData characterData = new() { Id = (uint)character.Id, @@ -64,10 +72,38 @@ namespace AscNet.Common.Database Id = uint.Parse(x.ToString().Take(6).ToArray()), Level = 1 })); + Fashions.Add(new() + { + Id = character.DefaultNpcFashtionId, + IsLock = false + }); + if (character.EquipId > 0) + AddEquip((uint)character.EquipId, character.Id); Characters.Add(characterData); } + public void AddEquip(uint equipId, int characterId = 0) + { + NotifyEquipDataList.NotifyEquipDataListEquipData equipData = new() + { + Id = NextEquipId, + TemplateId = equipId, + CharacterId = characterId, + Level = 1, + Exp = 0, + Breakthrough = 0, + ResonanceInfo = new(), + UnconfirmedResonanceInfo = new(), + AwakeSlotList = new(), + IsLock = false, + CreateTime = (uint)DateTimeOffset.Now.ToUnixTimeSeconds(), + IsRecycle = false + }; + + Equips.Add(equipData); + } + [BsonId] public ObjectId Id { get; set; } @@ -75,8 +111,16 @@ namespace AscNet.Common.Database [BsonRequired] public long Uid { get; set; } - [BsonElement("uid")] + [BsonElement("characters")] [BsonRequired] public List Characters { get; set; } + + [BsonElement("equips")] + [BsonRequired] + public List Equips { get; set; } + + [BsonElement("fashions")] + [BsonRequired] + public List Fashions { get; set; } } } diff --git a/AscNet.Common/Database/DatabaseExtensions.cs b/AscNet.Common/Database/DatabaseExtensions.cs new file mode 100644 index 00000000..185d1243 --- /dev/null +++ b/AscNet.Common/Database/DatabaseExtensions.cs @@ -0,0 +1,19 @@ +using System.Linq.Expressions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Options; + +namespace AscNet.Common.Database +{ + public static class DatabaseExtensions + { + public static BsonClassMap SetDictionaryRepresentation(this BsonClassMap classMap, Expression> memberLambda, DictionaryRepresentation representation) + { + var memberMap = classMap.GetMemberMap(memberLambda); + var serializer = memberMap.GetSerializer(); + if (serializer is IDictionaryRepresentationConfigurable dictionaryRepresentationSerializer) + serializer = dictionaryRepresentationSerializer.WithDictionaryRepresentation(representation); + memberMap.SetSerializer(serializer); + return classMap; + } + } +} diff --git a/AscNet.Common/Database/Player.cs b/AscNet.Common/Database/Player.cs index 451334ad..a0074b6f 100644 --- a/AscNet.Common/Database/Player.cs +++ b/AscNet.Common/Database/Player.cs @@ -2,6 +2,8 @@ using MongoDB.Bson; using MongoDB.Driver; using AscNet.Common.MsgPack; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Options; namespace AscNet.Common.Database { @@ -10,7 +12,7 @@ namespace AscNet.Common.Database { public static readonly IMongoCollection collection = Common.db.GetCollection("players"); - public static Player FromId(long id) + public static Player FromPlayerId(long id) { return collection.AsQueryable().FirstOrDefault(x => x.PlayerData.Id == id) ?? Create(id); } @@ -48,13 +50,45 @@ namespace AscNet.Common.Database CreateTime = DateTimeOffset.Now.ToUnixTimeSeconds(), LastLoginTime = DateTimeOffset.Now.ToUnixTimeSeconds(), Flags = 1 + }, + HeadPortraits = new(), + TeamGroups = new() + { + {1, new TeamGroupDatum() + { + TeamType = 1, + TeamId = 1, + CaptainPos = 1, + FirstFightPos = 1, + TeamData = new() + { + {1, 1021001}, + {2, 0}, + {3, 0} + }, + TeamName = null + }} } }; + player.AddHead(9000001); + player.AddHead(9000002); + player.AddHead(9000003); + collection.InsertOne(player); return player; } + public void AddHead(int id) + { + HeadPortraits.Add(new() + { + Id = id, + LeftCount = 1, + BeginTime = DateTimeOffset.Now.ToUnixTimeSeconds() + }); + } + [BsonId] public ObjectId Id { get; set; } @@ -65,5 +99,14 @@ namespace AscNet.Common.Database [BsonElement("player_data")] [BsonRequired] public PlayerData PlayerData { get; set; } + + [BsonElement("head_portraits")] + [BsonRequired] + public List HeadPortraits { get; set; } + + [BsonElement("team_groups")] + [BsonRequired] + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] + public Dictionary TeamGroups { get; set; } } } diff --git a/AscNet.Common/Database/Stage.cs b/AscNet.Common/Database/Stage.cs new file mode 100644 index 00000000..d4b66875 --- /dev/null +++ b/AscNet.Common/Database/Stage.cs @@ -0,0 +1,71 @@ +using AscNet.Common.MsgPack; +using AscNet.Table.share.guide; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; +using MongoDB.Driver; + +namespace AscNet.Common.Database +{ + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public class Stage + { + public static readonly IMongoCollection collection = Common.db.GetCollection("stages"); + + public static Stage FromUid(long uid) + { + return collection.AsQueryable().FirstOrDefault(x => x.Uid == uid) ?? Create(uid); + } + + private static Stage Create(long uid) + { + Stage stage = new() + { + Stages = new() + }; + foreach (var guideFight in GuideFightTableReader.Instance.All) + { + stage.AddStage(new StageDatum() + { + StageId = guideFight.StageId, + StarsMark = 7, + Passed = true, + PassTimesToday = 0, + PassTimesTotal = 1, + BuyCount = 0, + Score = 0, + LastPassTime = DateTimeOffset.Now.ToUnixTimeSeconds(), + RefreshTime = DateTimeOffset.Now.ToUnixTimeSeconds(), + CreateTime = DateTimeOffset.Now.ToUnixTimeSeconds(), + BestRecordTime = guideFight.DefaultRecordTime, + LastRecordTime = guideFight.DefaultRecordTime, + BestCardIds = new List() { 1021001 }, + LastCardIds = new List() { 1021001 } + }); + } + + collection.InsertOne(stage); + + return stage; + } + + public void AddStage(StageDatum stageData) + { + Stages.Add(stageData.StageId, stageData); + } + + [BsonId] + public ObjectId Id { get; set; } + + [BsonElement("uid")] + [BsonRequired] + public long Uid { get; set; } + + [BsonElement("stages")] + [BsonRequired] + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] + public Dictionary Stages { get; set; } + + } +} + diff --git a/AscNet.Common/MsgPack/Types.cs b/AscNet.Common/MsgPack/Types.cs index cab56c41..82fdaa24 100644 --- a/AscNet.Common/MsgPack/Types.cs +++ b/AscNet.Common/MsgPack/Types.cs @@ -1,5 +1,7 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. using MessagePack; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; namespace AscNet.Common.MsgPack { @@ -144,7 +146,7 @@ namespace AscNet.Common.MsgPack [MessagePackObject(true)] public partial class FubenData { - public Dictionary StageData { get; set; } + public Dictionary StageData { get; set; } public FubenBaseData FubenBaseData { get; set; } public List UnlockHideStages { get; set; } = new(); public List StageDifficulties { get; set; } = new(); @@ -191,7 +193,7 @@ namespace AscNet.Common.MsgPack public partial class FubenMainLineData { public List TreasureData { get; set; } = new(); - public Dictionary LastPassStage { get; set; } + public Dictionary LastPassStage { get; set; } = new(); public List MainChapterEventInfos { get; set; } = new(); } @@ -297,8 +299,9 @@ namespace AscNet.Common.MsgPack public long TeamId { get; set; } public long CaptainPos { get; set; } public long FirstFightPos { get; set; } + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] public Dictionary TeamData { get; set; } - public object TeamName { get; set; } + public string? TeamName { get; set; } } [MessagePackObject(true)] diff --git a/AscNet.GameServer/Handlers/AccountModule.cs b/AscNet.GameServer/Handlers/AccountModule.cs index 06192677..eb06efca 100644 --- a/AscNet.GameServer/Handlers/AccountModule.cs +++ b/AscNet.GameServer/Handlers/AccountModule.cs @@ -39,6 +39,8 @@ namespace AscNet.GameServer.Handlers } session.player = player; + session.character = Character.FromUid(player.PlayerData.Id); + session.stage = Stage.FromUid(player.PlayerData.Id); session.SendResponse(new LoginResponse { Code = 0, @@ -54,7 +56,18 @@ namespace AscNet.GameServer.Handlers public static void ReconnectRequestHandler(Session session, Packet.Request packet) { ReconnectRequest request = MessagePackSerializer.Deserialize(packet.Content); - Player? player = Player.FromToken(request.Token); + Player? player; + if (session.player is not null) + player = session.player; + else + { + player = Player.FromToken(request.Token); + if (player is not null && (session.character is null || session.stage is null)) + { + session.character = Character.FromUid(player.PlayerData.Id); + session.stage = Stage.FromUid(player.PlayerData.Id); + } + } if (player?.PlayerData.Id != request.PlayerId) { @@ -82,8 +95,34 @@ namespace AscNet.GameServer.Handlers // TODO: Move somewhere else, also split. static void DoLogin(Session session) { - NotifyLogin notifyLogin = JsonConvert.DeserializeObject(File.ReadAllText("Data/NotifyLogin.json"))!; + NotifyLogin notifyLogin = new() + { + PlayerData = session.player.PlayerData, + TeamGroupData = session.player.TeamGroups, + BaseEquipLoginData = new(), + FubenData = new() + { + StageData = session.stage.Stages, + FubenBaseData = new() + }, + FubenMainLineData = new(), + FubenChapterExtraLoginData = new(), + FubenUrgentEventData = new(), + UseBackgroundId = 14000001 // main ui theme, table still failed to dump + }; + notifyLogin.FashionList.AddRange(session.character.Fashions); + + NotifyCharacterDataList notifyCharacterData = new(); + notifyCharacterData.CharacterDataList.AddRange(session.character.Characters); + + NotifyEquipDataList notifyEquipData = new(); + notifyEquipData.EquipDataList.AddRange(session.character.Equips); + session.SendPush(notifyLogin); + session.SendPush(notifyCharacterData); + session.SendPush(notifyEquipData); + // NotifyLogin notifyLogin = JsonConvert.DeserializeObject(File.ReadAllText("Data/NotifyLogin.json"))!; + // session.SendPush(notifyLogin); // NEEDED to not softlock on stage selections! session.SendPush("NotifyFubenPrequelData", MessagePackSerializer.ConvertFromJson("{\"FubenPrequelData\": {\"RewardedStages\": [13010111, 13010112, 13010113, 13010211, 13010212, 13010213, 13010214, 13010215, 13010216, 13010311, 13010312, 13010313, 13010911, 13010912, 13010913, 13010414, 13010413, 13010415, 13010411, 13010412, 13010416, 13010316, 13010314, 13010315, 13010115, 13010116, 13010114, 13011011, 13011012, 13011013, 13011014, 13011015, 13011016], \"UnlockChallengeStages\": []}}")); diff --git a/AscNet.GameServer/Session.cs b/AscNet.GameServer/Session.cs index bc204c4e..ad719088 100644 --- a/AscNet.GameServer/Session.cs +++ b/AscNet.GameServer/Session.cs @@ -15,6 +15,8 @@ namespace AscNet.GameServer public readonly string id; public readonly TcpClient client; public Player player = default!; + public Character character = default!; + public Stage stage = default!; public readonly Logger log; private long lastPacketTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); private ushort packetNo = 0; diff --git a/AscNet.SDKServer/Controllers/AccountController.cs b/AscNet.SDKServer/Controllers/AccountController.cs index 242462ba..bea5afac 100644 --- a/AscNet.SDKServer/Controllers/AccountController.cs +++ b/AscNet.SDKServer/Controllers/AccountController.cs @@ -121,7 +121,7 @@ namespace AscNet.SDKServer.Controllers }); } - Player player = Player.FromId(account.Uid); + Player player = Player.FromPlayerId(account.Uid); LoginGate gate = new() { diff --git a/AscNet.SDKServer/SDKServer.cs b/AscNet.SDKServer/SDKServer.cs index 69dcc269..ca4cb3af 100644 --- a/AscNet.SDKServer/SDKServer.cs +++ b/AscNet.SDKServer/SDKServer.cs @@ -54,6 +54,10 @@ namespace AscNet.SDKServer { await _next(context); } + catch (Exception ex) + { + log.Error($"{ex.Message} Request below:"); + } finally { log.Info($"{context.Response.StatusCode} {context.Request.Method.ToUpper()} {context.Request.Path + context.Request.QueryString}");