diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..12ceda93 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# IDE0022: 使用方法的程序块主体 +csharp_style_expression_bodied_methods = false diff --git a/Common/Common.csproj b/Common/Common.csproj index f34b1b3b..7503b45f 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -15,6 +15,7 @@ + diff --git a/GameServer/Data/Excel/AvatarConfigExcel.cs b/Common/Data/Excel/AvatarConfigExcel.cs similarity index 66% rename from GameServer/Data/Excel/AvatarConfigExcel.cs rename to Common/Data/Excel/AvatarConfigExcel.cs index e4cf9404..90f9bd8a 100644 --- a/GameServer/Data/Excel/AvatarConfigExcel.cs +++ b/Common/Data/Excel/AvatarConfigExcel.cs @@ -1,10 +1,12 @@ namespace EggLink.DanhengServer.Data.Excel { [ResourceEntity("AvatarConfig.json", true)] - internal class AvatarConfigExcel : ExcelResource + public class AvatarConfigExcel : ExcelResource { public int AvatarID { get; set; } = 0; + public HashName AvatarName { get; set; } = new(); + public List DefaultSkillTree = []; public override int GetId() { return AvatarID; diff --git a/Common/Data/Excel/AvatarSkillTreeConfigExcel.cs b/Common/Data/Excel/AvatarSkillTreeConfigExcel.cs new file mode 100644 index 00000000..f209cb4a --- /dev/null +++ b/Common/Data/Excel/AvatarSkillTreeConfigExcel.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("AvatarSkillTreeConfig.json")] + public class AvatarSkillTreeConfigExcel : ExcelResource + { + + public int PointID { get; set; } + public int Level { get; set; } + public int AvatarID { get; set; } + public bool DefaultUnlock { get; set; } + public int MaxLevel { get; set; } + + public override int GetId() + { + return (PointID << 4) + Level; + } + + public override void AfterAllDone() + { + GameData.AvatarConfigData.TryGetValue(AvatarID, out var excel); + if (excel != null && DefaultUnlock) + { + excel.DefaultSkillTree.Add(this); + } + } + } +} diff --git a/Common/Data/Excel/MapEntranceExcel.cs b/Common/Data/Excel/MapEntranceExcel.cs new file mode 100644 index 00000000..295b3a33 --- /dev/null +++ b/Common/Data/Excel/MapEntranceExcel.cs @@ -0,0 +1,25 @@ +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("MapEntrance.json", true)] + public class MapEntranceExcel : ExcelResource + { + public int ID { get; set; } + public int PlaneID { get; set; } + public int FloorID { get; set; } + public int StartGroupID { get; set; } + public int StartAnchorID { get; set; } + + public List FinishMainMissionList { get; set; } = []; + public List FinishSubMissionList { get; set; } = []; + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + GameData.MapEntranceData.Add(ID, this); + } + } +} diff --git a/GameServer/Data/Excel/StageConfigExcel.cs b/Common/Data/Excel/StageConfigExcel.cs similarity index 88% rename from GameServer/Data/Excel/StageConfigExcel.cs rename to Common/Data/Excel/StageConfigExcel.cs index f124d728..0f08dea1 100644 --- a/GameServer/Data/Excel/StageConfigExcel.cs +++ b/Common/Data/Excel/StageConfigExcel.cs @@ -3,7 +3,7 @@ namespace EggLink.DanhengServer.Data.Excel { [ResourceEntity("StageConfig.json", false)] - internal class StageConfigExcel : ExcelResource + public class StageConfigExcel : ExcelResource { public int StageID { get; set; } = 0; public HashName StageName { get; set; } = new HashName(); @@ -20,7 +20,7 @@ namespace EggLink.DanhengServer.Data.Excel } } - internal class StageMonsterList + public class StageMonsterList { public int Monster0 { get; set; } = 0; public int Monster1 { get; set; } = 0; @@ -29,7 +29,7 @@ namespace EggLink.DanhengServer.Data.Excel public int Monster4 { get; set; } = 0; } - internal class HashName + public class HashName { public long Hash { get; set; } = 0; } diff --git a/GameServer/Data/ExcelResource.cs b/Common/Data/ExcelResource.cs similarity index 75% rename from GameServer/Data/ExcelResource.cs rename to Common/Data/ExcelResource.cs index c48af346..0ec48d3f 100644 --- a/GameServer/Data/ExcelResource.cs +++ b/Common/Data/ExcelResource.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace EggLink.DanhengServer.Data { - internal abstract class ExcelResource + public abstract class ExcelResource { public abstract int GetId(); @@ -17,5 +17,9 @@ namespace EggLink.DanhengServer.Data public virtual void Finalized() { } + + public virtual void AfterAllDone() + { + } } } diff --git a/GameServer/Data/GameData.cs b/Common/Data/GameData.cs similarity index 55% rename from GameServer/Data/GameData.cs rename to Common/Data/GameData.cs index 110746a2..f2924c5b 100644 --- a/GameServer/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -3,9 +3,10 @@ using System.Collections.Generic; namespace EggLink.DanhengServer.Data { - internal static class GameData + public static class GameData { - public static Dictionary AvatarConfigData { get; private set; } = new Dictionary(); - public static Dictionary StageConfigData { get; private set; } = new Dictionary(); + public static Dictionary AvatarConfigData { get; private set; } = []; + public static Dictionary StageConfigData { get; private set; } = []; + public static Dictionary MapEntranceData { get; private set; } = []; } } diff --git a/GameServer/Data/ResourceEntity.cs b/Common/Data/ResourceEntity.cs similarity index 100% rename from GameServer/Data/ResourceEntity.cs rename to Common/Data/ResourceEntity.cs diff --git a/GameServer/Data/ResourceManager.cs b/Common/Data/ResourceManager.cs similarity index 79% rename from GameServer/Data/ResourceManager.cs rename to Common/Data/ResourceManager.cs index 27b4ff08..7bc29238 100644 --- a/GameServer/Data/ResourceManager.cs +++ b/Common/Data/ResourceManager.cs @@ -1,14 +1,13 @@ using System; using System.IO; using System.Reflection; -using EggLink.DanhengServer.Program; using EggLink.DanhengServer.Util; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace EggLink.DanhengServer.Data { - internal class ResourceManager + public class ResourceManager { public static Logger Logger { get; private set; } = new Logger("ResourceManager"); public static void LoadGameData() @@ -21,12 +20,12 @@ namespace EggLink.DanhengServer.Data var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly foreach (var cls in classes) { - var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity)); + var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity))!; if (attribute != null) { - var resource = (ExcelResource)Activator.CreateInstance(cls); - var path = EntryPoint.GetConfig().Path.ResourcePath + "/ExcelOutput/" + attribute.FileName; + var resource = (ExcelResource)Activator.CreateInstance(cls)!; + var path = ConfigManager.Config.Path.ResourcePath + "/ExcelOutput/" + attribute.FileName; var file = new FileInfo(path); if (!file.Exists) { @@ -52,7 +51,7 @@ namespace EggLink.DanhengServer.Data foreach (var item in jArray) { var res = JsonConvert.DeserializeObject(item.ToString(), cls); - ((ExcelResource)res).Loaded(); + ((ExcelResource?)res)?.Loaded(); count++; } } @@ -71,9 +70,9 @@ namespace EggLink.DanhengServer.Data var nestedObject = JsonConvert.DeserializeObject(obj.ToString()); // Process only if it's a top-level dictionary, not nested - if (nestedObject.Count > 0 && nestedObject.First.First.Type != JTokenType.Object) + if (nestedObject?.Count > 0 && nestedObject?.First?.First?.Type != JTokenType.Object) { - ((ExcelResource)instance).Loaded(); + ((ExcelResource?)instance)?.Loaded(); } } else @@ -84,9 +83,21 @@ namespace EggLink.DanhengServer.Data } } } + resource.Finalized(); + Logger.Info($"Loaded {count} {cls.Name}s."); } } + foreach (var cls in classes) + { + var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity))!; + + if (attribute != null) + { + var resource = (ExcelResource)Activator.CreateInstance(cls)!; + resource.AfterAllDone(); + } + } } } diff --git a/Common/Database/Account/AccountData.cs b/Common/Database/Account/AccountData.cs index 705d6289..6cc8ca1a 100644 --- a/Common/Database/Account/AccountData.cs +++ b/Common/Database/Account/AccountData.cs @@ -1,62 +1,53 @@ using EggLink.DanhengServer.Util; using Microsoft.Data.Sqlite; +using SqlSugar; namespace EggLink.DanhengServer.Database.Account { - [DatabaseEntity("account")] - public class AccountData(string username, long uid, string comboToken = "", string dispatchToken = "", string permissions = "") : BaseDatabaseData + [SugarTable("Account")] + public class AccountData() : BaseDatabaseData { - public string Username { get; set; } = username; - public long Uid { get; set; } = uid; - public string ComboToken { get; set; } = comboToken; - public string DispatchToken { get; set; } = dispatchToken; - public string Permissions { get; set; } = permissions; // type: permission1,permission2,permission3... + public string? Username { get; set; } - public static BaseDatabaseData? GetAccountByUserName(string username) + [SugarColumn(IsNullable = true)] + public string? ComboToken { get; set; } + + [SugarColumn(IsNullable = true)] + public string? DispatchToken { get; set; } + + [SugarColumn(IsNullable = true)] + public string? Permissions { get; set; } // type: permission1,permission2,permission3... + + public static AccountData? GetAccountByUserName(string username) { - var connection = DatabaseHelper.Instance.connection; - var command = new SqliteCommand("SELECT * FROM account", connection); - var reader = command.ExecuteReader(); AccountData? result = null; - while (reader.Read()) + DatabaseHelper.Instance?.GetAllInstance()?.ForEach((account) => { - if (reader.GetString(0) == username) + if (account.Username == username) { - result = new(reader.GetString(0), reader.GetInt64(1), reader.GetString(2), reader.GetString(3), reader.GetString(4)); - break; + result = account; } - } + }); return result; } - public static BaseDatabaseData? GetAccountByUid(long uid) + public static AccountData? GetAccountByUid(long uid) { - var connection = DatabaseHelper.Instance.connection; - var command = new SqliteCommand("SELECT * FROM account", connection); - var reader = command.ExecuteReader(); - AccountData? result = null; - while (reader.Read()) - { - if (reader.GetInt64(1) == uid) - { - result = new(reader.GetString(0), reader.GetInt64(1), reader.GetString(2), reader.GetString(3), reader.GetString(4)); - break; - } - } + AccountData? result = DatabaseHelper.Instance?.GetInstance(uid); return result; } public string GenerateDispatchToken() { DispatchToken = Crypto.CreateSessionKey(Uid.ToString()); - ModifyDatabase(Uid, "dispatchToken", DispatchToken); + DatabaseHelper.Instance?.UpdateInstance(this); return DispatchToken; } public string GenerateComboToken() { ComboToken = Crypto.CreateSessionKey(Uid.ToString()); - ModifyDatabase(Uid, "comboToken", ComboToken); + DatabaseHelper.Instance?.UpdateInstance(this); return ComboToken; } } diff --git a/Common/Database/Account/AccountHelper.cs b/Common/Database/Account/AccountHelper.cs index 4304e0a8..589b4da9 100644 --- a/Common/Database/Account/AccountHelper.cs +++ b/Common/Database/Account/AccountHelper.cs @@ -17,7 +17,7 @@ namespace EggLink.DanhengServer.Database.Account long newUid = uid; if (uid == 0) { - newUid = 10000; // start from 10000 + newUid = 10001; // start from 10001 while (AccountData.GetAccountByUid(newUid) != null) { newUid++; @@ -25,8 +25,13 @@ namespace EggLink.DanhengServer.Database.Account } var per = ConfigManager.Config.ServerOption.DefaultPermissions; var perStr = string.Join(",", per); - var account = new AccountData(username,newUid, permissions: perStr); - account.SaveToDatabase(); + var account = new AccountData() + { + Uid = newUid, + Username = username, + Permissions = perStr + }; + DatabaseHelper.Instance?.SaveInstance(account); } } } diff --git a/Common/Database/Avatar/AvatarData.cs b/Common/Database/Avatar/AvatarData.cs new file mode 100644 index 00000000..8029d3c8 --- /dev/null +++ b/Common/Database/Avatar/AvatarData.cs @@ -0,0 +1,97 @@ +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Proto; +using SqlSugar; + +namespace EggLink.DanhengServer.Database.Avatar +{ + [SugarTable("Avatar")] + public class AvatarData : BaseDatabaseData + { + [SugarColumn(IsNullable = true)] + public List? Avatars { get; set; } + + public List AssistAvatars { get; set; } = []; + public List DisplayAvatars { get; set; } = []; + } + + public class AvatarInfo + { + public int AvatarId { get; set; } + public int Level { get; set; } + public int Exp { get; set; } + public int Promotion { get; set; } + public int Rewards { get; set; } + public long Timestamp { get; set; } + public int CurrentHp { get; set; } + public int CurrentSp { get; set; } + public int ExtraLineupHp { get; set; } + public int ExtraLineupSp { get; set; } + public int Rank { get; set; } + public Dictionary SkillTree { get; set; } = []; + public int EquipId { get; set; } = 0; + public Dictionary Relic { get; set; } = []; + + public AvatarConfigExcel Excel; + + public AvatarInfo(AvatarConfigExcel excel) + { + Excel = excel; + SkillTree = []; + excel.DefaultSkillTree.ForEach(skill => + { + SkillTree.Add(skill.PointID, skill.Level); + }); + } + + public bool HasTakenReward(int promotion) + { + return (Rewards & (1 << promotion)) != 0; + } + + public Proto.Avatar ToProto() + { + var proto = new Proto.Avatar() + { + BaseAvatarId = (uint)AvatarId, + Level = (uint)Level, + Exp = (uint)Exp, + Promotion = (uint)Promotion, + Rank = (uint)Rank, + FirstMetTimestamp = (ulong)Timestamp, + }; + + foreach (var item in Relic) + { + proto.EquipRelicList.Add(new EquipRelic() + { + RelicUniqueId = (uint)item.Value, + Slot = (uint)item.Key + }); + } + + if (EquipId != 0) + { + proto.EquipmentUniqueId = (uint)EquipId; + } + + foreach (var skill in SkillTree) + { + proto.SkilltreeList.Add(new AvatarSkillTree() + { + PointId = (uint)skill.Key, + Level = (uint)skill.Value + }); + } + + for (int i = 0; i < Promotion; i++) + { + if (HasTakenReward(i)) + { + proto.TakenRewards.Add((uint)i); + } + } + + return proto; + } + } +} diff --git a/Common/Database/BaseDatabaseData.cs b/Common/Database/BaseDatabaseData.cs index db5e4f24..43c093ae 100644 --- a/Common/Database/BaseDatabaseData.cs +++ b/Common/Database/BaseDatabaseData.cs @@ -1,77 +1,11 @@ using Microsoft.Data.Sqlite; +using SqlSugar; namespace EggLink.DanhengServer.Database { public abstract class BaseDatabaseData { - public void SaveToDatabase() - { - var connection = DatabaseHelper.Instance.connection; - var attributes = GetType().GetCustomAttributes(true); - string tableName = ""; - foreach ( var attribute in attributes ) - { - if (attribute is DatabaseEntity) - { - tableName = (attribute as DatabaseEntity).TableName; - break; - } - } - var command = new SqliteCommand($"INSERT INTO {tableName} (", connection); - var properties = GetType().GetProperties(); - foreach (var property in properties) - { - command.CommandText += $"{property.Name}, "; - } - command.CommandText = command.CommandText[..^2]; - command.CommandText += ") VALUES ("; - foreach (var property in properties) - { - command.CommandText += $"\"{property.GetValue(this)}\", "; - } - command.CommandText = command.CommandText[..^2]; - command.CommandText += ")"; - command.ExecuteNonQuery(); - } - - public void ModifyDatabase(long uid, string property, string value) - { - var connection = DatabaseHelper.Instance.connection; - var attributes = GetType().GetCustomAttributes(true); - string tableName = ""; - foreach ( var attribute in attributes ) - { - if (attribute is DatabaseEntity) - { - tableName = (attribute as DatabaseEntity).TableName; - break; - } - } - var command = new SqliteCommand($"UPDATE {tableName} SET \"{property}\" = \"{value}\" WHERE UID = {uid};", connection); - command.ExecuteNonQuery(); - } - - public void DeleteFromDatabase() - { - var connection = DatabaseHelper.Instance.connection; - var attributes = GetType().GetCustomAttributes(true); - string tableName = ""; - foreach ( var attribute in attributes ) - { - if (attribute is DatabaseEntity) - { - tableName = (attribute as DatabaseEntity).TableName; - break; - } - } - var command = new SqliteCommand($"DELETE FROM {tableName} WHERE ", connection); - var properties = GetType().GetProperties(); - foreach (var property in properties) - { - command.CommandText += $"{property.Name} = {property.GetValue(this)} AND "; - } - command.CommandText = command.CommandText[..^5]; - command.ExecuteNonQuery(); - } + [SugarColumn(IsPrimaryKey = true)] + public long Uid { get; set; } } } diff --git a/Common/Database/DatabaseEntity.cs b/Common/Database/DatabaseEntity.cs deleted file mode 100644 index c4dd0e5a..00000000 --- a/Common/Database/DatabaseEntity.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EggLink.DanhengServer.Database -{ - [AttributeUsage(AttributeTargets.Class)] - public class DatabaseEntity(string tableName) : Attribute - { - public string TableName { get; set; } = tableName; - } -} diff --git a/Common/Database/DatabaseHelper.cs b/Common/Database/DatabaseHelper.cs index 6a3ce33f..f8b7096a 100644 --- a/Common/Database/DatabaseHelper.cs +++ b/Common/Database/DatabaseHelper.cs @@ -1,6 +1,8 @@ using EggLink.DanhengServer.Configuration; +using EggLink.DanhengServer.Database.Account; using EggLink.DanhengServer.Util; using Microsoft.Data.Sqlite; +using SqlSugar; using System.Reflection; namespace EggLink.DanhengServer.Database @@ -9,8 +11,8 @@ namespace EggLink.DanhengServer.Database { public Logger logger = new("Database"); public ConfigContainer config = ConfigManager.Config; - public SqliteConnection connection; - public static DatabaseHelper Instance; + public static SqlSugarScope? sqlSugarScope; + public static DatabaseHelper? Instance; public DatabaseHelper() { @@ -19,7 +21,12 @@ namespace EggLink.DanhengServer.Database { f.Directory.Create(); } - connection = new SqliteConnection($"Data Source={f.FullName};"); + sqlSugarScope = new(new ConnectionConfig() + { + ConnectionString = $"Data Source={f.FullName};", + DbType = DbType.Sqlite, + IsAutoCloseConnection = true, + }); Instance = this; } public void Initialize() @@ -36,70 +43,65 @@ namespace EggLink.DanhengServer.Database } } - public void InitializeSqlite() + public static void InitializeSqlite() { - SQLitePCL.Batteries.Init(); - connection.Open(); - var classes = Assembly.GetExecutingAssembly().GetTypes(); - foreach (var cls in classes) { - var attribute = (DatabaseEntity)Attribute.GetCustomAttribute(cls, typeof(DatabaseEntity)); - if (attribute != null) - { - var tableName = attribute.TableName; - var createTable = $"CREATE TABLE IF NOT EXISTS {tableName} ("; - var properties = cls.GetProperties(); - foreach (var property in properties) - { - createTable += $"{property.Name} {GetSqliteType(property.PropertyType)}, "; - } - createTable = createTable.Substring(0, createTable.Length - 2); - createTable += ")"; - var command = new SqliteCommand(createTable, connection); - command.ExecuteNonQuery(); - } + var baseType = typeof(BaseDatabaseData); + var assembly = typeof(BaseDatabaseData).Assembly; + var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType)); + foreach (var type in types) + { + typeof(DatabaseHelper).GetMethod("InitializeSqliteTable")?.MakeGenericMethod(type).Invoke(null, null); } } - private string GetSqliteType(Type propertyType) + public static void InitializeSqliteTable() where T : class, new() { - if (propertyType == typeof(int)) + try { - return "INTEGER"; - } - else if (propertyType == typeof(string)) + sqlSugarScope?.Queryable().ToList(); + } catch { - return "TEXT"; - } - else if (propertyType == typeof(bool)) - { - return "INTEGER"; - } - else if (propertyType == typeof(float)) - { - return "REAL"; - } - else if (propertyType == typeof(double)) - { - return "REAL"; - } - else if (propertyType == typeof(long)) - { - return "INTEGER"; - } - else if (propertyType == typeof(byte[])) - { - return "BLOB"; - } - else - { - logger.Error($"Unsupported type {propertyType}"); - return "TEXT"; + sqlSugarScope?.CodeFirst.InitTables(); } } - public void Close() + public T? GetInstance(long uid) where T : class, new() { - connection.Close(); + try + { + return sqlSugarScope?.Queryable().Where(it => (it as BaseDatabaseData).Uid == uid).First(); + } catch + { + logger.Error("Unsupported type"); + return null; + } + } + + public List? GetAllInstance() where T : class, new() + { + try + { + return sqlSugarScope?.Queryable().ToList(); + } catch + { + logger.Error("Unsupported type"); + return null; + } + } + + public void SaveInstance(T instance) where T : class, new() + { + sqlSugarScope?.Insertable(instance).ExecuteCommand(); + } + + public void UpdateInstance(T instance) where T : class, new() + { + sqlSugarScope?.Updateable(instance).ExecuteCommand(); + } + + public void DeleteInstance(T instance) where T : class, new() + { + sqlSugarScope?.Deleteable(instance).ExecuteCommand(); } } } diff --git a/Common/Database/Inventory/InventoryData.cs b/Common/Database/Inventory/InventoryData.cs new file mode 100644 index 00000000..52489704 --- /dev/null +++ b/Common/Database/Inventory/InventoryData.cs @@ -0,0 +1,95 @@ +using EggLink.DanhengServer.Proto; +using SqlSugar; + +namespace EggLink.DanhengServer.Database.Inventory +{ + public class InventoryData : BaseDatabaseData + { + [SugarColumn(IsJson = true)] + public List MaterialItems { get; set; } = []; + [SugarColumn(IsJson = true)] + public List EquipmentItems { get; set; } = []; + [SugarColumn(IsJson = true)] + public List RelicItems { get; set; } = []; + } + + public class ItemData + { + [SugarColumn(IsPrimaryKey = true)] + public int UniqueId { get; set; } + public int ItemId { get; set; } + public int Count { get; set; } + public int Level { get; set; } + public int Exp { get; set; } + public int TotalExp { get; set; } + public int Promotion { get; set; } + public int Rank { get; set; } // Superimpose + public bool Locked { get; set; } + public bool Discarded { get; set; } + + public int MainAffix { get; set; } + public List SubAffixes { get; set; } = []; + + public int EquipAvatar { get; set; } + + public Material ToMaterialProto() + { + return new() + { + Tid = (uint)ItemId, + Num = (uint)Count, + }; + } + + public Relic ToRelicProto() + { + Relic relic = new() + { + Tid = (uint)ItemId, + UniqueId = (uint)UniqueId, + Level = (uint)Level, + IsProtected = Locked, + IsDiscarded = Discarded, + BaseAvatarId = (uint)EquipAvatar, + MainAffixId = (uint)MainAffix, + }; + if (SubAffixes.Count >= 1) + { + foreach (var subAffix in SubAffixes) + { + relic.SubAffixList.Add(subAffix.ToProto()); + } + } + return relic; + } + public Equipment ToEquipmentProto() + { + return new() + { + Tid = (uint)ItemId, + UniqueId = (uint)UniqueId, + Level = (uint)Level, + Exp = (uint)Exp, + IsProtected = Locked, + Promotion = (uint)Promotion, + Rank = (uint)Rank, + BaseAvatarId = (uint)EquipAvatar + }; + } + } + + public class ItemSubAffix + { + public int Id { get; set; } // Affix id + + public int Count { get; set; } + public int Step { get; set; } + + public RelicAffix ToProto() => new() + { + AffixId = (uint)Id, + Cnt = (uint)Count, + Step = (uint)Step + }; + } +} diff --git a/Common/Database/Lineup/LineupData.cs b/Common/Database/Lineup/LineupData.cs new file mode 100644 index 00000000..26d30223 --- /dev/null +++ b/Common/Database/Lineup/LineupData.cs @@ -0,0 +1,28 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Database.Lineup +{ + [SugarTable("Lineup")] + public class LineupData : BaseDatabaseData + { + public int CurLineup { get; set; } // index of current lineup + public string? Lineups { get; set; } // 9 * 4 + } + + public class LineupInfo + { + public string? Name { get; set; } + public int LineupType { get; set; } + public List? BaseAvatars { get; set; } + } + + public class LineupInfoJson + { + public Dictionary? Lineups { get; set; } = []; // 9 * 4 + } +} diff --git a/Common/Database/Player/PlayerData.cs b/Common/Database/Player/PlayerData.cs new file mode 100644 index 00000000..d0fa0f35 --- /dev/null +++ b/Common/Database/Player/PlayerData.cs @@ -0,0 +1,48 @@ +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using SqlSugar; + +namespace EggLink.DanhengServer.Database.Player +{ + [SugarTable("Player")] + public class PlayerData : BaseDatabaseData + { + public string? Name { get; set; } + public string? Signature { get; set; } + public int Birthday { get; set; } + public int CurBasicType { get; set; } + public int HeadIcon { get; set; } + public int PhoneTheme { get; set; } + public int ChatBubble { get; set; } + public int CurrentBgm { get; set; } + public Gender? CurrentGender { get; set; } + public int Level { get; set; } + public int Exp { get; set; } + public int WorldLevel { get; set; } + public int Scoin { get; set; } // Credits + public int Hcoin { get; set; } // Jade + public int Mcoin { get; set; } // Crystals + public int TalentPoints { get; set; } // Rogue talent points + + public int Stamina { get; set; } + public double StaminaReserve { get; set; } + public long NextStaminaRecover { get; set; } + + [SugarColumn(IsNullable = true)] + public Position? Pos { get; set; } + [SugarColumn(IsNullable = true)] + public Position? Rot { get; set; } + [SugarColumn(IsNullable = true)] + public int PlaneId { get; set; } + [SugarColumn(IsNullable = true)] + public int FloorId { get; set; } + [SugarColumn(IsNullable = true)] + public int EntryId { get; set; } + + [SugarColumn(IsNullable = true)] + public long LastActiveTime { get; set; } + + + } +} diff --git a/Common/Database/Player/PlayerUnlockData.cs b/Common/Database/Player/PlayerUnlockData.cs new file mode 100644 index 00000000..36910e52 --- /dev/null +++ b/Common/Database/Player/PlayerUnlockData.cs @@ -0,0 +1,9 @@ +namespace EggLink.DanhengServer.Database.Player +{ + public class PlayerUnlockData : BaseDatabaseData + { + public List HeadIcons { get; set; } = []; + public List ChatBubbles { get; set; } = []; + public List PhoneThemes { get; set; } = []; + } +} diff --git a/GameServer/Game/Player/Player.cs b/Common/Enums/PlayerGender.cs similarity index 50% rename from GameServer/Game/Player/Player.cs rename to Common/Enums/PlayerGender.cs index 1ac1afec..06e6df47 100644 --- a/GameServer/Game/Player/Player.cs +++ b/Common/Enums/PlayerGender.cs @@ -4,13 +4,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace EggLink.DanhengServer.Game.Player +namespace EggLink.DanhengServer.Enums { - public class Player + public enum PlayerGender { - public async Task OnLogoutAsync() - { - - } + PLAYER_MALE = 0, + PLAYER_FEMALE = 1, } } diff --git a/GameServer/Enums/SessionState.cs b/Common/Enums/SessionState.cs similarity index 76% rename from GameServer/Enums/SessionState.cs rename to Common/Enums/SessionState.cs index 4414cfd2..bb0c2b09 100644 --- a/GameServer/Enums/SessionState.cs +++ b/Common/Enums/SessionState.cs @@ -1,4 +1,4 @@ -namespace EggLink.DanhengServer.Enums +namespace EggLink.DanhengServer.Common.Enums { public enum SessionState { diff --git a/Common/Util/GameConstants.cs b/Common/Util/GameConstants.cs new file mode 100644 index 00000000..82d9ae44 --- /dev/null +++ b/Common/Util/GameConstants.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Util +{ + public static class GameConstants + { + public const int MATERIAL_HCOIN_ID = 1; // Material id for jades. DO NOT CHANGE + public const int MATERIAL_COIN_ID = 2; // Material id for credits. DO NOT CHANGE + public const int TRAILBLAZER_EXP_ID = 22; + public const int RELIC_REMAINS_ID = 235; + + public const int INVENTORY_MAX_EQUIPMENT = 1500; + public const int INVENTORY_MAX_RELIC = 1500; + public const int INVENTORY_MAX_MATERIAL = 2000; + } +} diff --git a/Common/Util/Logger.cs b/Common/Util/Logger.cs index 5efbd8d8..98f83528 100644 --- a/Common/Util/Logger.cs +++ b/Common/Util/Logger.cs @@ -107,10 +107,9 @@ namespace EggLink.DanhengServer.Util } } - public static Logger GetByClassName() - { - return new Logger(new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name); - } +#pragma warning disable CS8602 + public static Logger GetByClassName() => new(new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name); +#pragma warning restore CS8602 } public enum LoggerLevel diff --git a/Common/Util/Position.cs b/Common/Util/Position.cs new file mode 100644 index 00000000..b77fcc14 --- /dev/null +++ b/Common/Util/Position.cs @@ -0,0 +1,121 @@ +using EggLink.DanhengServer.Proto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Util +{ + public class Position + { + public int X { get; set; } + public int Y { get; set; } + public int Z { get; set; } + + public Position(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + + public Position(Vector vector) + { + X = vector.X; + Y = vector.Y; + Z = vector.Z; + } + + public Position() + { + X = 0; + Y = 0; + Z = 0; + } + + public Position(Position position) + { + X = position.X; + Y = position.Y; + Z = position.Z; + } + + public void Set(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + + public void Set(Position position) + { + X = position.X; + Y = position.Y; + Z = position.Z; + } + + public void Set(Vector vector) + { + X = vector.X; + Y = vector.Y; + Z = vector.Z; + } + + public void Add(int x, int y, int z) + { + X += x; + Y += y; + Z += z; + } + + public void Add(Position position) + { + X += position.X; + Y += position.Y; + Z += position.Z; + } + + public void Sub(int x, int y, int z) + { + X -= x; + Y -= y; + Z -= z; + } + + public void Sub(Position position) + { + X -= position.X; + Y -= position.Y; + Z -= position.Z; + } + + public void Mul(int x, int y, int z) + { + X *= x; + Y *= y; + Z *= z; + } + + public void Mul(Position position) + { + X *= position.X; + Y *= position.Y; + Z *= position.Z; + } + + public void Div(int x, int y, int z) + { + X /= x; + Y /= y; + Z /= z; + } + + public void Div(Position position) + { + X /= position.X; + Y /= position.Y; + Z /= position.Z; + } + } +} diff --git a/DanhengServer.sln b/DanhengServer.sln index 7a646f12..66f9e59b 100644 --- a/DanhengServer.sln +++ b/DanhengServer.sln @@ -14,6 +14,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决 README.md = README.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C822679-4BCC-497A-AF15-F441EC750CCE}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/GameServer/Game/Avatar/AvatarManager.cs b/GameServer/Game/Avatar/AvatarManager.cs new file mode 100644 index 00000000..71455692 --- /dev/null +++ b/GameServer/Game/Avatar/AvatarManager.cs @@ -0,0 +1,56 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Avatar; +using EggLink.DanhengServer.Game.Player; + +namespace EggLink.DanhengServer.Game.Avatar +{ + public class AvatarManager : BasePlayerManager + { + public AvatarData AvatarData { get; private set; } + + public AvatarManager(PlayerInstance player) : base(player) + { + var avatars = DatabaseHelper.Instance?.GetInstance(player.Uid); + if (avatars == null) + { + AvatarData = new() + { + Uid = player.Uid, + Avatars = [], + }; + DatabaseHelper.Instance?.SaveInstance(AvatarData); + } + else + { + AvatarData = avatars; + } + } + + public void AddAvatar(int avatarId) + { + GameData.AvatarConfigData.TryGetValue(avatarId, out AvatarConfigExcel? avatarExcel); + if (avatarExcel == null) + { + return; + } + var avatar = new AvatarInfo(avatarExcel) + { + AvatarId = avatarId, + Level = 1, + Timestamp = DateTime.Now.Ticks / TimeSpan.TicksPerSecond, + CurrentHp = 10000, + CurrentSp = 0 + }; + + if (AvatarData.Avatars == null) + { + AvatarData.Avatars = []; + } + + AvatarData.Avatars.Add(avatar); + DatabaseHelper.Instance?.UpdateInstance(AvatarData); + } + } +} diff --git a/GameServer/Game/BasePlayerManager.cs b/GameServer/Game/BasePlayerManager.cs new file mode 100644 index 00000000..a6fcde69 --- /dev/null +++ b/GameServer/Game/BasePlayerManager.cs @@ -0,0 +1,14 @@ +using EggLink.DanhengServer.Game.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game +{ + public class BasePlayerManager(PlayerInstance player) + { + public PlayerInstance Player { get; private set; } = player; + } +} diff --git a/GameServer/Game/Inventory/InventoryManager.cs b/GameServer/Game/Inventory/InventoryManager.cs new file mode 100644 index 00000000..d4a0d14c --- /dev/null +++ b/GameServer/Game/Inventory/InventoryManager.cs @@ -0,0 +1,26 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Inventory; +using EggLink.DanhengServer.Game.Player; + +namespace EggLink.DanhengServer.Game.Inventory +{ + public class InventoryManager : BasePlayerManager + { + public InventoryData Data; + public InventoryManager(PlayerInstance player) : base(player) + { + var inventory = DatabaseHelper.Instance?.GetInstance(player.Uid); + if (inventory == null) + { + DatabaseHelper.Instance?.SaveInstance(new InventoryData() + { + Uid = player.Uid, + }); + inventory = DatabaseHelper.Instance?.GetInstance(player.Uid); + } + Data = inventory!; + } + + + } +} diff --git a/GameServer/Game/Lineup/LineupManager.cs b/GameServer/Game/Lineup/LineupManager.cs new file mode 100644 index 00000000..a2e09285 --- /dev/null +++ b/GameServer/Game/Lineup/LineupManager.cs @@ -0,0 +1,95 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Lineup; +using EggLink.DanhengServer.Game.Player; +using Newtonsoft.Json; + +namespace EggLink.DanhengServer.Game.Lineup +{ + public class LineupManager : BasePlayerManager + { + public LineupData LineupData { get; private set; } + public LineupInfoJson LineupInfoJson { get; private set; } + + public LineupManager(PlayerInstance player) : base(player) + { + var lineup = DatabaseHelper.Instance?.GetInstance(player.Uid); + if (lineup == null) + { + LineupData = new() + { + Uid = player.Uid, + CurLineup = 0, + Lineups = "{}", + }; + DatabaseHelper.Instance?.SaveInstance(LineupData); + } + else + { + LineupData = lineup; + } + LineupInfoJson = JsonConvert.DeserializeObject(LineupData.Lineups ?? "{}") ?? new(); + } + + public LineupInfo? GetLineup(int lineupIndex) + { + if (LineupData.Lineups == null) + { + return null; + } + if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count) + { + return null; + } + return LineupInfoJson.Lineups?[lineupIndex]; + } + + public LineupInfo? GetCurLineup() + { + return GetLineup(LineupData.CurLineup); + } + + public void SetCurLineup(int lineupIndex) + { + if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count) + { + return; + } + LineupData.CurLineup = lineupIndex; + DatabaseHelper.Instance?.UpdateInstance(LineupData); + } + + public void AddAvatar(int lineupIndex, int avatarId) + { + if (lineupIndex < 0 || LineupData == null) + { + return; + } + if (LineupData.Lineups == null) + { + LineupData.Lineups = ""; + } + LineupInfo? lineup = null; + LineupInfoJson.Lineups?.TryGetValue(lineupIndex, out lineup); + if (lineup == null) + { + lineup = new() + { + Name = "Lineup " + lineupIndex, + LineupType = 0, + BaseAvatars = [avatarId], + }; + LineupInfoJson.Lineups?.Add(lineupIndex, lineup); + } else + { + lineup.BaseAvatars?.Add(avatarId); + } + LineupData.Lineups = JsonConvert.SerializeObject(LineupInfoJson); + DatabaseHelper.Instance?.UpdateInstance(LineupData!); + } + + public void AddAvatarToCurTeam(int avatarId) + { + AddAvatar(LineupData.CurLineup, avatarId); + } + } +} diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs new file mode 100644 index 00000000..a460e3c3 --- /dev/null +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -0,0 +1,138 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Player; +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Game.Avatar; +using EggLink.DanhengServer.Game.Inventory; +using EggLink.DanhengServer.Game.Lineup; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server; +using EggLink.DanhengServer.Server.Packet; +using EggLink.DanhengServer.Server.Packet.Send.Player; + +namespace EggLink.DanhengServer.Game.Player +{ + public class PlayerInstance(PlayerData data) + { + public PlayerData Data { get; set; } = data; + public ushort Uid { get; set; } + public Connection? Connection { get; set; } + public bool Initialized { get; set; } = false; + + #region Managers + + public AvatarManager AvatarManager { get; private set; } + public LineupManager LineupManager { get; private set; } + public InventoryManager InventoryManager { get; private set; } + + #endregion + + #region Datas + + public PlayerUnlockData PlayerUnlockData { get; private set; } + + #endregion + + public PlayerInstance() : this(new PlayerData()) + { + // new player + Data.Name = "无名客"; // Trailblazer in EN TODO: Add localization + Data.Signature = ""; + Data.Birthday = 0; + Data.CurBasicType = 8001; + Data.HeadIcon = 208001; + Data.PhoneTheme = 221000; + Data.ChatBubble = 220000; + Data.CurrentBgm = 210000; + Data.CurrentGender = Gender.Man; + Data.Stamina = 240; + Data.StaminaReserve = 0; + Data.NextStaminaRecover = DateTime.Now.Millisecond; + Data.Level = 1; + Data.Exp = 0; + Data.WorldLevel = 0; + Data.Scoin = 0; + Data.Hcoin = 0; + Data.Mcoin = 0; + Data.TalentPoints = 0; + + InitialPlayerManager(); + + AddAvatar(1005); + LineupManager.SetCurLineup(0); + LineupManager.AddAvatarToCurTeam(1005); + Initialized = true; + } + + private void InitialPlayerManager() + { + Uid = (ushort)Data.Uid; + AvatarManager = new(this); + LineupManager = new(this); + InventoryManager = new(this); + + var unlock = DatabaseHelper.Instance?.GetInstance(Uid); + if (unlock == null) + { + DatabaseHelper.Instance?.SaveInstance(new PlayerUnlockData() + { + Uid = Uid, + }); + unlock = DatabaseHelper.Instance?.GetInstance(Uid); + } + PlayerUnlockData = unlock!; + } + + + + #region Network + public void OnLogin() + { + if (!Initialized) + { + InitialPlayerManager(); + } + + SendPacket(new PacketStaminaInfoScNotify(this)); + + } + + public async Task OnLogoutAsync() + { + + } + + public void SendPacket(BasePacket packet) + { + Connection?.SendPacket(packet); + } + #endregion + + #region Extra + + public void AddAvatar(int avatarId) + { + AvatarManager.AddAvatar(avatarId); + } + + #endregion + + #region Proto + + public PlayerBasicInfo ToProto() + { + return new() + { + Nickname = Data.Name, + Level = (uint)Data.Level, + Exp = (uint)Data.Exp, + WorldLevel = (uint)Data.WorldLevel, + Scoin = (uint)Data.Scoin, + Hcoin = (uint)Data.Hcoin, + Mcoin = (uint)Data.Mcoin, + Stamina = (uint)Data.Stamina, + }; + } + + #endregion + } +} diff --git a/GameServer/Game/Scene/SceneInstance.cs b/GameServer/Game/Scene/SceneInstance.cs new file mode 100644 index 00000000..7ebe8670 --- /dev/null +++ b/GameServer/Game/Scene/SceneInstance.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game.Scene +{ + public class SceneInstance + { + } +} diff --git a/GameServer/GameServer.csproj b/GameServer/GameServer.csproj index 12e34365..6840fba8 100644 --- a/GameServer/GameServer.csproj +++ b/GameServer/GameServer.csproj @@ -16,11 +16,9 @@ - - @@ -31,18 +29,16 @@ - - - + diff --git a/GameServer/Program/EntryPoint.cs b/GameServer/Program/EntryPoint.cs index 5a4a8267..cd250410 100644 --- a/GameServer/Program/EntryPoint.cs +++ b/GameServer/Program/EntryPoint.cs @@ -3,7 +3,6 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Configuration; using EggLink.DanhengServer.WebServer; using EggLink.DanhengServer.Database; -using System.Runtime.InteropServices; using EggLink.DanhengServer.Server; using EggLink.DanhengServer.Server.Packet; diff --git a/GameServer/Server/Connection.cs b/GameServer/Server/Connection.cs index cff7df81..19f670fe 100644 --- a/GameServer/Server/Connection.cs +++ b/GameServer/Server/Connection.cs @@ -1,7 +1,8 @@ using System.Buffers; using System.Net; +using System.Net.Sockets; using System.Reflection; -using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Common.Enums; using EggLink.DanhengServer.Game.Player; using EggLink.DanhengServer.KcpSharp; using EggLink.DanhengServer.Program; @@ -21,16 +22,14 @@ public partial class Connection private readonly CancellationTokenSource CancelToken; public readonly IPEndPoint RemoteEndPoint; public SessionState State { get; set; } = SessionState.INACTIVE; - private bool UseSecretKey { get; set; } = false; - private byte[] SecretKey = new byte[0x1000]; - public Player? Player { get; set; } + public PlayerInstance? Player { get; set; } public uint ClientTime { get; private set; } public long LastPingTime { get; private set; } private uint LastClientSeq = 10; public static readonly List BANNED_PACKETS = []; - private static Logger Logger = new("GameServer"); + private static readonly Logger Logger = new("GameServer"); #if DEBUG - private static uint LogIndex = 0; + private static readonly Dictionary LogMap = []; #endif public Connection(KcpConversation conversation, IPEndPoint remote) { @@ -38,11 +37,14 @@ public partial class Connection RemoteEndPoint = remote; CancelToken = new CancellationTokenSource(); Start(); +#if DEBUG + JsonConvert.DeserializeObject(File.ReadAllText("LogMap.json")).Properties().ToList().ForEach(x => LogMap.Add(x.Name, x.Value.ToString())); +#endif } private async void Start() { - Logger.Info($"New connection to {RemoteEndPoint} created with conversation id {Conversation.ConversationId}"); + Logger.Info($"New connection from {RemoteEndPoint}."); State = SessionState.WAITING_FOR_TOKEN; await ReceiveLoop(); } @@ -72,14 +74,20 @@ public partial class Connection #if DEBUG public static void LogPacket(string sendOrRecv, ushort opcode, byte[] payload) { - //Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}"); - Type? typ = AppDomain.CurrentDomain.GetAssemblies(). - SingleOrDefault(assembly => assembly.GetName().Name == "Shared").GetTypes().First(t => t.Name == $"{Enum.GetName(typeof(CmdId), opcode)}"); //get the type using the packet name - MessageDescriptor? descriptor = (MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); // get the static property Descriptor - IMessage? packet = descriptor.Parser.ParseFrom(payload); - JsonFormatter? formatter = JsonFormatter.Default; - string? asJson = formatter.Format(packet); - Logger.Debug($"{sendOrRecv}: {Enum.GetName(typeof(CmdId), opcode)}({opcode})\r\n{asJson}"); + try + { + //Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}"); + Type? typ = AppDomain.CurrentDomain.GetAssemblies(). + SingleOrDefault(assembly => assembly.GetName().Name == "Common").GetTypes().First(t => t.Name == $"{LogMap[opcode.ToString()]}"); //get the type using the packet name + MessageDescriptor? descriptor = (MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); // get the static property Descriptor + IMessage? packet = descriptor.Parser.ParseFrom(payload); + JsonFormatter? formatter = JsonFormatter.Default; + string? asJson = formatter.Format(packet); + Logger.Debug($"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})\r\n{asJson}"); + } catch (Exception e) + { + Logger.Debug($"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})"); + } } #endif @@ -131,8 +139,6 @@ public partial class Connection { byte[] gamePacket = data.ToArray(); - // Decrypt and turn back into a packet - //Crypto.Xor(gamePacket, UseSecretKey ? SecretKey : Crypto.DISPATCH_KEY); await using MemoryStream? ms = new(gamePacket); using BinaryReader? br = new(ms); @@ -159,8 +165,10 @@ public partial class Connection uint payloadLength = br.ReadUInt32BE(); byte[] header = br.ReadBytes(headerLength); byte[] payload = br.ReadBytes((int)payloadLength); - - await HandlePacketAsync(opcode, header, payload); +#if DEBUG + LogPacket("Recv", opcode, payload); +#endif + HandlePacket(opcode, header, payload); } } @@ -174,7 +182,7 @@ public partial class Connection } } - private async Task HandlePacketAsync(ushort opcode, byte[] header, byte[] payload) + private bool HandlePacket(ushort opcode, byte[] header, byte[] payload) { // Find the Handler for this opcode Handler? handler = EntryPoint.HandlerManager.GetHandler(opcode); @@ -185,7 +193,7 @@ public partial class Connection SessionState state = State; switch ((int)opcode) { - case CmdId.PlayerGetTokenCsReq: + case CmdIds.PlayerGetTokenCsReq: { if (state != SessionState.WAITING_FOR_TOKEN) { @@ -193,7 +201,7 @@ public partial class Connection } goto default; } - case CmdId.PlayerLoginCsReq: + case CmdIds.PlayerLoginCsReq: { if (state != SessionState.WAITING_FOR_LOGIN) { @@ -204,15 +212,14 @@ public partial class Connection default: break; } - handler.OnHandle(header, payload); + handler.OnHandle(this, header, payload); return true; } return false; } - - public async Task SendPacketAsync(BasePacket packet) + public void SendPacket(BasePacket packet) { // Test if (packet.CmdId <= 0) @@ -226,10 +233,40 @@ public partial class Connection { return; } - +#if DEBUG + LogPacket("Send", packet.CmdId, packet.Data); +#endif // Header byte[] packetBytes = packet.BuildPacket(); - await Conversation.SendAsync(packetBytes, CancelToken.Token); +#pragma warning disable CA2012 + _ = Conversation.SendAsync(packetBytes, CancelToken.Token); +#pragma warning restore CA2012 + } + + public void SendPacket(int cmdId) + { + // Test + if (cmdId <= 0) + { + Logger.Debug("Tried to send packet with missing cmd id!"); + return; + } + + // DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can) + if (BANNED_PACKETS.Contains(cmdId)) + { + return; + } +#if DEBUG + LogPacket("Send", (ushort)cmdId, []); +#endif + + // Header + byte[] packetBytes = new BasePacket((ushort)cmdId).BuildPacket(); + +#pragma warning disable CA2012 + _ = Conversation.SendAsync(packetBytes, CancelToken.Token); +#pragma warning restore CA2012 } } diff --git a/GameServer/Server/GameSession.cs b/GameServer/Server/GameSession.cs deleted file mode 100644 index 25bb3b47..00000000 --- a/GameServer/Server/GameSession.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace EggLink.DanhengServer.Server -{ - public class GameSession() - { - } -} diff --git a/GameServer/Server/Packet/BasePacket.cs b/GameServer/Server/Packet/BasePacket.cs index 474f778d..95d33902 100644 --- a/GameServer/Server/Packet/BasePacket.cs +++ b/GameServer/Server/Packet/BasePacket.cs @@ -1,4 +1,6 @@ -using System; +using EggLink.DanhengServer.Util; +using Google.Protobuf; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,13 +8,42 @@ using System.Threading.Tasks; namespace EggLink.DanhengServer.Server.Packet { - public class BasePacket + public class BasePacket(ushort cmdId) { - public int CmdId; + private const uint HEADER_CONST = 0x9d74c714; + private const uint TAIL_CONST = 0xd7a152c8; + + public ushort CmdId { get; set; } = cmdId; + public byte[] Data { get; private set; } = []; + + public void SetData(byte[] data) + { + Data = data; + } + + public void SetData(IMessage message) + { + Data = message.ToByteArray(); + } public byte[] BuildPacket() { - return []; + using MemoryStream? ms = new(); + using BinaryWriter? bw = new(ms); + + bw.WriteUInt32BE(HEADER_CONST); + bw.WriteUInt16BE(CmdId); + bw.WriteUInt16BE(0); + bw.WriteUInt32BE((uint)Data.Length); + if (Data.Length > 0) + { + bw.Write(Data); + } + bw.WriteUInt32BE(TAIL_CONST); + + byte[] packet = ms.ToArray(); + + return packet; } } } diff --git a/GameServer/Server/Packet/CmdId.cs b/GameServer/Server/Packet/CmdIds.cs similarity index 99% rename from GameServer/Server/Packet/CmdId.cs rename to GameServer/Server/Packet/CmdIds.cs index 5431078e..427ff3f9 100644 --- a/GameServer/Server/Packet/CmdId.cs +++ b/GameServer/Server/Packet/CmdIds.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace EggLink.DanhengServer.Server.Packet { - public class CmdId + public class CmdIds { // None public const int None = 0; diff --git a/GameServer/Server/Packet/Handler.cs b/GameServer/Server/Packet/Handler.cs index 3d1358eb..301718ab 100644 --- a/GameServer/Server/Packet/Handler.cs +++ b/GameServer/Server/Packet/Handler.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace EggLink.DanhengServer.Server.Packet +namespace EggLink.DanhengServer.Server.Packet { public abstract class Handler { - public abstract void OnHandle(byte[] header, byte[] data); + public abstract void OnHandle(Connection connection, byte[] header, byte[] data); } } diff --git a/GameServer/Server/Packet/HandlerManager.cs b/GameServer/Server/Packet/HandlerManager.cs index 8e10f5a9..1863f786 100644 --- a/GameServer/Server/Packet/HandlerManager.cs +++ b/GameServer/Server/Packet/HandlerManager.cs @@ -1,10 +1,4 @@ -using EggLink.DanhengServer.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; namespace EggLink.DanhengServer.Server.Packet { diff --git a/GameServer/Server/Packet/Recv/Avatar/HandlerGetAvatarDataCsReq.cs b/GameServer/Server/Packet/Recv/Avatar/HandlerGetAvatarDataCsReq.cs new file mode 100644 index 00000000..b4718287 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Avatar/HandlerGetAvatarDataCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Server.Packet.Send.Avatar; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar +{ + [Opcode(CmdIds.GetAvatarDataCsReq)] + public class HandlerGetAvatarDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetAvatarDataScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Mission/HandlerGetMissionStatusCsReq.cs b/GameServer/Server/Packet/Recv/Mission/HandlerGetMissionStatusCsReq.cs new file mode 100644 index 00000000..2e4d3471 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Mission/HandlerGetMissionStatusCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Mission; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Mission +{ + [Opcode(CmdIds.GetMissionStatusCsReq)] + public class HandlerGetMissionStatusCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetMissionStatusCsReq.Parser.ParseFrom(data); + if (req != null) + { + connection.SendPacket(new PacketGetMissionStatusScRsp(req)); + } + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerGetBagCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerGetBagCsReq.cs new file mode 100644 index 00000000..d0fbdb61 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerGetBagCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.GetBagCsReq)] + public class HandlerGetBagCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetBagScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerGetBasicInfoCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerGetBasicInfoCsReq.cs new file mode 100644 index 00000000..673a057c --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerGetBasicInfoCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.GetBasicInfoCsReq)] + public class HandlerGetBasicInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetBasicInfoScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs new file mode 100644 index 00000000..ce925790 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Server.Packet.Send.Player; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.GetHeroBasicTypeInfoCsReq)] + public class HandlerGetHeroBasicTypeInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetHeroBasicTypeInfoScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerGetPlayerBoardDataCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerGetPlayerBoardDataCsReq.cs new file mode 100644 index 00000000..82e88421 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerGetPlayerBoardDataCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Server.Packet.Send.Player; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.GetPlayerBoardDataCsReq)] + public class HandlerGetPlayerBoardDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetPlayerBoardDataScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs index 15ccb9cd..5a1c1cd4 100644 --- a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs @@ -1,20 +1,33 @@ -using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Common.Enums; +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Player; +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Player; using EggLink.DanhengServer.Util; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace EggLink.DanhengServer.Server.Packet.Recv.Player { - [Opcode(CmdId.PlayerGetTokenCsReq)] + [Opcode(CmdIds.PlayerGetTokenCsReq)] public class HandlerPlayerGetTokenCsReq : Handler { - public override void OnHandle(byte[] header, byte[] data) + public override void OnHandle(Connection connection, byte[] header, byte[] data) { var req = PlayerGetTokenCsReq.Parser.ParseFrom(data); - Logger.GetByClassName().Debug("OnHandle" + req.ToString()); + connection.State = SessionState.WAITING_FOR_LOGIN; + var pd = DatabaseHelper.Instance?.GetInstance(long.Parse(req.AccountUid)); + if (pd == null) + connection.Player = new PlayerInstance() + { + Uid = ushort.Parse(req.AccountUid), + }; + else + { + connection.Player = new PlayerInstance(pd); + } + connection.Player.OnLogin(); + connection.Player.Connection = connection; + connection.SendPacket(new PacketPlayerGetTokenScRsp(connection)); } } } diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerHeartBeatCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerHeartBeatCsReq.cs new file mode 100644 index 00000000..9a5cef68 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerHeartBeatCsReq.cs @@ -0,0 +1,23 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.PlayerHeartBeatCsReq)] + public class HandlerPlayerHeartBeatCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = PlayerHeartbeatCsReq.Parser.ParseFrom(data); + if (req != null) + { + connection.SendPacket(new PacketPlayerHeartBeatScRsp((long)req.ClientTimeMs)); + } + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginCsReq.cs new file mode 100644 index 00000000..e5248f24 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginCsReq.cs @@ -0,0 +1,15 @@ +using EggLink.DanhengServer.Common.Enums; +using EggLink.DanhengServer.Server.Packet.Send.Player; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.PlayerLoginCsReq)] + public class HandlerPlayerLoginCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.State = SessionState.ACTIVE; + connection.SendPacket(new PacketPlayerLoginScRsp(connection)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginFinishCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginFinishCsReq.cs new file mode 100644 index 00000000..92627212 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLoginFinishCsReq.cs @@ -0,0 +1,12 @@ +namespace EggLink.DanhengServer.Server.Packet.Recv.Player +{ + [Opcode(CmdIds.PlayerLoginFinishCsReq)] + public class HandlerPlayerLoginFinishCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.PlayerLoginFinishScRsp); + connection.SendPacket(CmdIds.GetArchiveDataScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerGetEnteredSceneCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerGetEnteredSceneCsReq.cs new file mode 100644 index 00000000..ca3bdae3 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerGetEnteredSceneCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Server.Packet.Send.Scene; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Scene +{ + [Opcode(CmdIds.GetEnteredSceneCsReq)] + public class HandlerGetEnteredSceneCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetEnteredSceneScRsp()); + } + } +} diff --git a/GameServer/Server/Packet/Send/Avatar/PacketGetAvatarDataScRsp.cs b/GameServer/Server/Packet/Send/Avatar/PacketGetAvatarDataScRsp.cs new file mode 100644 index 00000000..1c0bc791 --- /dev/null +++ b/GameServer/Server/Packet/Send/Avatar/PacketGetAvatarDataScRsp.cs @@ -0,0 +1,23 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Avatar +{ + public class PacketGetAvatarDataScRsp : BasePacket + { + public PacketGetAvatarDataScRsp(PlayerInstance player) : base(CmdIds.GetAvatarDataScRsp) + { + var proto = new GetAvatarDataScRsp() + { + IsGetAll = true, + }; + + player.AvatarManager?.AvatarData?.Avatars?.ForEach(avatar => + { + proto.AvatarList.Add(avatar.ToProto()); + }); + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Mission/PacketGetMissionStatusScRsp.cs b/GameServer/Server/Packet/Send/Mission/PacketGetMissionStatusScRsp.cs new file mode 100644 index 00000000..97764c1a --- /dev/null +++ b/GameServer/Server/Packet/Send/Mission/PacketGetMissionStatusScRsp.cs @@ -0,0 +1,28 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Mission +{ + public class PacketGetMissionStatusScRsp : BasePacket + { + public PacketGetMissionStatusScRsp(GetMissionStatusCsReq req) : base(CmdIds.GetMissionStatusScRsp) + { + var proto = new GetMissionStatusScRsp(); + + foreach (var item in req.MainMissionIdList) + { + proto.FinishedMainMissionIdList.Add(item); + } + + foreach (var item in req.SubMissionIdList) + { + proto.SubMissionStatusList.Add(new Proto.Mission() + { + Id = item, + Status = MissionStatus.MissionFinish, + }); + } + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketGetBagScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketGetBagScRsp.cs new file mode 100644 index 00000000..ebbdb615 --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketGetBagScRsp.cs @@ -0,0 +1,30 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketGetBagScRsp : BasePacket + { + public PacketGetBagScRsp(PlayerInstance player) : base(CmdIds.GetBagScRsp) + { + var proto = new GetBagScRsp(); + + player.InventoryManager.Data.MaterialItems.ForEach(item => + { + proto.MaterialList.Add(item.ToMaterialProto()); + }); + + player.InventoryManager.Data.RelicItems.ForEach(item => + { + proto.RelicList.Add(item.ToRelicProto()); + }); + + player.InventoryManager.Data.EquipmentItems.ForEach(item => + { + proto.EquipmentList.Add(item.ToEquipmentProto()); + }); + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketGetBasicInfoScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketGetBasicInfoScRsp.cs new file mode 100644 index 00000000..927f4bfa --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketGetBasicInfoScRsp.cs @@ -0,0 +1,26 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketGetBasicInfoScRsp : BasePacket + { + public PacketGetBasicInfoScRsp(PlayerInstance player) : base(CmdIds.GetBasicInfoScRsp) + { + var proto = new GetBasicInfoScRsp() + { + CurDay = 1, + NextRecoverTime = player.Data.NextStaminaRecover / 1000, + GameplayBirthday = (uint)player.Data.Birthday, + PlayerSettingInfo = new(), + }; + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs new file mode 100644 index 00000000..d760ed1b --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs @@ -0,0 +1,19 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketGetHeroBasicTypeInfoScRsp : BasePacket + { + public PacketGetHeroBasicTypeInfoScRsp(PlayerInstance player) : base(CmdIds.GetHeroBasicTypeInfoScRsp) + { + var proto = new GetHeroBasicTypeInfoScRsp() + { + Gender = player.Data.CurrentGender ?? Gender.None, + CurBasicType = (HeroBasicType)player.Data.CurBasicType, + }; + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketGetPlayerBoardDataScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketGetPlayerBoardDataScRsp.cs new file mode 100644 index 00000000..88f34c00 --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketGetPlayerBoardDataScRsp.cs @@ -0,0 +1,37 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketGetPlayerBoardDataScRsp : BasePacket + { + public PacketGetPlayerBoardDataScRsp(PlayerInstance player) : base(CmdIds.GetPlayerBoardDataScRsp) + { + var proto = new GetPlayerBoardDataScRsp() + { + CurrentHeadIconId = (uint)player.Data.HeadIcon, + Signature = player.Data.Signature, + }; + + player.PlayerUnlockData.HeadIcons.ForEach(id => + { + HeadIcon headIcon = new() { Id = (uint)id }; + proto.UnlockedHeadIconList.Add(headIcon); + }); + + proto.DisplayAvatarVec = new(); + var pos = 0; + player.AvatarManager.AvatarData.DisplayAvatars.ForEach(avatar => + { + DisplayAvatar displayAvatar = new() + { + AvatarId = (uint)avatar, + Pos = (uint)pos++, + }; + proto.DisplayAvatarVec.DisplayAvatarList.Add(displayAvatar); + }); + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs new file mode 100644 index 00000000..ed5dc285 --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketPlayerGetTokenScRsp : BasePacket + { + public PacketPlayerGetTokenScRsp(Connection connection) : base(CmdIds.PlayerGetTokenScRsp) + { + var rsp = new PlayerGetTokenScRsp() + { + BlackInfo = new BlackInfo(), + Uid = connection.Player?.Uid ?? 0, + }; + + SetData(rsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketPlayerHeartBeatScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketPlayerHeartBeatScRsp.cs new file mode 100644 index 00000000..12e44325 --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketPlayerHeartBeatScRsp.cs @@ -0,0 +1,23 @@ +using EggLink.DanhengServer.Proto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketPlayerHeartBeatScRsp : BasePacket + { + public PacketPlayerHeartBeatScRsp(long clientTime) : base(CmdIds.PlayerHeartBeatScRsp) + { + var data = new PlayerHeartbeatScRsp() + { + ClientTimeMs = (ulong)clientTime, + ServerTimeMs = (ulong)(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond), + }; + + SetData(data); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketPlayerLoginScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketPlayerLoginScRsp.cs new file mode 100644 index 00000000..ea26b99d --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketPlayerLoginScRsp.cs @@ -0,0 +1,26 @@ +using EggLink.DanhengServer.Proto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketPlayerLoginScRsp : BasePacket + { + public PacketPlayerLoginScRsp(Connection connection) : base(CmdIds.PlayerLoginScRsp) + { + var rsp = new PlayerLoginScRsp() + { + CurTimezone = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalHours, + ServerTimestampMs = (ulong)(DateTime.Now.Ticks / 10000), + BasicInfo = connection?.Player?.ToProto(), // should not be null + Stamina = (uint)(connection?.Player?.Data.Stamina ?? 0), + }; + + SetData(rsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketPlayerSyncScNotify.cs b/GameServer/Server/Packet/Send/Player/PacketPlayerSyncScNotify.cs new file mode 100644 index 00000000..01b879bb --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketPlayerSyncScNotify.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketPlayerSyncScNotify : BasePacket + { + public PacketPlayerSyncScNotify() : base(CmdIds.PlayerSyncScNotify) + { + + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketStaminaInfoScNotify.cs b/GameServer/Server/Packet/Send/Player/PacketStaminaInfoScNotify.cs new file mode 100644 index 00000000..809a7575 --- /dev/null +++ b/GameServer/Server/Packet/Send/Player/PacketStaminaInfoScNotify.cs @@ -0,0 +1,20 @@ +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Player +{ + public class PacketStaminaInfoScNotify : BasePacket + { + public PacketStaminaInfoScNotify(PlayerInstance player) : base(CmdIds.StaminaInfoScNotify) + { + var proto = new StaminaInfoScNotify() + { + Stamina = (uint)player.Data.Stamina, + ReserveStamina = (uint)player.Data.StaminaReserve, + NextRecoverTime = player.Data.NextStaminaRecover, + }; + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketGetEnteredSceneScRsp.cs b/GameServer/Server/Packet/Send/Scene/PacketGetEnteredSceneScRsp.cs new file mode 100644 index 00000000..142274bc --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketGetEnteredSceneScRsp.cs @@ -0,0 +1,33 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketGetEnteredSceneScRsp : BasePacket + { + public PacketGetEnteredSceneScRsp() : base(CmdIds.GetEnteredSceneScRsp) + { + var proto = new GetEnteredSceneScRsp(); + + foreach (var excel in GameData.MapEntranceData.Values) + { + // Skip these + if (excel.FinishMainMissionList.Count == 0 && excel.FinishMainMissionList.Count == 0) + { + continue; + } + + // Add info + var info = new EnteredSceneInfo() + { + FloorId = (uint)excel.FloorID, + PlaneId = (uint)excel.PlaneID, + }; + + proto.EnteredSceneInfo.Add(info); + } + + SetData(proto); + } + } +} diff --git a/WebServer/Handler/ComboTokenGranterHandler.cs b/WebServer/Handler/ComboTokenGranterHandler.cs index 0e4ddfec..f52ac559 100644 --- a/WebServer/Handler/ComboTokenGranterHandler.cs +++ b/WebServer/Handler/ComboTokenGranterHandler.cs @@ -17,7 +17,7 @@ namespace EggLink.DanhengServer.WebServer.Handler res.message = "Invalid login data"; return new JsonResult(res); } - AccountData? account = AccountData.GetAccountByUid(long.Parse(tokenData.uid)) as AccountData; + AccountData? account = AccountData.GetAccountByUid(long.Parse(tokenData.uid)); if (account == null) { res.retcode = -201; diff --git a/WebServer/Handler/TokenLoginHandler.cs b/WebServer/Handler/TokenLoginHandler.cs index 842dc90b..71344dcf 100644 --- a/WebServer/Handler/TokenLoginHandler.cs +++ b/WebServer/Handler/TokenLoginHandler.cs @@ -9,7 +9,7 @@ namespace EggLink.DanhengServer.WebServer.Handler { public JsonResult Handle(string uid, string token) { - AccountData? account = AccountData.GetAccountByUid(long.Parse(uid)) as AccountData; + AccountData? account = AccountData.GetAccountByUid(long.Parse(uid)); var res = new LoginResJson(); if (account == null || !account.DispatchToken.Equals(token)) { diff --git a/WebServer/Handler/UsernameLoginHandler.cs b/WebServer/Handler/UsernameLoginHandler.cs index 0de9cb3b..32f11b31 100644 --- a/WebServer/Handler/UsernameLoginHandler.cs +++ b/WebServer/Handler/UsernameLoginHandler.cs @@ -11,14 +11,14 @@ namespace EggLink.DanhengServer.WebServer.Handler public JsonResult Handle(string account, string password, bool isCrypto) { LoginResJson res = new(); - AccountData accountData = (AccountData)AccountData.GetAccountByUserName(account); + AccountData? accountData = AccountData.GetAccountByUserName(account); if (accountData == null) { if (ConfigManager.Config.ServerOption.AutoCreateUser) { AccountHelper.CreateAccount(account, 0); - accountData = (AccountData)AccountData.GetAccountByUserName(account); + accountData = AccountData.GetAccountByUserName(account); } else { diff --git a/WebServer/WebServer.csproj b/WebServer/WebServer.csproj index 50bf9dc4..d84feb49 100644 --- a/WebServer/WebServer.csproj +++ b/WebServer/WebServer.csproj @@ -14,6 +14,7 @@ +