From 505c3caa2834e305d92155da899851c1dc6e4c1c Mon Sep 17 00:00:00 2001 From: Somebody Date: Sun, 10 Mar 2024 16:55:50 +0800 Subject: [PATCH] implement basic scene and cocoon ( not drop ) --- Common/Common.csproj | 1 + Common/Data/Config/FloorInfo.cs | 6 +- Common/Data/Config/MonsterInfo.cs | 2 +- Common/Data/Config/PositionInfo.cs | 21 +++ Common/Data/Config/PropInfo.cs | 2 +- Common/Data/Excel/CocoonConfigExcel.cs | 27 +++ Common/Data/Excel/InteractConfigExcel.cs | 28 +++ Common/Data/Excel/MazePropExcel.cs | 53 ++++++ Common/Data/Excel/NPCDataExcel.cs | 24 +++ Common/Data/Excel/NPCMonsterDataExcel.cs | 25 +++ Common/Data/Excel/QuestDataExcel.cs | 26 +++ Common/Data/Excel/StageConfigExcel.cs | 38 +++- Common/Data/GameData.cs | 6 + Common/Data/ResourceManager.cs | 12 +- Common/Database/Avatar/AvatarData.cs | 168 ++++++++++++++++- Common/Database/DatabaseHelper.cs | 35 +++- Common/Database/Inventory/InventoryData.cs | 2 +- Common/Database/Lineup/LineupData.cs | 101 +++++++++- Common/Database/Player/PlayerData.cs | 4 +- .../Enums/{PropState.cs => PropStateEnum.cs} | 11 +- Common/Enums/PropTypeEnum.cs | 35 ++++ Common/Util/Extensions.cs | 34 +++- Common/Util/Position.cs | 17 ++ GameServer/Command/Cmd/CommandGive.cs | 31 ++++ GameServer/Command/CommandArg.cs | 73 ++++++++ GameServer/Command/CommandInfo.cs | 34 ++++ GameServer/Command/CommandManager.cs | 122 ++++++++++++ GameServer/Command/CommandSender.cs | 22 +++ GameServer/Command/ICommand.cs | 12 ++ GameServer/Game/Avatar/AvatarManager.cs | 16 +- GameServer/Game/Battle/BattleInstance.cs | 56 ++++++ GameServer/Game/Battle/BattleManager.cs | 152 +++++++++++++++ GameServer/Game/Lineup/LineupManager.cs | 49 +++-- GameServer/Game/Player/PlayerInstance.cs | 175 +++++++++++++++++- GameServer/Game/Scene/Entity/EntityMonster.cs | 45 +++++ GameServer/Game/Scene/Entity/EntityNpc.cs | 62 +++++++ GameServer/Game/Scene/Entity/EntityProp.cs | 49 +++++ GameServer/Game/Scene/Entity/IGameEntity.cs | 20 ++ GameServer/Game/Scene/SceneEntityLoader.cs | 128 +++++++++++++ GameServer/Game/Scene/SceneInstance.cs | 156 +++++++++++++++- GameServer/GameServer.csproj | 6 +- GameServer/Program/EntryPoint.cs | 21 ++- GameServer/Server/Connection.cs | 37 +--- GameServer/Server/Listener.cs | 1 - .../Avatar/HandlerGetAssistHistoryCsReq.cs | 18 ++ .../HandlerGetHeroBasicTypeInfoCsReq.cs | 4 +- .../Battle/HandlerGetCurBattleInfoCsReq.cs | 17 ++ .../Battle/HandlerPVEBattleResultCsReq.cs | 19 ++ .../Recv/Battle/HandlerSceneCastSkillCsReq.cs | 18 ++ .../Battle/HandlerStartCocoonStageCsReq.cs | 19 ++ .../Lineup/HandlerGetAllLineupDataCsReq.cs | 13 ++ .../Lineup/HandlerGetCurLineupDataCsReq.cs | 18 ++ .../Recv/Player/HandlerPlayerGetTokenCsReq.cs | 17 +- .../Recv/Player/HandlerPlayerLogoutCsReq.cs | 18 ++ .../Recv/Quest/HandlerGetQuestDataCsReq.cs | 18 ++ .../Rogue/HandlerGetRogueHandbookDataCsReq.cs | 17 ++ .../Recv/Rogue/HandlerGetRogueInfoCsReq.cs | 17 ++ .../HandlerGetRogueScoreRewardInfoCsReq.cs | 17 ++ .../Recv/Scene/HandlerEnterSceneCsReq.cs | 21 +++ .../Recv/Scene/HandlerGetCurSceneInfoCsReq.cs | 5 - ...andlerGetFirstTalkByPerformanceNpcCsReq.cs | 15 ++ .../Recv/Scene/HandlerGetSceneMapInfoCsReq.cs | 15 ++ .../Recv/Scene/HandlerInteractPropCsReq.cs | 16 ++ .../Recv/Scene/HandlerSceneEntityMoveCsReq.cs | 34 ++++ .../Avatar/PacketGetAssistHistoryScRsp.cs | 17 ++ .../PacketGetHeroBasicTypeInfoScRsp.cs | 2 +- .../Send/Battle/PacketPVEBattleResultScRsp.cs | 38 ++++ .../Send/Battle/PacketSceneCastSkillScRsp.cs | 16 ++ .../Battle/PacketStartCocoonStageScRsp.cs | 35 ++++ .../Lineup/PacketGetAllLineupDataScRsp.cs | 27 +++ .../Lineup/PacketGetCurLineupDataScRsp.cs | 23 +++ .../Send/Lineup/PacketSyncLineupNotify.cs | 17 ++ .../Send/Player/PacketPlayerGetTokenScRsp.cs | 11 +- .../Send/Quest/PacketGetQuestDataScRsp.cs | 27 +++ .../Scene/PacketEnterSceneByServerScNotify.cs | 20 ++ ...PacketGetFirstTalkByPerformanceNpcScRsp.cs | 22 +++ .../Send/Scene/PacketGetSceneMapInfoScRsp.cs | 86 +++++++++ .../Scene/PacketGroupStateChangeScNotify.cs | 23 +++ .../Send/Scene/PacketInteractPropScRsp.cs | 20 ++ .../Scene/PacketSceneEntityMoveScNotify.cs | 28 +++ README.md | 4 +- WebServer/WebServer.csproj | 1 + 82 files changed, 2553 insertions(+), 145 deletions(-) create mode 100644 Common/Data/Excel/CocoonConfigExcel.cs create mode 100644 Common/Data/Excel/InteractConfigExcel.cs create mode 100644 Common/Data/Excel/MazePropExcel.cs create mode 100644 Common/Data/Excel/NPCDataExcel.cs create mode 100644 Common/Data/Excel/NPCMonsterDataExcel.cs create mode 100644 Common/Data/Excel/QuestDataExcel.cs rename Common/Enums/{PropState.cs => PropStateEnum.cs} (83%) create mode 100644 Common/Enums/PropTypeEnum.cs create mode 100644 GameServer/Command/Cmd/CommandGive.cs create mode 100644 GameServer/Command/CommandArg.cs create mode 100644 GameServer/Command/CommandInfo.cs create mode 100644 GameServer/Command/CommandManager.cs create mode 100644 GameServer/Command/CommandSender.cs create mode 100644 GameServer/Command/ICommand.cs create mode 100644 GameServer/Game/Battle/BattleInstance.cs create mode 100644 GameServer/Game/Battle/BattleManager.cs create mode 100644 GameServer/Game/Scene/Entity/EntityMonster.cs create mode 100644 GameServer/Game/Scene/Entity/EntityNpc.cs create mode 100644 GameServer/Game/Scene/Entity/EntityProp.cs create mode 100644 GameServer/Game/Scene/Entity/IGameEntity.cs create mode 100644 GameServer/Game/Scene/SceneEntityLoader.cs create mode 100644 GameServer/Server/Packet/Recv/Avatar/HandlerGetAssistHistoryCsReq.cs rename GameServer/Server/Packet/Recv/{Player => Avatar}/HandlerGetHeroBasicTypeInfoCsReq.cs (73%) create mode 100644 GameServer/Server/Packet/Recv/Battle/HandlerGetCurBattleInfoCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Battle/HandlerPVEBattleResultCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Battle/HandlerSceneCastSkillCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Battle/HandlerStartCocoonStageCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Lineup/HandlerGetAllLineupDataCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Lineup/HandlerGetCurLineupDataCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Quest/HandlerGetQuestDataCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueHandbookDataCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueInfoCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueScoreRewardInfoCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Scene/HandlerEnterSceneCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Scene/HandlerGetFirstTalkByPerformanceNpcCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Scene/HandlerGetSceneMapInfoCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Scene/HandlerInteractPropCsReq.cs create mode 100644 GameServer/Server/Packet/Recv/Scene/HandlerSceneEntityMoveCsReq.cs create mode 100644 GameServer/Server/Packet/Send/Avatar/PacketGetAssistHistoryScRsp.cs rename GameServer/Server/Packet/Send/{Player => Avatar}/PacketGetHeroBasicTypeInfoScRsp.cs (90%) create mode 100644 GameServer/Server/Packet/Send/Battle/PacketPVEBattleResultScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Battle/PacketSceneCastSkillScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Battle/PacketStartCocoonStageScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Lineup/PacketGetAllLineupDataScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Lineup/PacketGetCurLineupDataScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Lineup/PacketSyncLineupNotify.cs create mode 100644 GameServer/Server/Packet/Send/Quest/PacketGetQuestDataScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketEnterSceneByServerScNotify.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketGetFirstTalkByPerformanceNpcScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketGetSceneMapInfoScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketGroupStateChangeScNotify.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketInteractPropScRsp.cs create mode 100644 GameServer/Server/Packet/Send/Scene/PacketSceneEntityMoveScNotify.cs diff --git a/Common/Common.csproj b/Common/Common.csproj index 7503b45f..377b9ef3 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -13,6 +13,7 @@ + diff --git a/Common/Data/Config/FloorInfo.cs b/Common/Data/Config/FloorInfo.cs index 44ae1f29..ec24dba1 100644 --- a/Common/Data/Config/FloorInfo.cs +++ b/Common/Data/Config/FloorInfo.cs @@ -14,8 +14,8 @@ namespace EggLink.DanhengServer.Data.Config public bool Loaded = false; public Dictionary Groups = []; - private Dictionary CachedTeleports = []; - private List UnlockedCheckpoints = []; // DEBUG + public Dictionary CachedTeleports = []; + public List UnlockedCheckpoints = []; public AnchorInfo? GetAnchorInfo(int groupId, int anchorId) { @@ -47,7 +47,7 @@ namespace EggLink.DanhengServer.Data.Config UnlockedCheckpoints.Add(prop); // Force prop to be in the unlocked state - prop.State = PropState.CheckPointEnable; + prop.State = PropStateEnum.CheckPointEnable; } else if (!string.IsNullOrEmpty(prop.InitLevelGraph)) { diff --git a/Common/Data/Config/MonsterInfo.cs b/Common/Data/Config/MonsterInfo.cs index aab2d113..dfd24ed8 100644 --- a/Common/Data/Config/MonsterInfo.cs +++ b/Common/Data/Config/MonsterInfo.cs @@ -7,7 +7,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType; namespace EggLink.DanhengServer.Data.Config { - public class MonsterInfo : GroupInfo + public class MonsterInfo : PositionInfo { public int NPCMonsterID { get; set; } public int EventID { get; set; } diff --git a/Common/Data/Config/PositionInfo.cs b/Common/Data/Config/PositionInfo.cs index eb99e70c..45f0d1ea 100644 --- a/Common/Data/Config/PositionInfo.cs +++ b/Common/Data/Config/PositionInfo.cs @@ -1,4 +1,5 @@ using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Util; using System; using System.Collections.Generic; using System.Linq; @@ -16,5 +17,25 @@ namespace EggLink.DanhengServer.Data.Config public bool IsDelete { get; set; } public string Name { get; set; } = ""; public float RotY { get; set; } + + public Position ToPositionProto() + { + return new() + { + X = (int)(PosX * 1000f), + Y = (int)(PosY * 1000f), + Z = (int)(PosZ * 1000f), + }; + } + + public Position ToRotationProto() + { + return new() + { + Y = (int)(RotY * 1000f), + X = 0, + Z = 0, + }; + } } } diff --git a/Common/Data/Config/PropInfo.cs b/Common/Data/Config/PropInfo.cs index 5d883d4b..dba10036 100644 --- a/Common/Data/Config/PropInfo.cs +++ b/Common/Data/Config/PropInfo.cs @@ -22,7 +22,7 @@ namespace EggLink.DanhengServer.Data.Config public string? InitLevelGraph { get; set; } [JsonConverter(typeof(StringEnumConverter))] - public PropState State { get; set; } = PropState.Closed; + public PropStateEnum State { get; set; } = PropStateEnum.Closed; } public class PropValueSource diff --git a/Common/Data/Excel/CocoonConfigExcel.cs b/Common/Data/Excel/CocoonConfigExcel.cs new file mode 100644 index 00000000..2ae7c752 --- /dev/null +++ b/Common/Data/Excel/CocoonConfigExcel.cs @@ -0,0 +1,27 @@ +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("CocoonConfig.json")] + public class CocoonConfigExcel : ExcelResource + { + public int ID { get; set; } + public int MappingInfoID { get; set; } + public int WorldLevel { get; set; } + public int PropID { get; set; } + public int StaminaCost { get; set; } + public int MaxWave { get; set; } + public List StageIDList { get; set; } = []; + public List DropList { get; set; } = []; + + public override int GetId() + { + return (ID * 100) + WorldLevel; + } + + public override void Loaded() + { + GameData.CocoonConfigData.Add(GetId(), this); + } + } +} diff --git a/Common/Data/Excel/InteractConfigExcel.cs b/Common/Data/Excel/InteractConfigExcel.cs new file mode 100644 index 00000000..71b66761 --- /dev/null +++ b/Common/Data/Excel/InteractConfigExcel.cs @@ -0,0 +1,28 @@ +using EggLink.DanhengServer.Enums; +using Newtonsoft.Json.Converters; +using System.Text.Json.Serialization; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("InteractConfig.json")] + public class InteractConfigExcel : ExcelResource + { + public int InteractID { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public PropStateEnum SrcState { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public PropStateEnum TargetState { get; set; } = PropStateEnum.Closed; + + public override int GetId() + { + return InteractID; + } + + public override void Loaded() + { + GameData.InteractConfigData.Add(InteractID, this); + } + } +} diff --git a/Common/Data/Excel/MazePropExcel.cs b/Common/Data/Excel/MazePropExcel.cs new file mode 100644 index 00000000..c0abc2bc --- /dev/null +++ b/Common/Data/Excel/MazePropExcel.cs @@ -0,0 +1,53 @@ +using EggLink.DanhengServer.Enums; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("MazeProp.json")] + public class MazePropExcel : ExcelResource + { + public int ID { get; set; } + public HashName PropName { get; set; } = new(); + public string JsonPath { get; set; } = ""; + + [JsonConverter(typeof(StringEnumConverter))] + public PropTypeEnum PropType { get; set; } + + [JsonProperty(ItemConverterType = typeof(StringEnumConverter))] + public List PropStateList { get; set; } = []; + + public bool IsHpRecover = false; + public bool IsMpRecover = false; + public bool IsDoor = false; + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + if (JsonPath != "") + { + if (JsonPath.Contains("MPBox") || JsonPath.Contains("MPRecover")) + { + IsMpRecover = true; + } else if (JsonPath.Contains("HPBox") || JsonPath.Contains("HPRecover")) + { + IsHpRecover = true; + } else if (JsonPath.Contains("_Door_")) + { + IsDoor = true; + } + } + + GameData.MazePropData.Add(ID, this); + } + } +} diff --git a/Common/Data/Excel/NPCDataExcel.cs b/Common/Data/Excel/NPCDataExcel.cs new file mode 100644 index 00000000..b48b3f1a --- /dev/null +++ b/Common/Data/Excel/NPCDataExcel.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("NPCData.json")] + public class NPCDataExcel : ExcelResource + { + public int ID { get; set; } + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + GameData.NpcDataData.Add(ID, this); + } + } +} diff --git a/Common/Data/Excel/NPCMonsterDataExcel.cs b/Common/Data/Excel/NPCMonsterDataExcel.cs new file mode 100644 index 00000000..8b53381c --- /dev/null +++ b/Common/Data/Excel/NPCMonsterDataExcel.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("NPCMonsterData.json")] + public class NPCMonsterDataExcel : ExcelResource + { + public int ID { get; set; } + public HashName NPCName { get; set; } = new(); + + public override int GetId() + { + return ID; + } + + public override void Loaded() + { + GameData.NpcMonsterDataData.Add(ID, this); + } + } +} diff --git a/Common/Data/Excel/QuestDataExcel.cs b/Common/Data/Excel/QuestDataExcel.cs new file mode 100644 index 00000000..32bb43ed --- /dev/null +++ b/Common/Data/Excel/QuestDataExcel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Data.Excel +{ + [ResourceEntity("QuestData.json")] + public class QuestDataExcel : ExcelResource + { + public int QuestID { get; set; } + public int QuestType { get; set; } + public HashName QuestTitle { get; set; } = new(); + + public override int GetId() + { + return QuestID; + } + + public override void AfterAllDone() + { + GameData.QuestDataData.Add(QuestID, this); + } + } +} diff --git a/Common/Data/Excel/StageConfigExcel.cs b/Common/Data/Excel/StageConfigExcel.cs index 0f08dea1..c3921e73 100644 --- a/Common/Data/Excel/StageConfigExcel.cs +++ b/Common/Data/Excel/StageConfigExcel.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using EggLink.DanhengServer.Proto; +using System.Collections.Generic; namespace EggLink.DanhengServer.Data.Excel { @@ -7,7 +8,7 @@ namespace EggLink.DanhengServer.Data.Excel { public int StageID { get; set; } = 0; public HashName StageName { get; set; } = new HashName(); - public List MonsterList { get; set; } = new List(); + public List MonsterList { get; set; } = []; public override int GetId() @@ -18,6 +19,39 @@ namespace EggLink.DanhengServer.Data.Excel { GameData.StageConfigData.Add(StageID, this); } + + public SceneMonsterWave ToProto() + { + var proto = new SceneMonsterWave() + { + WaveId = 1, + StageId = (uint)StageID, + }; + foreach (var monsters in MonsterList) + { + proto.MonsterList.Add(new SceneMonster() + { + MonsterId = (uint)monsters.Monster0, + }); + proto.MonsterList.Add(new SceneMonster() + { + MonsterId = (uint)monsters.Monster1, + }); + proto.MonsterList.Add(new SceneMonster() + { + MonsterId = (uint)monsters.Monster2, + }); + proto.MonsterList.Add(new SceneMonster() + { + MonsterId = (uint)monsters.Monster3, + }); + proto.MonsterList.Add(new SceneMonster() + { + MonsterId = (uint)monsters.Monster4, + }); + } + return proto; + } } public class StageMonsterList diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 56baec82..5d183d66 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -7,9 +7,15 @@ namespace EggLink.DanhengServer.Data public static class GameData { public static Dictionary AvatarConfigData { get; private set; } = []; + public static Dictionary CocoonConfigData { get; private set; } = []; public static Dictionary StageConfigData { get; private set; } = []; public static Dictionary MapEntranceData { get; private set; } = []; public static Dictionary MazePlaneData { get; private set; } = []; + public static Dictionary MazePropData { get; private set; } = []; + public static Dictionary InteractConfigData { get; private set; } = []; + public static Dictionary NpcMonsterDataData { get; private set; } = []; + public static Dictionary NpcDataData { get; private set; } = []; + public static Dictionary QuestDataData { get; private set; } = []; public static Dictionary FloorInfoData { get; private set; } = []; diff --git a/Common/Data/ResourceManager.cs b/Common/Data/ResourceManager.cs index fb83285b..376c077e 100644 --- a/Common/Data/ResourceManager.cs +++ b/Common/Data/ResourceManager.cs @@ -68,16 +68,18 @@ namespace EggLink.DanhengServer.Data { var id = int.Parse(item.Key); var obj = item.Value; - var instance = JsonConvert.DeserializeObject(obj.ToString(), cls); - if (instance == null) + var instance = JsonConvert.DeserializeObject(obj!.ToString(), cls); + + if (((ExcelResource?)instance)?.GetId() == 0 || ((ExcelResource?)instance) == null) { // Deserialize as JObject to handle nested dictionaries 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) + foreach (var nestedItem in nestedObject ?? []) { - ((ExcelResource?)instance)?.Loaded(); + var nestedInstance = JsonConvert.DeserializeObject(nestedItem.Value!.ToString(), cls); + ((ExcelResource?)nestedInstance)?.Loaded(); + count++; } } else diff --git a/Common/Database/Avatar/AvatarData.cs b/Common/Database/Avatar/AvatarData.cs index 8029d3c8..de1e97c2 100644 --- a/Common/Database/Avatar/AvatarData.cs +++ b/Common/Database/Avatar/AvatarData.cs @@ -1,5 +1,10 @@ -using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database.Inventory; +using EggLink.DanhengServer.Database.Player; using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using Newtonsoft.Json; using SqlSugar; namespace EggLink.DanhengServer.Database.Avatar @@ -7,7 +12,7 @@ namespace EggLink.DanhengServer.Database.Avatar [SugarTable("Avatar")] public class AvatarData : BaseDatabaseData { - [SugarColumn(IsNullable = true)] + [SugarColumn(IsNullable = true, IsJson = true)] public List? Avatars { get; set; } public List AssistAvatars { get; set; } = []; @@ -22,16 +27,26 @@ namespace EggLink.DanhengServer.Database.Avatar 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 CurrentHp { get; set; } = 10000; + public int CurrentSp { get; set; } = 10000; + public int ExtraLineupHp { get; set; } = 10000; + public int ExtraLineupSp { get; set; } = 10000; public int Rank { get; set; } public Dictionary SkillTree { get; set; } = []; public int EquipId { get; set; } = 0; public Dictionary Relic { get; set; } = []; + [JsonIgnore()] public AvatarConfigExcel Excel; + [JsonIgnore()] + public int EntityId; + [JsonIgnore()] + public PlayerData? PlayerData; + + public AvatarInfo() + { + // only for db + } public AvatarInfo(AvatarConfigExcel excel) { @@ -48,11 +63,45 @@ namespace EggLink.DanhengServer.Database.Avatar return (Rewards & (1 << promotion)) != 0; } + public int GetCurHp(bool isExtraLineup) + { + return isExtraLineup ? ExtraLineupHp : CurrentHp; + } + + public int GetCurSp(bool isExtraLineup) + { + return isExtraLineup ? ExtraLineupSp : CurrentSp; + } + + public void SetCurHp(int value, bool isExtraLineup) + { + if (isExtraLineup) + { + ExtraLineupHp = value; + } + else + { + CurrentHp = value; + } + } + + public void SetCurSp(int value, bool isExtraLineup) + { + if (isExtraLineup) + { + ExtraLineupSp = value; + } + else + { + CurrentSp = value; + } + } + public Proto.Avatar ToProto() { var proto = new Proto.Avatar() { - BaseAvatarId = (uint)AvatarId, + BaseAvatarId = (uint)(AvatarId == 1005 ? 8001 : AvatarId), // 1005 will trigger npe Level = (uint)Level, Exp = (uint)Exp, Promotion = (uint)Promotion, @@ -93,5 +142,110 @@ namespace EggLink.DanhengServer.Database.Avatar return proto; } + + public SceneEntityInfo ToSceneEntityInfo(AvatarType avatarType = AvatarType.AvatarFormalType) + { + return new() + { + EntityId = (uint)EntityId, + Motion = new() + { + Pos = PlayerData?.Pos?.ToProto() ?? new(), + Rot = PlayerData?.Rot?.ToProto() ?? new(), + }, + Actor = new() + { + BaseAvatarId = (uint)(AvatarId == 1005? 8001 : AvatarId), + AvatarType = avatarType + } + }; + } + + public LineupAvatar ToLineupInfo(int slot, Lineup.LineupInfo info, AvatarType avatarType = AvatarType.AvatarFormalType) + { + return new() + { + Id = (uint)(AvatarId == 1005 ? 8001 : AvatarId), + Slot = (uint)slot, + AvatarType = avatarType, + Hp = info.IsExtraLineup() ? (uint)ExtraLineupHp : (uint)CurrentHp, + SpBar = new() + { + CurSp = info.IsExtraLineup() ? (uint)ExtraLineupSp : (uint)CurrentSp, + MaxSp = 10000, + }, + }; + } + + public BattleAvatar ToBattleProto(Lineup.LineupInfo lineup, InventoryData inventory, AvatarType avatarType = AvatarType.AvatarFormalType) + { + var proto = new BattleAvatar() + { + Id = (uint)(AvatarId == 1005 ? 8001 : AvatarId), + AvatarType = avatarType, + Level = (uint)Level, + Promotion = (uint)Promotion, + Rank = (uint)Rank, + Index = (uint)lineup.GetSlot(AvatarId), + Hp = (uint)GetCurHp(lineup.LineupType != 0), + SpBar = new() + { + CurSp = (uint)GetCurSp(lineup.LineupType != 0), + MaxSp = 10000, + }, + WorldLevel = (uint)(PlayerData?.WorldLevel ?? 0), + }; + + foreach (var skill in SkillTree) + { + proto.SkilltreeList.Add(new AvatarSkillTree() + { + PointId = (uint)skill.Key, + Level = (uint)skill.Value + }); + } + + foreach (var relic in Relic) + { + var item = inventory.RelicItems?.Find(item => item.UniqueId == relic.Value); + if (item != null) + { + var protoRelic = new BattleRelic() + { + Id = (uint)item.ItemId, + UniqueId = (uint)item.UniqueId, + Level = (uint)item.Level, + MainAffixId = (uint)item.MainAffix, + }; + + if (item.SubAffixes.Count >= 1) + { + foreach (var subAffix in item.SubAffixes) + { + protoRelic.SubAffixList.Add(subAffix.ToProto()); + } + } + + proto.RelicList.Add(protoRelic); + } + } + + if (EquipId != 0) + { + var item = inventory.EquipmentItems?.Find(item => item.UniqueId == EquipId); + if (item != null) + { + proto.EquipmentList.Add(new BattleEquipment() + { + Id = (uint)item.ItemId, + Level = (uint)item.Level, + Promotion = (uint)item.Promotion, + Rank = (uint)item.Rank, + }); + } + } + + return proto; + } } } diff --git a/Common/Database/DatabaseHelper.cs b/Common/Database/DatabaseHelper.cs index f8b7096a..a03ed558 100644 --- a/Common/Database/DatabaseHelper.cs +++ b/Common/Database/DatabaseHelper.cs @@ -13,6 +13,7 @@ namespace EggLink.DanhengServer.Database public ConfigContainer config = ConfigManager.Config; public static SqlSugarScope? sqlSugarScope; public static DatabaseHelper? Instance; + private static readonly object _lock = new(); public DatabaseHelper() { @@ -69,10 +70,14 @@ namespace EggLink.DanhengServer.Database { try { - return sqlSugarScope?.Queryable().Where(it => (it as BaseDatabaseData).Uid == uid).First(); - } catch + lock (_lock) + { + return sqlSugarScope?.Queryable().Where(it => (it as BaseDatabaseData).Uid == uid).First(); + } + } + catch (Exception e) { - logger.Error("Unsupported type"); + logger.Error("Unsupported type", e); return null; } } @@ -81,27 +86,39 @@ namespace EggLink.DanhengServer.Database { try { - return sqlSugarScope?.Queryable().ToList(); - } catch + lock (_lock) + { + return sqlSugarScope?.Queryable().ToList(); + } + } catch(Exception e) { - logger.Error("Unsupported type"); + logger.Error("Unsupported type", e); return null; } } public void SaveInstance(T instance) where T : class, new() { - sqlSugarScope?.Insertable(instance).ExecuteCommand(); + lock (_lock) + { + sqlSugarScope?.Insertable(instance).ExecuteCommand(); + } } public void UpdateInstance(T instance) where T : class, new() { - sqlSugarScope?.Updateable(instance).ExecuteCommand(); + lock (_lock) + { + sqlSugarScope?.Updateable(instance).ExecuteCommand(); + } } public void DeleteInstance(T instance) where T : class, new() { - sqlSugarScope?.Deleteable(instance).ExecuteCommand(); + lock (_lock) + { + sqlSugarScope?.Deleteable(instance).ExecuteCommand(); + } } } } diff --git a/Common/Database/Inventory/InventoryData.cs b/Common/Database/Inventory/InventoryData.cs index 52489704..2fbfdbf2 100644 --- a/Common/Database/Inventory/InventoryData.cs +++ b/Common/Database/Inventory/InventoryData.cs @@ -15,7 +15,6 @@ namespace EggLink.DanhengServer.Database.Inventory public class ItemData { - [SugarColumn(IsPrimaryKey = true)] public int UniqueId { get; set; } public int ItemId { get; set; } public int Count { get; set; } @@ -62,6 +61,7 @@ namespace EggLink.DanhengServer.Database.Inventory } return relic; } + public Equipment ToEquipmentProto() { return new() diff --git a/Common/Database/Lineup/LineupData.cs b/Common/Database/Lineup/LineupData.cs index 26d30223..aa3bb14f 100644 --- a/Common/Database/Lineup/LineupData.cs +++ b/Common/Database/Lineup/LineupData.cs @@ -1,4 +1,6 @@ -using SqlSugar; +using EggLink.DanhengServer.Database.Avatar; +using Newtonsoft.Json; +using SqlSugar; using System; using System.Collections.Generic; using System.Linq; @@ -11,18 +13,107 @@ namespace EggLink.DanhengServer.Database.Lineup public class LineupData : BaseDatabaseData { public int CurLineup { get; set; } // index of current lineup - public string? Lineups { get; set; } // 9 * 4 + [SugarColumn(IsJson = true)] + public Dictionary Lineups { get; set; } = []; // 9 * 4 + public int Mp { get; set; } = 5; } public class LineupInfo { public string? Name { get; set; } public int LineupType { get; set; } - public List? BaseAvatars { get; set; } + public int LeaderAvatarId { get; set; } + public List? BaseAvatars { get; set; } + + [JsonIgnore()] + public LineupData? LineupData { get; set; } + + [JsonIgnore()] + public AvatarData? AvatarData { get; set; } + + public int GetSlot(int avatarId) + { + return BaseAvatars?.FindIndex(item => item.BaseAvatarId == avatarId) ?? -1; + } + + public bool Heal(int count, bool allowRevive) + { + bool result = false; + if (BaseAvatars != null && AvatarData != null) + { + foreach (var avatar in BaseAvatars) + { + var avatarInfo = AvatarData?.Avatars?.Find(item => item.AvatarId == avatar.BaseAvatarId); + if (avatarInfo != null) + { + if (avatarInfo.CurrentHp <= 0 && !allowRevive) + { + continue; + } + if (avatarInfo.CurrentHp >= 10000) + { + continue; + } + avatarInfo.CurrentHp = Math.Min(avatarInfo.GetCurHp(LineupType != 0) + count, 10000); + result = true; + } + } + DatabaseHelper.Instance?.UpdateInstance(AvatarData!); + } + return result; + } + + public bool IsExtraLineup() + { + return LineupType != 0; + } + + public Proto.LineupInfo ToProto() + { + Proto.LineupInfo info = new() + { + Name = Name, + MaxMp = 5, + Mp = (uint)(LineupData?.Mp ?? 0), + ExtraLineupType = Proto.ExtraLineupType.LineupNone, + Index = (uint)(LineupData?.Lineups?.Values.ToList().IndexOf(this) ?? 0), + }; + if (BaseAvatars?.Find(item => item.BaseAvatarId == LeaderAvatarId) != null) + { + info.LeaderSlot = (uint)BaseAvatars.IndexOf(BaseAvatars.Find(item => item.BaseAvatarId == LeaderAvatarId)!); + } else + { + info.LeaderSlot = 0; + } + if (BaseAvatars != null) + { + foreach (var avatar in BaseAvatars) + { + if (avatar.AssistUid != 0) + { + var assistPlayer = DatabaseHelper.Instance?.GetInstance(avatar.AssistUid); + if (assistPlayer != null) + { + info.AvatarList.Add(assistPlayer?.Avatars?.Find(item => item.AvatarId == avatar.BaseAvatarId)?.ToLineupInfo(BaseAvatars.IndexOf(avatar), this, Proto.AvatarType.AvatarAssistType)); + } + } else if (avatar.SpecialAvatarId != 0) + { + + } else + { + info.AvatarList.Add(AvatarData?.Avatars?.Find(item => item.AvatarId == avatar.BaseAvatarId)?.ToLineupInfo(BaseAvatars.IndexOf(avatar), this)); + } + } + } + + return info; + } } - public class LineupInfoJson + public class AvatarInfo { - public Dictionary? Lineups { get; set; } = []; // 9 * 4 + public int BaseAvatarId { get; set; } + public int AssistUid { get; set; } + public int SpecialAvatarId { get; set; } } } diff --git a/Common/Database/Player/PlayerData.cs b/Common/Database/Player/PlayerData.cs index d0fa0f35..e4c06163 100644 --- a/Common/Database/Player/PlayerData.cs +++ b/Common/Database/Player/PlayerData.cs @@ -29,9 +29,9 @@ namespace EggLink.DanhengServer.Database.Player public double StaminaReserve { get; set; } public long NextStaminaRecover { get; set; } - [SugarColumn(IsNullable = true)] + [SugarColumn(IsNullable = true, IsJson = true)] public Position? Pos { get; set; } - [SugarColumn(IsNullable = true)] + [SugarColumn(IsNullable = true, IsJson = true)] public Position? Rot { get; set; } [SugarColumn(IsNullable = true)] public int PlaneId { get; set; } diff --git a/Common/Enums/PropState.cs b/Common/Enums/PropStateEnum.cs similarity index 83% rename from Common/Enums/PropState.cs rename to Common/Enums/PropStateEnum.cs index e7df85e5..d6c22da0 100644 --- a/Common/Enums/PropState.cs +++ b/Common/Enums/PropStateEnum.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace EggLink.DanhengServer.Enums +namespace EggLink.DanhengServer.Enums { - public enum PropState + public enum PropStateEnum { Closed = 0, Open = 1, @@ -44,5 +38,4 @@ namespace EggLink.DanhengServer.Enums CustomState08 = 108, CustomState09 = 109 } - } diff --git a/Common/Enums/PropTypeEnum.cs b/Common/Enums/PropTypeEnum.cs new file mode 100644 index 00000000..6d11d1fe --- /dev/null +++ b/Common/Enums/PropTypeEnum.cs @@ -0,0 +1,35 @@ +namespace EggLink.DanhengServer.Enums +{ + public enum PropTypeEnum + { + PROP_NONE = 0, + PROP_ORDINARY = 1, + PROP_SUMMON = 2, + PROP_DESTRUCT = 3, + PROP_SPRING = 4, + PROP_PLATFORM = 5, + PROP_TREASURE_CHEST = 6, + PROP_MATERIAL_ZONE = 7, + PROP_COCOON = 8, + PROP_MAPPINGINFO = 9, + PROP_PUZZLES = 10, + PROP_ELEVATOR = 11, + PROP_NO_REWARD_DESTRUCT = 12, + PROP_LIGHT = 13, + PROP_ROGUE_DOOR = 14, + PROP_ROGUE_OBJECT = 15, + PROP_ROGUE_CHEST = 16, + PROP_TELEVISION = 17, + PROP_RELIC = 18, + PROP_ELEMENT = 19, + PROP_ROGUE_HIDDEN_DOOR = 20, + PROP_PERSPECTIVE_WALL = 21, + PROP_MAZE_PUZZLE = 22, + PROP_MAZE_DECAL = 23, + PROP_ROGUE_REWARD_OBJECT = 24, + PROP_MAP_ROTATION_CHARGER = 25, + PROP_MAP_ROTATION_VOLUME = 26, + PROP_MAP_ROTATION_SWITCHER = 27, + PROP_BOXMAN_BINDED = 28 + } +} diff --git a/Common/Util/Extensions.cs b/Common/Util/Extensions.cs index e8a81d7a..40626fc2 100644 --- a/Common/Util/Extensions.cs +++ b/Common/Util/Extensions.cs @@ -1,4 +1,6 @@ -using System.Buffers.Binary; +using EggLink.DanhengServer.Proto; +using Newtonsoft.Json; +using System.Buffers.Binary; namespace EggLink.DanhengServer.Util; @@ -67,4 +69,34 @@ public static class Extensions BinaryPrimitives.WriteUInt64BigEndian(data, value); bw.Write(data); } + + public static long GetNowTimeMillis(this DateTime dt) + { + return dt.Ticks / TimeSpan.TicksPerMillisecond; + } + + public static Position ToPosition(this Vector vector) + { + return new Position + { + X = vector.X, + Y = vector.Y, + Z = vector.Z + }; + } + + public static T RandomElement (this List values) + { + var index = new Random().Next(values.Count); + return values[index]; + } + + public static string ToArrayString(this List list) + { + return list.JoinFormat(", ", ""); + } + public static string ToJsonString(this Dictionary dic) + { + return JsonConvert.SerializeObject(dic); + } } diff --git a/Common/Util/Position.cs b/Common/Util/Position.cs index b77fcc14..07ac29bc 100644 --- a/Common/Util/Position.cs +++ b/Common/Util/Position.cs @@ -117,5 +117,22 @@ namespace EggLink.DanhengServer.Util Y /= position.Y; Z /= position.Z; } + + public Vector ToProto() + { + return new() + { + X = X, + Y = Y, + Z = Z + }; + } + + public long GetFast2dDist(Position pos) + { + long x = X - pos.X; + long z = Z - pos.Z; + return (x * x) + (z * z); + } } } diff --git a/GameServer/Command/Cmd/CommandGive.cs b/GameServer/Command/Cmd/CommandGive.cs new file mode 100644 index 00000000..83abdc50 --- /dev/null +++ b/GameServer/Command/Cmd/CommandGive.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Command.Cmd +{ + [CommandInfo("give", "Give item to player", "give l r p x")] + public class CommandGive : ICommand + { + [CommandDefault] + public void GiveItem(CommandArg arg) + { + if (arg.Target == null) + { + arg.SendMsg("Target not found."); + return; + } + + var player = arg.Target.Player; + if (player == null) + { + arg.SendMsg("Target not found."); + return; + } + + + } + } +} diff --git a/GameServer/Command/CommandArg.cs b/GameServer/Command/CommandArg.cs new file mode 100644 index 00000000..61a7dc69 --- /dev/null +++ b/GameServer/Command/CommandArg.cs @@ -0,0 +1,73 @@ +using EggLink.DanhengServer.Server; +using EggLink.DanhengServer.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Command +{ + public class CommandArg + { + public string Raw { get; } + public List BasicArgs { get; } = []; + public Dictionary CharacterArgs { get; } = []; + public Connection? Target { get; set; } + public ICommandSender Sender { get; } + + public CommandArg(string raw, ICommandSender sender, Connection? con = null) + { + Raw = raw; + Sender = sender; + var args = raw.Split(' '); + foreach (var arg in args) + { + if (string.IsNullOrEmpty(arg)) + { + continue; + } + var character = arg[0]; + if (!int.TryParse(character.ToString(), out var _)) + { + try + { + CharacterArgs.Add(arg[..1], arg[1..]); + } catch + { + BasicArgs.Add(arg); + } + } + else + { + BasicArgs.Add(arg); + } + } + if (con != null) + { + Target = con; + } else + { + CharacterArgs.TryGetValue("@", out var target); + if (target != null) + { + var connection = Listener.Connections.Values.ToList().Find(item => item.Player?.Uid.ToString() == target); + if (connection != null) + { + Target = connection; + } + } + } + } + + public void SendMsg(string msg) + { + Sender.SendMsg(msg); + } + + public override string ToString() + { + return $"BasicArg: {BasicArgs.ToArrayString()}. CharacterArg: {CharacterArgs.ToJsonString()}."; + } + } +} diff --git a/GameServer/Command/CommandInfo.cs b/GameServer/Command/CommandInfo.cs new file mode 100644 index 00000000..701b3119 --- /dev/null +++ b/GameServer/Command/CommandInfo.cs @@ -0,0 +1,34 @@ +namespace EggLink.DanhengServer.Command +{ + [AttributeUsage(AttributeTargets.Class)] + public class CommandInfo(string name, string description, string usage, string keyword = "") : Attribute + { + public CommandInfo(string name, string description, string usage, List alias, string keyword = "") : this(name, description, usage, keyword) + { + Alias = alias ?? []; + } + + public string Name { get; } = name; + public string Description { get; } = description; + public string Usage { get; } = usage; + public string Keyword { get; } = keyword; + public List Alias { get; } = []; + } + + [AttributeUsage(AttributeTargets.Method)] + public class CommandMethod(List conditions) : Attribute + { + public List Conditions { get; } = conditions; + } + + [AttributeUsage(AttributeTargets.Method)] + public class CommandDefault : Attribute + { + } + + public class CommandCondition + { + public int Index { get; set; } + public string ShouldBe { get; set; } = ""; + } +} diff --git a/GameServer/Command/CommandManager.cs b/GameServer/Command/CommandManager.cs new file mode 100644 index 00000000..68f28165 --- /dev/null +++ b/GameServer/Command/CommandManager.cs @@ -0,0 +1,122 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Program; +using EggLink.DanhengServer.Server; +using EggLink.DanhengServer.Util; +using Spectre.Console; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Command +{ + public class CommandManager + { + public Dictionary Commands { get; } = []; + public Dictionary CommandInfo { get; } = []; + public Logger Logger { get; } = new Logger("CommandManager"); + public Connection? Target { get; set; } = null; + + public void RegisterCommand() + { + foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) + { + var attr = type.GetCustomAttribute(); + if (attr != null) + { + var instance = Activator.CreateInstance(type); + if (instance is ICommand command) + { + Commands.Add(attr.Name, command); + CommandInfo.Add(attr.Name, attr); + } + } + } + Logger.Info($"Register {Commands.Count} commands."); + } + + public void Start() + { + while (true) + { + string? input = AnsiConsole.Ask("> "); + if (string.IsNullOrEmpty(input)) + { + continue; + } + var cmd = input.Split(' ')[0]; + if (cmd.StartsWith('@')) + { + var target = cmd[1..]; + var con = Listener.Connections.Values.ToList().Find(item => item.Player?.Uid.ToString() == target); + if (con != null) + { + Target = con; + Logger.Info($"Online player {target}({con.Player!.Data.Name}) is found, the next command will target it by default."); + } + else + { + // offline or not exist + Logger.Warn($"Target {target} is offline or not found."); + } + continue; + } + if (Commands.TryGetValue(cmd, out var command)) + { + var split = input.Split(' ').ToList(); + split.RemoveAt(0); + + var arg = new CommandArg(split.JoinFormat(" ", ""), new ConsoleCommandSender(Logger), Target); + // find the proper method with attribute CommandMethod + var isFound = false; + foreach (var method in command.GetType().GetMethods()) + { + var attr = method.GetCustomAttribute(); + if (attr != null) + { + var canRun = true; + foreach (var condition in attr.Conditions) + { + if (split.Count <= condition.Index) + { + canRun = false; + break; + } + if (!split[condition.Index].Equals(condition.ShouldBe)) + { + canRun = false; + break; + } + } + if (canRun) + { + isFound = true; + method.Invoke(command, [arg]); + break; + } + } + } + if (!isFound) + { + // find the default method with attribute CommandDefault + foreach (var method in command.GetType().GetMethods()) + { + var attr = method.GetCustomAttribute(); + if (attr != null) + { + method.Invoke(command, [arg]); + break; + } + } + } + } + else + { + Logger.Info($"Command {cmd} not found."); + } + } + } + } +} diff --git a/GameServer/Command/CommandSender.cs b/GameServer/Command/CommandSender.cs new file mode 100644 index 00000000..9297b953 --- /dev/null +++ b/GameServer/Command/CommandSender.cs @@ -0,0 +1,22 @@ +using EggLink.DanhengServer.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Command +{ + public interface ICommandSender + { + public void SendMsg(string msg); + } + + public class ConsoleCommandSender(Logger logger) : ICommandSender + { + public void SendMsg(string msg) + { + logger.Info(msg); + } + } +} diff --git a/GameServer/Command/ICommand.cs b/GameServer/Command/ICommand.cs new file mode 100644 index 00000000..cd6e4873 --- /dev/null +++ b/GameServer/Command/ICommand.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Command +{ + public interface ICommand + { + } +} diff --git a/GameServer/Game/Avatar/AvatarManager.cs b/GameServer/Game/Avatar/AvatarManager.cs index 71455692..9c51eb44 100644 --- a/GameServer/Game/Avatar/AvatarManager.cs +++ b/GameServer/Game/Avatar/AvatarManager.cs @@ -8,7 +8,7 @@ namespace EggLink.DanhengServer.Game.Avatar { public class AvatarManager : BasePlayerManager { - public AvatarData AvatarData { get; private set; } + public AvatarData? AvatarData { get; private set; } public AvatarManager(PlayerInstance player) : base(player) { @@ -25,6 +25,11 @@ namespace EggLink.DanhengServer.Game.Avatar else { AvatarData = avatars; + foreach (var avatar in AvatarData?.Avatars ?? []) + { + avatar.PlayerData = player.Data; + avatar.Excel = GameData.AvatarConfigData[avatar.AvatarId]; + } } } @@ -44,13 +49,18 @@ namespace EggLink.DanhengServer.Game.Avatar CurrentSp = 0 }; - if (AvatarData.Avatars == null) + if (AvatarData?.Avatars == null) { - AvatarData.Avatars = []; + AvatarData!.Avatars = []; } AvatarData.Avatars.Add(avatar); DatabaseHelper.Instance?.UpdateInstance(AvatarData); } + + public AvatarInfo? GetAvatar(int baseAvatarId) + { + return AvatarData?.Avatars?.Find(avatar => avatar.AvatarId == baseAvatarId); + } } } diff --git a/GameServer/Game/Battle/BattleInstance.cs b/GameServer/Game/Battle/BattleInstance.cs new file mode 100644 index 00000000..40d742c4 --- /dev/null +++ b/GameServer/Game/Battle/BattleInstance.cs @@ -0,0 +1,56 @@ +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Game.Battle +{ + public class BattleInstance(PlayerInstance player, Database.Lineup.LineupInfo lineup, List stages) : BasePlayerManager(player) + { + public int BattleId { get; set; } = ++player.NextBattleId; + public int StaminaCost { get; set; } + public int WorldLevel { get; set; } + public int CocoonWave { get; set; } + public int MappingInfoId { get; set; } + public int RoundLimit { get; set; } + public int StageId { get; set; } = stages[0].StageID; + public BattleEndStatus BattleEndStatus { get; set; } + + public List Stages { get; set; } = stages; + public Database.Lineup.LineupInfo Lineup { get; set; } = lineup; + + public ItemList GetDropItemList() + { + return new() + { + + }; + } + + public SceneBattleInfo ToProto() + { + var proto = new SceneBattleInfo() + { + BattleId = (uint)BattleId, + WorldLevel = (uint)WorldLevel, + RoundsLimit = (uint)RoundLimit, + StageId = (uint)StageId, + LogicRandomSeed = (uint)Random.Shared.Next(), + }; + + foreach (var wave in Stages) + { + proto.MonsterWaveList.Add(wave.ToProto()); + } + + foreach (var avatar in Lineup.BaseAvatars!) + { + var avatarInstance = Player.AvatarManager.GetAvatar(avatar.BaseAvatarId); + if (avatarInstance == null) continue; + + proto.BattleAvatarList.Add(avatarInstance.ToBattleProto(Player.LineupManager.GetCurLineup()!, Player.InventoryManager.Data)); + } + + return proto; + } + } +} diff --git a/GameServer/Game/Battle/BattleManager.cs b/GameServer/Game/Battle/BattleManager.cs new file mode 100644 index 00000000..0bfa3604 --- /dev/null +++ b/GameServer/Game/Battle/BattleManager.cs @@ -0,0 +1,152 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Battle; +using EggLink.DanhengServer.Server.Packet.Send.Lineup; +using EggLink.DanhengServer.Util; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game.Battle +{ + public class BattleManager(PlayerInstance player) : BasePlayerManager(player) + { + public void StartCocoonStage(int cocoonId, int wave, int worldLevel) + { + if (Player.BattleInstance != null) return; + + GameData.CocoonConfigData.TryGetValue(cocoonId * 100 + worldLevel, out var config); + if (config == null) + { + Player.SendPacket(new PacketStartCocoonStageScRsp()); + return; + } + wave = Math.Min(Math.Max(wave, 1), config.MaxWave); + + int cost = config.StaminaCost * wave; + if (Player.Data.Stamina < cost) + { + Player.SendPacket(new PacketStartCocoonStageScRsp()); + return; + } + + List stageConfigExcels = []; + for (int i = 0; i < wave; i++) + { + var stageId = config.StageIDList.RandomElement(); + GameData.StageConfigData.TryGetValue(stageId, out var stageConfig); + if (stageConfig == null) continue; + + stageConfigExcels.Add(stageConfig); + } + + if (stageConfigExcels.Count == 0) + { + Player.SendPacket(new PacketStartCocoonStageScRsp()); + return; + } + + BattleInstance battleInstance = new(Player, Player.LineupManager.GetCurLineup()!, stageConfigExcels) + { + StaminaCost = cost, + WorldLevel = config.WorldLevel, + CocoonWave = wave, + MappingInfoId = config.MappingInfoID, + }; + + Player.BattleInstance = battleInstance; + + Player.SendPacket(new PacketStartCocoonStageScRsp(battleInstance, cocoonId, wave)); + } + + public void EndBattle(PVEBattleResultCsReq req) + { + if (Player.BattleInstance == null) + { + Player.SendPacket(new PacketPVEBattleResultScRsp()); + return; + } + Player.BattleInstance.BattleEndStatus = req.EndStatus; + var battle = Player.BattleInstance; + bool updateStatus = true; + bool teleportToAnchor = false; + var minimumHp = 0; + + switch (req.EndStatus) + { + case BattleEndStatus.BattleEndWin: + // Remove monsters from the map - Could optimize it a little better + //for (var monster in battle.NpcMonsters) + //{ + // // Dont remove farmable monsters from the scene when they are defeated + // if (monster.isFarmElement()) continue; + // // Remove monster + // player.SceneInstance.RemoveEntity(monster); + //} + // Drops + // Spend stamina + if (battle.StaminaCost > 0) + { + player.SpendStamina(battle.StaminaCost); + } + break; + case BattleEndStatus.BattleEndLose: + // Set avatar hp to 20% if the player's party is downed + minimumHp = 2000; + teleportToAnchor = true; + break; + case BattleEndStatus.BattleEndQuit: + updateStatus = false; + break; + default: + updateStatus = false; + break; + } + + if (updateStatus) + { + var lineup = player.LineupManager.GetCurLineup()!; + // Update battle status + foreach (var avatar in req.Stt.BattleAvatarList) + { + var avatarInstance = player.AvatarManager.GetAvatar((int)avatar.Id); + if (avatarInstance == null) continue; + + var prop = avatar.AvatarStatus; + int curHp = (int)Math.Round(prop.LeftHp / prop.MaxHp * 10000); + int curSp = (int)prop.LeftSp * 100; + + avatarInstance.SetCurHp(curHp, lineup.LineupType != 0); + avatarInstance.SetCurSp(curSp, lineup.LineupType != 0); + } + + DatabaseHelper.Instance?.UpdateInstance(Player.AvatarManager.AvatarData!); + Player.SendPacket(new PacketSyncLineupNotify(battle.Lineup)); + } + if (teleportToAnchor) + { + var anchorProp = player.SceneInstance.GetNearestSpring(long.MaxValue); + if (anchorProp != null && anchorProp.PropInfo != null) + { + var anchor = player?.SceneInstance?.FloorInfo?.GetAnchorInfo( + anchorProp.PropInfo.AnchorGroupID, + anchorProp.PropInfo.AnchorID + ); + if (anchor != null) + { + Player.MoveTo(anchor.ToPositionProto()); + } + } + } + + Player.BattleInstance = null; + Player.SendPacket(new PacketPVEBattleResultScRsp(req, Player, battle)); + } + } +} diff --git a/GameServer/Game/Lineup/LineupManager.cs b/GameServer/Game/Lineup/LineupManager.cs index a2e09285..bf2a2ff7 100644 --- a/GameServer/Game/Lineup/LineupManager.cs +++ b/GameServer/Game/Lineup/LineupManager.cs @@ -8,7 +8,7 @@ namespace EggLink.DanhengServer.Game.Lineup public class LineupManager : BasePlayerManager { public LineupData LineupData { get; private set; } - public LineupInfoJson LineupInfoJson { get; private set; } + public Dictionary LineupInfo { get; private set; } public LineupManager(PlayerInstance player) : base(player) { @@ -18,29 +18,29 @@ namespace EggLink.DanhengServer.Game.Lineup LineupData = new() { Uid = player.Uid, - CurLineup = 0, - Lineups = "{}", + CurLineup = 1, }; DatabaseHelper.Instance?.SaveInstance(LineupData); } else { LineupData = lineup; + if (LineupData.Lineups != null) + { + foreach (var lineupInfo in LineupData.Lineups?.Values!) + { + lineupInfo.LineupData = LineupData; + lineupInfo.AvatarData = player.AvatarManager.AvatarData; + } + } } - LineupInfoJson = JsonConvert.DeserializeObject(LineupData.Lineups ?? "{}") ?? new(); + LineupInfo = LineupData.Lineups ?? []; } public LineupInfo? GetLineup(int lineupIndex) { - if (LineupData.Lineups == null) - { - return null; - } - if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count) - { - return null; - } - return LineupInfoJson.Lineups?[lineupIndex]; + LineupInfo.TryGetValue(lineupIndex, out var lineup); + return lineup; } public LineupInfo? GetCurLineup() @@ -50,7 +50,7 @@ namespace EggLink.DanhengServer.Game.Lineup public void SetCurLineup(int lineupIndex) { - if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count) + if (lineupIndex < 0 || !LineupInfo.ContainsKey(lineupIndex)) { return; } @@ -60,31 +60,28 @@ namespace EggLink.DanhengServer.Game.Lineup public void AddAvatar(int lineupIndex, int avatarId) { - if (lineupIndex < 0 || LineupData == null) + if (lineupIndex < 0) { return; } - if (LineupData.Lineups == null) - { - LineupData.Lineups = ""; - } - LineupInfo? lineup = null; - LineupInfoJson.Lineups?.TryGetValue(lineupIndex, out lineup); + LineupInfo.TryGetValue(lineupIndex, out LineupInfo? lineup); if (lineup == null) { lineup = new() { Name = "Lineup " + lineupIndex, LineupType = 0, - BaseAvatars = [avatarId], + BaseAvatars = [new() { BaseAvatarId = avatarId }], + LineupData = LineupData, + AvatarData = Player.AvatarManager.AvatarData, }; - LineupInfoJson.Lineups?.Add(lineupIndex, lineup); + LineupInfo.Add(lineupIndex, lineup); } else { - lineup.BaseAvatars?.Add(avatarId); + lineup.BaseAvatars?.Add(new() { BaseAvatarId = avatarId }); + LineupInfo[lineupIndex] = lineup; } - LineupData.Lineups = JsonConvert.SerializeObject(LineupInfoJson); - DatabaseHelper.Instance?.UpdateInstance(LineupData!); + DatabaseHelper.Instance?.UpdateInstance(LineupData); } public void AddAvatarToCurTeam(int avatarId) diff --git a/GameServer/Game/Player/PlayerInstance.cs b/GameServer/Game/Player/PlayerInstance.cs index 108efc27..eef042c5 100644 --- a/GameServer/Game/Player/PlayerInstance.cs +++ b/GameServer/Game/Player/PlayerInstance.cs @@ -1,15 +1,21 @@ using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Data.Excel; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Database.Player; -using EggLink.DanhengServer.Enums; using EggLink.DanhengServer.Game.Avatar; +using EggLink.DanhengServer.Game.Battle; using EggLink.DanhengServer.Game.Inventory; using EggLink.DanhengServer.Game.Lineup; using EggLink.DanhengServer.Game.Scene; +using EggLink.DanhengServer.Game.Scene.Entity; using EggLink.DanhengServer.Proto; using EggLink.DanhengServer.Server; using EggLink.DanhengServer.Server.Packet; +using EggLink.DanhengServer.Server.Packet.Send.Lineup; using EggLink.DanhengServer.Server.Packet.Send.Player; +using EggLink.DanhengServer.Server.Packet.Send.Scene; +using EggLink.DanhengServer.Util; namespace EggLink.DanhengServer.Game.Player { @@ -19,12 +25,16 @@ namespace EggLink.DanhengServer.Game.Player public ushort Uid { get; set; } public Connection? Connection { get; set; } public bool Initialized { get; set; } = false; + public bool IsNewPlayer { get; set; } = false; + public int NextBattleId { get; set; } = 0; #region Managers public AvatarManager AvatarManager { get; private set; } public LineupManager LineupManager { get; private set; } public InventoryManager InventoryManager { get; private set; } + public BattleManager? BattleManager { get; private set; } + public BattleInstance? BattleInstance { get; set; } #endregion @@ -35,9 +45,10 @@ namespace EggLink.DanhengServer.Game.Player #endregion - public PlayerInstance() : this(new PlayerData()) + public PlayerInstance(int uid) : this(new PlayerData() { Uid = uid }) { // new player + IsNewPlayer = true; Data.Name = "无名客"; // Trailblazer in EN TODO: Add localization Data.Signature = ""; Data.Birthday = 0; @@ -56,13 +67,19 @@ namespace EggLink.DanhengServer.Game.Player Data.Scoin = 0; Data.Hcoin = 0; Data.Mcoin = 0; + Data.PlaneId = 20001; + Data.FloorId = 20001001; Data.TalentPoints = 0; + DatabaseHelper.Instance?.SaveInstance(Data); InitialPlayerManager(); AddAvatar(1005); - LineupManager.SetCurLineup(0); + LineupManager.SetCurLineup(1); LineupManager.AddAvatarToCurTeam(1005); + + EnterScene(2000101, 0, false); + Initialized = true; } @@ -72,6 +89,7 @@ namespace EggLink.DanhengServer.Game.Player AvatarManager = new(this); LineupManager = new(this); InventoryManager = new(this); + BattleManager = new(this); var unlock = DatabaseHelper.Instance?.GetInstance(Uid); if (unlock == null) @@ -83,7 +101,11 @@ namespace EggLink.DanhengServer.Game.Player unlock = DatabaseHelper.Instance?.GetInstance(Uid); } PlayerUnlockData = unlock!; - SceneInstance = new(this, GameData.MazePlaneData[20001], 20001001); + + if (!IsNewPlayer) + { + LoadScene(Data.PlaneId, Data.FloorId, Data.EntryId, Data.Pos!, Data.Rot!, false); + } } @@ -102,7 +124,11 @@ namespace EggLink.DanhengServer.Game.Player public async Task OnLogoutAsync() { - + DatabaseHelper.Instance?.UpdateInstance(Data); + DatabaseHelper.Instance?.UpdateInstance(PlayerUnlockData); + DatabaseHelper.Instance?.UpdateInstance(LineupManager.LineupData); + DatabaseHelper.Instance?.UpdateInstance(InventoryManager.Data); + DatabaseHelper.Instance?.UpdateInstance(AvatarManager.AvatarData!); } public void SendPacket(BasePacket packet) @@ -118,6 +144,145 @@ namespace EggLink.DanhengServer.Game.Player AvatarManager.AddAvatar(avatarId); } + public void OnMove() + { + if (SceneInstance != null) + { + EntityProp? prop = SceneInstance.GetNearestSpring(25_000_000); + + bool isInRange = prop != null; + + if (isInRange) + { + if (LineupManager.GetCurLineup()?.Heal(10000, true) == true) + { + SendPacket(new PacketSyncLineupNotify(LineupManager.GetCurLineup()!)); + } + } + } + } + + public EntityProp? InteractProp(int propEntityId, int interactId) + { + if (SceneInstance != null) + { + SceneInstance.Entities.TryGetValue(propEntityId, out IGameEntity? entity); + if (entity == null) return null; + if (entity is EntityProp prop) + { + GameData.InteractConfigData.TryGetValue(interactId, out var config); + if (config == null || config.SrcState != prop.State) return prop; + + var oldState = prop.State; + var newState = prop.State = config.TargetState; + SendPacket(new PacketGroupStateChangeScNotify(Data.EntryId, prop.GroupID, prop.State)); + switch (prop.Excel.PropType) + { + case Enums.PropTypeEnum.PROP_TREASURE_CHEST: + if (oldState == Enums.PropStateEnum.ChestClosed && newState == Enums.PropStateEnum.ChestUsed) + { + // TODO: Add treasure chest handling + } + break; + case Enums.PropTypeEnum.PROP_DESTRUCT: + if (newState == Enums.PropStateEnum.Closed) + { + prop.State = Enums.PropStateEnum.Open; + } + break; + case Enums.PropTypeEnum.PROP_MAZE_PUZZLE: + if (newState == Enums.PropStateEnum.Closed || newState == Enums.PropStateEnum.Open) + { + foreach (var p in SceneInstance.GetEntitiesInGroup(prop.GroupID)) + { + if (p.Excel.PropType == Enums.PropTypeEnum.PROP_TREASURE_CHEST) + { + p.State = Enums.PropStateEnum.ChestClosed; + } + else if (p.Excel.PropType == Enums.PropTypeEnum.PROP_MAZE_PUZZLE) + { + // Skip + } + else + { + p.State = Enums.PropStateEnum.Open; + } + } + } + break; + } + return prop; + } + } + return null; + } + + public void EnterScene(int entryId, int teleportId, bool sendPacket) + { + GameData.MapEntranceData.TryGetValue(entryId, out var entrance); + if (entrance == null) return; + + GameData.GetFloorInfo(entrance.PlaneID, entrance.FloorID, out var floorInfo); + if (floorInfo == null) return; + + int StartGroup = entrance.StartGroupID; + int StartAnchor = entrance.StartAnchorID; + + if (teleportId != 0) + { + floorInfo.CachedTeleports.TryGetValue(teleportId, out var teleport); + if (teleport != null) + { + StartGroup = teleport.AnchorGroupID; + StartAnchor = teleport.AnchorID; + } + } else if (StartAnchor == 0) + { + StartGroup = floorInfo.StartGroupID; + StartAnchor = floorInfo.StartAnchorID; + } + AnchorInfo? anchor = floorInfo.GetAnchorInfo(StartGroup, StartAnchor); + + LoadScene(entrance.PlaneID, entrance.FloorID, entryId, anchor!.ToPositionProto(), anchor.ToRotationProto(), sendPacket); + } + + public void MoveTo(Position position) + { + Data.Pos = position; + SendPacket(new PacketSceneEntityMoveScNotify(this)); + } + + + public void LoadScene(int planeId, int floorId, int entryId, Position pos, Position rot, bool sendPacket) + { + GameData.MazePlaneData.TryGetValue(planeId, out var plane); + if (plane == null) return; + + // TODO: Sanify check + Data.Pos = pos; + Data.Rot = rot; + SceneInstance instance = new(this, plane, floorId, entryId); + if (planeId != Data.PlaneId || floorId != Data.FloorId || entryId != Data.EntryId) + { + Data.PlaneId = planeId; + Data.FloorId = floorId; + Data.EntryId = entryId; + DatabaseHelper.Instance?.UpdateInstance(Data); + } + SceneInstance = instance; + + if (sendPacket) + { + SendPacket(new PacketEnterSceneByServerScNotify(instance)); + } + } + + public void SpendStamina(int staminaCost) + { + Data.Stamina -= staminaCost; + SendPacket(new PacketStaminaInfoScNotify(this)); + } + #endregion #region Proto diff --git a/GameServer/Game/Scene/Entity/EntityMonster.cs b/GameServer/Game/Scene/Entity/EntityMonster.cs new file mode 100644 index 00000000..cba230b7 --- /dev/null +++ b/GameServer/Game/Scene/Entity/EntityMonster.cs @@ -0,0 +1,45 @@ +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game.Scene.Entity +{ + public class EntityMonster(SceneInstance scene, Position pos, Position rot, int GroupID, int InstID, NPCMonsterDataExcel excel, MonsterInfo info) : IGameEntity + { + public int EntityID { get; set; } = 0; + public int GroupID { get; set; } = GroupID; + public Position Position { get; set; } = pos; + public Position Rotation { get; set; } = rot; + public int InstID { get; set; } = InstID; + public NPCMonsterDataExcel MonsterData { get; set; } = excel; + public MonsterInfo Info { get; set; } = info; + + public SceneEntityInfo ToProto() + { + return new() + { + EntityId = (uint)EntityID, + GroupId = (uint)GroupID, + InstId = (uint)InstID, + Motion = new() + { + Pos = Position.ToProto(), + Rot = Rotation.ToProto() + }, + NpcMonster = new() + { + EventId = (uint)Info.EventID, + MonsterId = (uint)MonsterData.ID, + WorldLevel = (uint)scene.Player.Data.WorldLevel, + } + + }; + } + } +} diff --git a/GameServer/Game/Scene/Entity/EntityNpc.cs b/GameServer/Game/Scene/Entity/EntityNpc.cs new file mode 100644 index 00000000..7d5836c8 --- /dev/null +++ b/GameServer/Game/Scene/Entity/EntityNpc.cs @@ -0,0 +1,62 @@ +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game.Scene.Entity +{ + public class EntityNpc(SceneInstance scene, GroupInfo group, NpcInfo npcInfo) : IGameEntity + { + public int EntityID { get; set; } + public int GroupID { get; set; } = group.Id; + public Position Position { get; set; } = npcInfo.ToPositionProto(); + public Position Rotation { get; set; } = npcInfo.ToRotationProto(); + public int NpcId { get; set; } = npcInfo.NPCID; + public int InstId { get; set; } = npcInfo.ID; + + #region For Rogue + + public int RogueNpcId { get; set; } + public bool IsFinishedTalk { get; set; } + public int EventUniqueId { get; set; } + + #endregion + + public SceneEntityInfo ToProto() + { + SceneNpcInfo npc = new() + { + NpcId = (uint)NpcId, + }; + if (RogueNpcId > 0) + { + var rogue = new NpcRogueInfo() + { + RogueNpcId = (uint)RogueNpcId, + FinishDialogue = IsFinishedTalk, + UniqueId = (uint)EventUniqueId, + }; + + npc.ExtraInfo.RogueInfo = rogue; + } + + return new SceneEntityInfo() + { + EntityId = (uint)EntityID, + GroupId = (uint)GroupID, + Motion = new MotionInfo() + { + Pos = Position.ToProto(), + Rot = Rotation.ToProto(), + }, + InstId = (uint)InstId, + Npc = npc, + }; + } + } +} diff --git a/GameServer/Game/Scene/Entity/EntityProp.cs b/GameServer/Game/Scene/Entity/EntityProp.cs new file mode 100644 index 00000000..b8216e86 --- /dev/null +++ b/GameServer/Game/Scene/Entity/EntityProp.cs @@ -0,0 +1,49 @@ +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; + +namespace EggLink.DanhengServer.Game.Scene.Entity +{ + public class EntityProp(SceneInstance scene, MazePropExcel excel, GroupInfo group, PropInfo prop) : IGameEntity + { + public int EntityID { get; set; } + public int GroupID { get; set; } = group.Id; + public Position Position { get; set; } = prop.ToPositionProto(); + public Position Rotation { get; set; } = prop.ToRotationProto(); + public PropStateEnum State { get; set; } = PropStateEnum.Closed; + public int InstId { get; set; } = prop.ID; + public MazePropExcel Excel { get; set; } = excel; + public PropInfo PropInfo { get; set; } = prop; + + public PropRogueInfo? RogueInfo { get; set; } + + public SceneEntityInfo ToProto() + { + var prop = new ScenePropInfo() + { + PropId = (uint)Excel.ID, + PropState = (uint)State, + }; + + if (RogueInfo != null) + { + prop.ExtraInfo.RogueInfo = RogueInfo; + } + + return new SceneEntityInfo() + { + EntityId = (uint)EntityID, + GroupId = (uint)GroupID, + Motion = new MotionInfo() + { + Pos = Position.ToProto(), + Rot = Rotation.ToProto(), + }, + InstId = (uint)InstId, + Prop = prop, + }; + } + } +} diff --git a/GameServer/Game/Scene/Entity/IGameEntity.cs b/GameServer/Game/Scene/Entity/IGameEntity.cs new file mode 100644 index 00000000..0202a55c --- /dev/null +++ b/GameServer/Game/Scene/Entity/IGameEntity.cs @@ -0,0 +1,20 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Game.Scene.Entity +{ + public interface IGameEntity + { + public int EntityID { get; set; } + public int GroupID { get; set; } + public Position Position { get; set; } + public Position Rotation { get; set; } + + public SceneEntityInfo ToProto(); + } +} diff --git a/GameServer/Game/Scene/SceneEntityLoader.cs b/GameServer/Game/Scene/SceneEntityLoader.cs new file mode 100644 index 00000000..b9542d2f --- /dev/null +++ b/GameServer/Game/Scene/SceneEntityLoader.cs @@ -0,0 +1,128 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Game.Scene.Entity; + +namespace EggLink.DanhengServer.Game.Scene +{ + public class SceneEntityLoader(SceneInstance scene) + { + public void LoadEntity() + { + if (scene.IsLoaded) return; + + foreach (var group in scene?.FloorInfo?.Groups.Values!) // Sanity check in SceneInstance + { + if (group.LoadSide == GroupLoadSideEnum.Client) + { + continue; + } + + LoadGroup(group); + } + scene.IsLoaded = true; + } + + public void LoadGroup(GroupInfo info) + { + foreach (var npc in info.NPCList) + { + try + { + LoadNpc(npc, info); + } catch + { + } + } + + foreach (var monster in info.MonsterList) + { + try + { + LoadMonster(monster, info); + } catch + { + } + } + + foreach (var prop in info.PropList) + { + try + { + LoadProp(prop, info); + } catch + { + } + } + } + + public void LoadNpc(NpcInfo info, GroupInfo group) + { + if (info.IsClientOnly || info.IsDelete) + { + return; + } + if (!GameData.NpcDataData.ContainsKey(info.NPCID)) + { + return; + } + bool hasDuplicateNpcId = false; + foreach (IGameEntity entity in scene.Entities.Values) + { + if (entity is EntityNpc eNpc && eNpc.NpcId == info.NPCID) + { + hasDuplicateNpcId = true; + break; + } + } + if (hasDuplicateNpcId) + { + return; + } + EntityNpc npc = new(scene, group, info); + scene.AddEntity(npc); + } + + public void LoadMonster(MonsterInfo info, GroupInfo group) + { + if (info.IsClientOnly || info.IsDelete) + { + return; + } + + GameData.NpcMonsterDataData.TryGetValue(info.NPCMonsterID, out var excel); + if (excel == null) + { + return; + } + + EntityMonster entity = new(scene ,info.ToPositionProto(), info.ToRotationProto(), group.Id, excel.ID, excel, info); + scene.AddEntity(entity); + } + + public void LoadProp(PropInfo info, GroupInfo group) + { + if (info.IsClientOnly || info.IsDelete) + { + return; + } + + GameData.MazePropData.TryGetValue(info.PropID, out var excel); + if (excel == null) + { + return; + } + + var prop = new EntityProp(scene, excel, group, info); + + scene.AddEntity(prop); + + if (excel.PropType == PropTypeEnum.PROP_SPRING) + { + scene.HealingSprings.Add(prop); + prop.State = PropStateEnum.CheckPointEnable; + } else + prop.State = PropStateEnum.Open; + } + } +} diff --git a/GameServer/Game/Scene/SceneInstance.cs b/GameServer/Game/Scene/SceneInstance.cs index 7224c546..711043ef 100644 --- a/GameServer/Game/Scene/SceneInstance.cs +++ b/GameServer/Game/Scene/SceneInstance.cs @@ -1,7 +1,10 @@ using EggLink.DanhengServer.Data; using EggLink.DanhengServer.Data.Config; using EggLink.DanhengServer.Data.Excel; +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Avatar; using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Game.Scene.Entity; using EggLink.DanhengServer.Proto; namespace EggLink.DanhengServer.Game.Scene @@ -10,26 +13,130 @@ namespace EggLink.DanhengServer.Game.Scene { public PlayerInstance Player; public MazePlaneExcel Excel; - public FloorInfo FloorInfo; + public FloorInfo? FloorInfo; public int FloorId; public int PlaneId; public int EntryId; - public int LeaveEntryId; public int LastEntityId; public bool IsLoaded = false; - public SceneInstance(PlayerInstance player, MazePlaneExcel excel, int floorId) + public Dictionary AvatarInfo = []; + public int LeaderAvatarId; + public Dictionary Entities = []; + public List HealingSprings = []; + + public SceneEntityLoader? EntityLoader; + + public SceneInstance(PlayerInstance player, MazePlaneExcel excel, int floorId, int entryId) { Player = player; Excel = excel; PlaneId = excel.PlaneID; FloorId = floorId; + EntryId = entryId; + + SyncLineup(); + GameData.GetFloorInfo(PlaneId, FloorId, out FloorInfo); if (FloorInfo == null) return; + switch (Excel.PlaneType) + { + default: + EntityLoader = new(this); + break; + } + + EntityLoader.LoadEntity(); } + public void SyncLineup() + { + AvatarInfo = []; + foreach (var avatar in Player.LineupManager?.GetCurLineup()?.BaseAvatars ?? []) + { + if (avatar.AssistUid != 0) + { + var assistPlayer = DatabaseHelper.Instance?.GetInstance(avatar.AssistUid); + if (assistPlayer != null) + { + var assistAvatar = assistPlayer.Avatars?.Find(x => x.AvatarId == avatar.BaseAvatarId); + if (assistAvatar != null) + { + AvatarInfo.Add(assistAvatar.AvatarId, new(assistAvatar, AvatarType.AvatarAssistType)); + } + } + } else if (avatar.SpecialAvatarId != 0) + { + + } else + { + var avatarData = Player.AvatarManager?.GetAvatar(avatar.BaseAvatarId); + if (avatarData?.AvatarId == avatar.BaseAvatarId) + { + avatarData.EntityId = ++LastEntityId; + AvatarInfo.Add(avatarData.EntityId, new(avatarData, AvatarType.AvatarFormalType)); + } + } + }; + + LeaderAvatarId = Player.LineupManager?.GetCurLineup()?.LeaderAvatarId ?? 0; + } + + public EntityProp? GetNearestSpring(long minDistSq) + { + EntityProp? spring = null; + long springDist = 0; + + foreach (EntityProp prop in HealingSprings) + { + long dist = Player.Data?.Pos?.GetFast2dDist(prop.Position) ?? 1000000; + if (dist > minDistSq) continue; + + if (spring == null || dist < springDist) + { + spring = prop; + springDist = dist; + } + } + + return spring; + } + + public void AddEntity(IGameEntity entity) + { + AddEntity(entity, IsLoaded); + } + + public void AddEntity(IGameEntity entity, bool SendPacket) + { + if (entity == null || entity.EntityID != 0) return; + entity.EntityID = ++LastEntityId; + + Entities.Add(entity.EntityID, entity); + } + + public void RemoveEntity(IGameEntity monster, bool SendPacket = false) + { + Entities.Remove(monster.EntityID); + } + + public List GetEntitiesInGroup(int groupID) + { + List entities = []; + foreach (var entity in Entities) + { + if (entity.Value.GroupID == groupID && entity.Value is T t) + { + entities.Add(t); + } + } + return entities; + } + + #region Proto Convert + public SceneInfo ToProto() { SceneInfo sceneInfo = new() @@ -40,8 +147,51 @@ namespace EggLink.DanhengServer.Game.Scene FloorId = (uint)FloorId, EntryId = (uint)EntryId, }; + var playerGroupInfo = new SceneEntityGroupInfo(); // avatar group + foreach (var avatar in AvatarInfo) + { + playerGroupInfo.EntityList.Add(avatar.Value.AvatarInfo.ToSceneEntityInfo(avatar.Value.AvatarType)); + } + + if (LeaderAvatarId != 0) + { + sceneInfo.LeaderEntityId = (uint)LeaderAvatarId; + } else + { + LeaderAvatarId = AvatarInfo.Keys.First(); + sceneInfo.LeaderEntityId = (uint)LeaderAvatarId; + } + sceneInfo.EntityGroupList.Add(playerGroupInfo); + + List groups = []; // other groups + + foreach (var entity in Entities) + { + if (entity.Value.GroupID == 0) continue; + if (groups.FindIndex(x => x.GroupId == entity.Value.GroupID) == -1) + { + groups.Add(new SceneEntityGroupInfo() + { + GroupId = (uint)entity.Value.GroupID + }); + } + groups[groups.FindIndex(x => x.GroupId == entity.Value.GroupID)].EntityList.Add(entity.Value.ToProto()); + } + + foreach (var group in groups) + { + sceneInfo.EntityGroupList.Add(group); + } return sceneInfo; } + + #endregion + } + + public class AvatarSceneInfo(AvatarInfo avatarInfo, AvatarType avatarType) + { + public AvatarInfo AvatarInfo = avatarInfo; + public AvatarType AvatarType = avatarType; } } diff --git a/GameServer/GameServer.csproj b/GameServer/GameServer.csproj index 6840fba8..8eb77c24 100644 --- a/GameServer/GameServer.csproj +++ b/GameServer/GameServer.csproj @@ -15,27 +15,23 @@ - - - - - + diff --git a/GameServer/Program/EntryPoint.cs b/GameServer/Program/EntryPoint.cs index cd250410..7cbf255f 100644 --- a/GameServer/Program/EntryPoint.cs +++ b/GameServer/Program/EntryPoint.cs @@ -5,6 +5,9 @@ using EggLink.DanhengServer.WebServer; using EggLink.DanhengServer.Database; using EggLink.DanhengServer.Server; using EggLink.DanhengServer.Server.Packet; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using EggLink.DanhengServer.Command; namespace EggLink.DanhengServer.Program { @@ -14,6 +17,7 @@ namespace EggLink.DanhengServer.Program public static DatabaseHelper DatabaseHelper = new(); public static Listener Listener = new(); public static HandlerManager HandlerManager = new(); + public static CommandManager CommandManager = new(); public static void Main(string[] args) { @@ -65,6 +69,15 @@ namespace EggLink.DanhengServer.Program Console.ReadLine(); return; } + try + { + CommandManager.RegisterCommand(); + } catch (Exception e) + { + logger.Error("Failed to initialize command manager", e); + Console.ReadLine(); + return; + } WebProgram.Main([$"--urls=http://{GetConfig().HttpServer.PublicAddress}:{GetConfig().HttpServer.PublicPort}/"]); logger.Info($"DispatchServer is running on http://{GetConfig().HttpServer.PublicAddress}:{GetConfig().HttpServer.PublicPort}/"); @@ -73,10 +86,10 @@ namespace EggLink.DanhengServer.Program var elapsed = DateTime.Now - time; logger.Info($"Done in {elapsed.TotalSeconds.ToString()[..4]}s! Type '/help' to get help of commands."); - while (true) - { - Console.ReadLine(); - } +#if DEBUG + JsonConvert.DeserializeObject(File.ReadAllText("LogMap.json"))!.Properties().ToList().ForEach(x => Connection.LogMap.Add(x.Name, x.Value.ToString())); +#endif + CommandManager.Start(); } public static ConfigContainer GetConfig() diff --git a/GameServer/Server/Connection.cs b/GameServer/Server/Connection.cs index 19f670fe..8e753cc6 100644 --- a/GameServer/Server/Connection.cs +++ b/GameServer/Server/Connection.cs @@ -23,13 +23,10 @@ public partial class Connection public readonly IPEndPoint RemoteEndPoint; public SessionState State { get; set; } = SessionState.INACTIVE; 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 readonly Logger Logger = new("GameServer"); #if DEBUG - private static readonly Dictionary LogMap = []; + public static readonly Dictionary LogMap = []; #endif public Connection(KcpConversation conversation, IPEndPoint remote) { @@ -37,9 +34,6 @@ 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() @@ -65,12 +59,6 @@ public partial class Connection } - private void UpdateLastPingTime(uint clientTime) - { - ClientTime = clientTime; - LastPingTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - } - #if DEBUG public static void LogPacket(string sendOrRecv, ushort opcode, byte[] payload) { @@ -246,27 +234,6 @@ public partial class Connection 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 + SendPacket(new BasePacket((ushort)cmdId)); } } diff --git a/GameServer/Server/Listener.cs b/GameServer/Server/Listener.cs index 934148d4..78680fad 100644 --- a/GameServer/Server/Listener.cs +++ b/GameServer/Server/Listener.cs @@ -14,7 +14,6 @@ namespace EggLink.DanhengServer.Server private static UdpClient? UDPClient; private static IPEndPoint? ListenAddress; private static IKcpTransport? KCPTransport; - private static readonly CancellationTokenSource CancelToken = new(); private static readonly Logger Logger = new("GameServer"); private static IKcpMultiplexConnection? Multiplex => KCPTransport?.Connection; public static readonly SortedList Connections = []; diff --git a/GameServer/Server/Packet/Recv/Avatar/HandlerGetAssistHistoryCsReq.cs b/GameServer/Server/Packet/Recv/Avatar/HandlerGetAssistHistoryCsReq.cs new file mode 100644 index 00000000..0c4f615f --- /dev/null +++ b/GameServer/Server/Packet/Recv/Avatar/HandlerGetAssistHistoryCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Avatar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar +{ + [Opcode(CmdIds.GetAssistHistoryCsReq)] + public class HandlerGetAssistHistoryCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetAssistHistoryScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs b/GameServer/Server/Packet/Recv/Avatar/HandlerGetHeroBasicTypeInfoCsReq.cs similarity index 73% rename from GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs rename to GameServer/Server/Packet/Recv/Avatar/HandlerGetHeroBasicTypeInfoCsReq.cs index ce925790..d88be833 100644 --- a/GameServer/Server/Packet/Recv/Player/HandlerGetHeroBasicTypeInfoCsReq.cs +++ b/GameServer/Server/Packet/Recv/Avatar/HandlerGetHeroBasicTypeInfoCsReq.cs @@ -1,6 +1,6 @@ -using EggLink.DanhengServer.Server.Packet.Send.Player; +using EggLink.DanhengServer.Server.Packet.Send.Avatar; -namespace EggLink.DanhengServer.Server.Packet.Recv.Player +namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar { [Opcode(CmdIds.GetHeroBasicTypeInfoCsReq)] public class HandlerGetHeroBasicTypeInfoCsReq : Handler diff --git a/GameServer/Server/Packet/Recv/Battle/HandlerGetCurBattleInfoCsReq.cs b/GameServer/Server/Packet/Recv/Battle/HandlerGetCurBattleInfoCsReq.cs new file mode 100644 index 00000000..e35276a3 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Battle/HandlerGetCurBattleInfoCsReq.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Battle +{ + [Opcode(CmdIds.GetCurBattleInfoCsReq)] + public class HandlerGetCurBattleInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.GetCurBattleInfoScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Battle/HandlerPVEBattleResultCsReq.cs b/GameServer/Server/Packet/Recv/Battle/HandlerPVEBattleResultCsReq.cs new file mode 100644 index 00000000..1428df21 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Battle/HandlerPVEBattleResultCsReq.cs @@ -0,0 +1,19 @@ +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.Recv.Battle +{ + [Opcode(CmdIds.PVEBattleResultCsReq)] + public class HandlerPVEBattleResultCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = PVEBattleResultCsReq.Parser.ParseFrom(data); + connection.Player?.BattleManager?.EndBattle(req); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Battle/HandlerSceneCastSkillCsReq.cs b/GameServer/Server/Packet/Recv/Battle/HandlerSceneCastSkillCsReq.cs new file mode 100644 index 00000000..7665d886 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Battle/HandlerSceneCastSkillCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Battle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Battle +{ + [Opcode(CmdIds.SceneCastSkillCsReq)] + public class HandlerSceneCastSkillCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketSceneCastSkillScRsp()); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Battle/HandlerStartCocoonStageCsReq.cs b/GameServer/Server/Packet/Recv/Battle/HandlerStartCocoonStageCsReq.cs new file mode 100644 index 00000000..54076db3 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Battle/HandlerStartCocoonStageCsReq.cs @@ -0,0 +1,19 @@ +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.Recv.Battle +{ + [Opcode(CmdIds.StartCocoonStageCsReq)] + public class HandlerStartCocoonStageCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = StartCocoonStageCsReq.Parser.ParseFrom(data); + connection.Player?.BattleManager?.StartCocoonStage((int)req.CocoonId, (int)req.Wave, (int)req.WorldLevel); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Lineup/HandlerGetAllLineupDataCsReq.cs b/GameServer/Server/Packet/Recv/Lineup/HandlerGetAllLineupDataCsReq.cs new file mode 100644 index 00000000..c9515201 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lineup/HandlerGetAllLineupDataCsReq.cs @@ -0,0 +1,13 @@ +using EggLink.DanhengServer.Server.Packet.Send.Lineup; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Lineup +{ + [Opcode(CmdIds.GetAllLineupDataCsReq)] + public class HandlerGetAllLineupDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetAllLineupDataScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Lineup/HandlerGetCurLineupDataCsReq.cs b/GameServer/Server/Packet/Recv/Lineup/HandlerGetCurLineupDataCsReq.cs new file mode 100644 index 00000000..dcf100c7 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Lineup/HandlerGetCurLineupDataCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Lineup; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Lineup +{ + [Opcode(CmdIds.GetCurLineupDataCsReq)] + public class HandlerGetCurLineupDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetCurLineupDataScRsp(connection.Player!)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs index 5a1c1cd4..67f56e0f 100644 --- a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs @@ -1,10 +1,10 @@ using EggLink.DanhengServer.Common.Enums; using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Account; 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; namespace EggLink.DanhengServer.Server.Packet.Recv.Player { @@ -14,17 +14,20 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Player public override void OnHandle(Connection connection, byte[] header, byte[] data) { var req = PlayerGetTokenCsReq.Parser.ParseFrom(data); + + var account = DatabaseHelper.Instance?.GetInstance(long.Parse(req.AccountUid)); + if (account == null) + { + connection.SendPacket(new PacketPlayerGetTokenScRsp()); + return; + } + 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), - }; + connection.Player = new PlayerInstance(int.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/HandlerPlayerLogoutCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs new file mode 100644 index 00000000..ff1c5db6 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerLogoutCsReq.cs @@ -0,0 +1,18 @@ +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.PlayerLogoutCsReq)] + public class HandlerPlayerLogoutCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.PlayerLogoutScRsp); + connection.Stop(); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Quest/HandlerGetQuestDataCsReq.cs b/GameServer/Server/Packet/Recv/Quest/HandlerGetQuestDataCsReq.cs new file mode 100644 index 00000000..63dff54d --- /dev/null +++ b/GameServer/Server/Packet/Recv/Quest/HandlerGetQuestDataCsReq.cs @@ -0,0 +1,18 @@ +using EggLink.DanhengServer.Server.Packet.Send.Quest; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Quest +{ + [Opcode(CmdIds.GetQuestDataCsReq)] + public class HandlerGetQuestDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(new PacketGetQuestDataScRsp()); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueHandbookDataCsReq.cs b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueHandbookDataCsReq.cs new file mode 100644 index 00000000..ce843636 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueHandbookDataCsReq.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Rogue +{ + [Opcode(CmdIds.GetRogueHandbookDataCsReq)] + public class HandlerGetRogueHandbookDataCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.GetRogueHandbookDataScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueInfoCsReq.cs b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueInfoCsReq.cs new file mode 100644 index 00000000..7af3af01 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueInfoCsReq.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Rogue +{ + [Opcode(CmdIds.GetRogueInfoCsReq)] + public class HandlerGetRogueInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.GetRogueInfoScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueScoreRewardInfoCsReq.cs b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueScoreRewardInfoCsReq.cs new file mode 100644 index 00000000..9728ce9e --- /dev/null +++ b/GameServer/Server/Packet/Recv/Rogue/HandlerGetRogueScoreRewardInfoCsReq.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Rogue +{ + [Opcode(CmdIds.GetRogueScoreRewardInfoCsReq)] + public class HandlerGetRogueScoreRewardInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + connection.SendPacket(CmdIds.GetRogueScoreRewardInfoScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerEnterSceneCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerEnterSceneCsReq.cs new file mode 100644 index 00000000..3e052005 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerEnterSceneCsReq.cs @@ -0,0 +1,21 @@ +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.Recv.Scene +{ + [Opcode(CmdIds.EnterSceneCsReq)] + public class HandlerEnterSceneCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = EnterSceneCsReq.Parser.ParseFrom(data); + connection.Player?.EnterScene((int)req.EntryId, (int)req.TeleportId, true); + + connection.SendPacket(CmdIds.EnterSceneScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerGetCurSceneInfoCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerGetCurSceneInfoCsReq.cs index 7e62c9c7..0fcb0236 100644 --- a/GameServer/Server/Packet/Recv/Scene/HandlerGetCurSceneInfoCsReq.cs +++ b/GameServer/Server/Packet/Recv/Scene/HandlerGetCurSceneInfoCsReq.cs @@ -1,9 +1,4 @@ using EggLink.DanhengServer.Server.Packet.Send.Scene; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace EggLink.DanhengServer.Server.Packet.Recv.Scene { diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerGetFirstTalkByPerformanceNpcCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerGetFirstTalkByPerformanceNpcCsReq.cs new file mode 100644 index 00000000..e7b15723 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerGetFirstTalkByPerformanceNpcCsReq.cs @@ -0,0 +1,15 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Scene; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Scene +{ + [Opcode(CmdIds.GetFirstTalkByPerformanceNpcCsReq)] + public class HandlerGetFirstTalkByPerformanceNpcCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetFirstTalkByPerformanceNpcCsReq.Parser.ParseFrom(data); + connection.SendPacket(new PacketGetFirstTalkByPerformanceNpcScRsp(req)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerGetSceneMapInfoCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerGetSceneMapInfoCsReq.cs new file mode 100644 index 00000000..10261ed3 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerGetSceneMapInfoCsReq.cs @@ -0,0 +1,15 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Scene; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Scene +{ + [Opcode(CmdIds.GetSceneMapInfoCsReq)] + public class HandlerGetSceneMapInfoCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = GetSceneMapInfoCsReq.Parser.ParseFrom(data); + connection.SendPacket(new PacketGetSceneMapInfoScRsp(req)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerInteractPropCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerInteractPropCsReq.cs new file mode 100644 index 00000000..be8eb058 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerInteractPropCsReq.cs @@ -0,0 +1,16 @@ +using EggLink.DanhengServer.Proto; +using EggLink.DanhengServer.Server.Packet.Send.Scene; + +namespace EggLink.DanhengServer.Server.Packet.Recv.Scene +{ + [Opcode(CmdIds.InteractPropCsReq)] + public class HandlerInteractPropCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = InteractPropCsReq.Parser.ParseFrom(data); + var prop = connection.Player?.InteractProp((int)req.PropEntityId, (int)req.InteractId); + connection.SendPacket(new PacketInteractPropScRsp(prop)); + } + } +} diff --git a/GameServer/Server/Packet/Recv/Scene/HandlerSceneEntityMoveCsReq.cs b/GameServer/Server/Packet/Recv/Scene/HandlerSceneEntityMoveCsReq.cs new file mode 100644 index 00000000..8e3e39f9 --- /dev/null +++ b/GameServer/Server/Packet/Recv/Scene/HandlerSceneEntityMoveCsReq.cs @@ -0,0 +1,34 @@ +using EggLink.DanhengServer.Proto; +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.Scene +{ + [Opcode(CmdIds.SceneEntityMoveCsReq)] + public class HandlerSceneEntityMoveCsReq : Handler + { + public override void OnHandle(Connection connection, byte[] header, byte[] data) + { + var req = SceneEntityMoveCsReq.Parser.ParseFrom(data); + if (req != null) + { + foreach (var motion in req.EntityMotionList) + { + var avatar = connection?.Player?.SceneInstance.AvatarInfo.ToList().Find(x => x.Value.AvatarInfo.EntityId == motion.EntityId); + if (avatar != null) + { + connection!.Player!.Data.Pos = motion.Motion.Pos.ToPosition(); + connection.Player.Data.Rot = motion.Motion.Rot.ToPosition(); + connection.Player.OnMove(); + } + } + } + + connection!.SendPacket(CmdIds.SceneEntityMoveScRsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Avatar/PacketGetAssistHistoryScRsp.cs b/GameServer/Server/Packet/Send/Avatar/PacketGetAssistHistoryScRsp.cs new file mode 100644 index 00000000..4128bd87 --- /dev/null +++ b/GameServer/Server/Packet/Send/Avatar/PacketGetAssistHistoryScRsp.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Game.Player; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EggLink.DanhengServer.Server.Packet.Send.Avatar +{ + public class PacketGetAssistHistoryScRsp : BasePacket + { + public PacketGetAssistHistoryScRsp(PlayerInstance player) : base(CmdIds.GetAssistHistoryScRsp) + { + + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs b/GameServer/Server/Packet/Send/Avatar/PacketGetHeroBasicTypeInfoScRsp.cs similarity index 90% rename from GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs rename to GameServer/Server/Packet/Send/Avatar/PacketGetHeroBasicTypeInfoScRsp.cs index d760ed1b..d37528b9 100644 --- a/GameServer/Server/Packet/Send/Player/PacketGetHeroBasicTypeInfoScRsp.cs +++ b/GameServer/Server/Packet/Send/Avatar/PacketGetHeroBasicTypeInfoScRsp.cs @@ -1,7 +1,7 @@ using EggLink.DanhengServer.Game.Player; using EggLink.DanhengServer.Proto; -namespace EggLink.DanhengServer.Server.Packet.Send.Player +namespace EggLink.DanhengServer.Server.Packet.Send.Avatar { public class PacketGetHeroBasicTypeInfoScRsp : BasePacket { diff --git a/GameServer/Server/Packet/Send/Battle/PacketPVEBattleResultScRsp.cs b/GameServer/Server/Packet/Send/Battle/PacketPVEBattleResultScRsp.cs new file mode 100644 index 00000000..1d2cc42f --- /dev/null +++ b/GameServer/Server/Packet/Send/Battle/PacketPVEBattleResultScRsp.cs @@ -0,0 +1,38 @@ +using EggLink.DanhengServer.Game.Battle; +using EggLink.DanhengServer.Game.Player; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Battle +{ + public class PacketPVEBattleResultScRsp : BasePacket + { + public PacketPVEBattleResultScRsp() : base(CmdIds.PVEBattleResultScRsp) + { + var proto = new PVEBattleResultScRsp() + { + Retcode = 1, + }; + + SetData(proto); + } + + public PacketPVEBattleResultScRsp(PVEBattleResultCsReq req, PlayerInstance player, BattleInstance battle) : base(CmdIds.PVEBattleResultScRsp) + { + var proto = new PVEBattleResultScRsp() + { + DropData = battle.GetDropItemList(), + ResVersion = req.ClientResVersion.ToString(), + BinVersion = "", + StageId = req.StageId, + BattleId = req.BattleId, + EndStatus = req.EndStatus, + CheckIdentical = true, + Unk1 = new(), + Unk2 = new(), + Unk3 = new(), + }; + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Battle/PacketSceneCastSkillScRsp.cs b/GameServer/Server/Packet/Send/Battle/PacketSceneCastSkillScRsp.cs new file mode 100644 index 00000000..88c34834 --- /dev/null +++ b/GameServer/Server/Packet/Send/Battle/PacketSceneCastSkillScRsp.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.Battle +{ + public class PacketSceneCastSkillScRsp : BasePacket + { + public PacketSceneCastSkillScRsp() : base(CmdIds.SceneCastSkillScRsp) + { + + } + } +} diff --git a/GameServer/Server/Packet/Send/Battle/PacketStartCocoonStageScRsp.cs b/GameServer/Server/Packet/Send/Battle/PacketStartCocoonStageScRsp.cs new file mode 100644 index 00000000..d29ca0a9 --- /dev/null +++ b/GameServer/Server/Packet/Send/Battle/PacketStartCocoonStageScRsp.cs @@ -0,0 +1,35 @@ +using EggLink.DanhengServer.Game.Battle; +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.Battle +{ + public class PacketStartCocoonStageScRsp : BasePacket + { + public PacketStartCocoonStageScRsp() : base(CmdIds.StartCocoonStageScRsp) + { + var rsp = new StartCocoonStageScRsp() + { + Retcode = 1 + }; + + SetData(rsp); + } + + public PacketStartCocoonStageScRsp(BattleInstance battle, int cocoonId, int wave) : base(CmdIds.StartCocoonStageScRsp) + { + var rsp = new StartCocoonStageScRsp() + { + CocoonId = (uint)cocoonId, + Wave = (uint)wave, + BattleInfo = battle.ToProto() + }; + + SetData(rsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Lineup/PacketGetAllLineupDataScRsp.cs b/GameServer/Server/Packet/Send/Lineup/PacketGetAllLineupDataScRsp.cs new file mode 100644 index 00000000..1d8c53df --- /dev/null +++ b/GameServer/Server/Packet/Send/Lineup/PacketGetAllLineupDataScRsp.cs @@ -0,0 +1,27 @@ +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.Lineup +{ + public class PacketGetAllLineupDataScRsp : BasePacket + { + public PacketGetAllLineupDataScRsp(PlayerInstance player) : base(CmdIds.GetAllLineupDataScRsp) + { + var proto = new GetAllLineupDataScRsp() + { + CurIndex = (uint)(player.LineupManager.LineupData.CurLineup - 1), + }; + foreach (var lineup in player.LineupManager.LineupInfo.Values) + { + proto.LineupList.Add(lineup.ToProto()); + } + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Lineup/PacketGetCurLineupDataScRsp.cs b/GameServer/Server/Packet/Send/Lineup/PacketGetCurLineupDataScRsp.cs new file mode 100644 index 00000000..a896f3ce --- /dev/null +++ b/GameServer/Server/Packet/Send/Lineup/PacketGetCurLineupDataScRsp.cs @@ -0,0 +1,23 @@ +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.Lineup +{ + public class PacketGetCurLineupDataScRsp : BasePacket + { + public PacketGetCurLineupDataScRsp(PlayerInstance player) : base(CmdIds.GetCurLineupDataScRsp) + { + var data = new GetCurLineupDataScRsp() + { + Lineup = player.LineupManager.GetCurLineup()!.ToProto(), + }; + + SetData(data); + } + } +} diff --git a/GameServer/Server/Packet/Send/Lineup/PacketSyncLineupNotify.cs b/GameServer/Server/Packet/Send/Lineup/PacketSyncLineupNotify.cs new file mode 100644 index 00000000..3bc397f4 --- /dev/null +++ b/GameServer/Server/Packet/Send/Lineup/PacketSyncLineupNotify.cs @@ -0,0 +1,17 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Lineup +{ + public class PacketSyncLineupNotify : BasePacket + { + public PacketSyncLineupNotify(Database.Lineup.LineupInfo info) : base(CmdIds.SyncLineupNotify) + { + var proto = new SyncLineupNotify() + { + Lineup = info.ToProto(), + }; + + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs b/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs index ed5dc285..e946f3b4 100644 --- a/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs +++ b/GameServer/Server/Packet/Send/Player/PacketPlayerGetTokenScRsp.cs @@ -8,10 +8,19 @@ namespace EggLink.DanhengServer.Server.Packet.Send.Player { var rsp = new PlayerGetTokenScRsp() { - BlackInfo = new BlackInfo(), + BlackInfo = new(), Uid = connection.Player?.Uid ?? 0, }; + SetData(rsp); + } + public PacketPlayerGetTokenScRsp() : base(CmdIds.PlayerGetTokenScRsp) + { + var rsp = new PlayerGetTokenScRsp() + { + Retcode = 114514, + }; + SetData(rsp); } } diff --git a/GameServer/Server/Packet/Send/Quest/PacketGetQuestDataScRsp.cs b/GameServer/Server/Packet/Send/Quest/PacketGetQuestDataScRsp.cs new file mode 100644 index 00000000..5dbdf83d --- /dev/null +++ b/GameServer/Server/Packet/Send/Quest/PacketGetQuestDataScRsp.cs @@ -0,0 +1,27 @@ +using EggLink.DanhengServer.Data; +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.Quest +{ + public class PacketGetQuestDataScRsp : BasePacket + { + public PacketGetQuestDataScRsp() : base(CmdIds.GetQuestDataScRsp) + { + var proto = new GetQuestDataScRsp(); + foreach (var quest in GameData.QuestDataData.Values) + { + proto.QuestList.Add(new Proto.Quest() + { + Id = (uint)quest.QuestID, + Status = QuestStatus.QuestFinish + }); + } + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketEnterSceneByServerScNotify.cs b/GameServer/Server/Packet/Send/Scene/PacketEnterSceneByServerScNotify.cs new file mode 100644 index 00000000..8112cb5e --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketEnterSceneByServerScNotify.cs @@ -0,0 +1,20 @@ +using EggLink.DanhengServer.Game.Scene; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketEnterSceneByServerScNotify : BasePacket + { + public PacketEnterSceneByServerScNotify(SceneInstance scene) : base(CmdIds.EnterSceneByServerScNotify) + { + var sceneInfo = scene.ToProto(); + var notify = new EnterSceneByServerScNotify() + { + Scene = sceneInfo, + Lineup = scene.Player.LineupManager.GetCurLineup()!.ToProto(), + }; + + SetData(notify); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketGetFirstTalkByPerformanceNpcScRsp.cs b/GameServer/Server/Packet/Send/Scene/PacketGetFirstTalkByPerformanceNpcScRsp.cs new file mode 100644 index 00000000..e143a2ce --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketGetFirstTalkByPerformanceNpcScRsp.cs @@ -0,0 +1,22 @@ +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketGetFirstTalkByPerformanceNpcScRsp : BasePacket + { + public PacketGetFirstTalkByPerformanceNpcScRsp(GetFirstTalkByPerformanceNpcCsReq req) : base(CmdIds.GetFirstTalkByPerformanceNpcScRsp) + { + var rsp = new GetFirstTalkByPerformanceNpcScRsp(); + + foreach (var id in req.NpcTalkList) + { + rsp.NpcTalkInfoList.Add(new NpcTalkInfo + { + NpcTalkId = id, + }); + } + + SetData(rsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketGetSceneMapInfoScRsp.cs b/GameServer/Server/Packet/Send/Scene/PacketGetSceneMapInfoScRsp.cs new file mode 100644 index 00000000..898d85ad --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketGetSceneMapInfoScRsp.cs @@ -0,0 +1,86 @@ +using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Config; +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketGetSceneMapInfoScRsp : BasePacket + { + public PacketGetSceneMapInfoScRsp(GetSceneMapInfoCsReq req) : base(CmdIds.GetSceneMapInfoScRsp) + { + var rsp = new GetSceneMapInfoScRsp(); + foreach (var entry in req.EntryIdList) + { + var mazeMap = new MazeMapData() + { + EntryId = entry, + }; + GameData.MapEntranceData.TryGetValue((int)entry, out var mapData); + if (mapData == null) + { + rsp.MapList.Add(mazeMap); + continue; + } + + GameData.GetFloorInfo(mapData.PlaneID, mapData.FloorID, out var floorInfo); + if (floorInfo == null) + { + rsp.MapList.Add(mazeMap); + continue; + } + + mazeMap.UnlockedChestList.Add(new MazeChest() + { + TotalAmountList = 1, + MapInfoChestType = MapInfoChestType.Normal + }); + + mazeMap.UnlockedChestList.Add(new MazeChest() + { + TotalAmountList = 1, + MapInfoChestType = MapInfoChestType.Puzzle + }); + + mazeMap.UnlockedChestList.Add(new MazeChest() + { + TotalAmountList = 1, + MapInfoChestType = MapInfoChestType.Challenge + }); + + foreach (GroupInfo groupInfo in floorInfo.Groups.Values) // all the icons on the map + { + var mazeGroup = new MazeGroup() + { + GroupId = (uint)groupInfo.Id, + }; + mazeMap.MazeGroupList.Add(mazeGroup); + } + + foreach (var teleport in floorInfo.CachedTeleports.Values) + { + mazeMap.UnlockedTeleportList.Add((uint)teleport.MappingInfoID); + } + + foreach (var prop in floorInfo.UnlockedCheckpoints) + { + var mazeProp = new MazeProp() + { + GroupId = (uint)prop.AnchorGroupID, + ConfigId = (uint)prop.ID, + State = (uint)PropStateEnum.CheckPointEnable, + }; + mazeMap.MazePropList.Add(mazeProp); + } + + for (int i = 0; i < 100; i++) + { + mazeMap.LightenSectionList.Add((uint)i); + } + + rsp.MapList.Add(mazeMap); + } + SetData(rsp); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketGroupStateChangeScNotify.cs b/GameServer/Server/Packet/Send/Scene/PacketGroupStateChangeScNotify.cs new file mode 100644 index 00000000..eed55770 --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketGroupStateChangeScNotify.cs @@ -0,0 +1,23 @@ +using EggLink.DanhengServer.Enums; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketGroupStateChangeScNotify : BasePacket + { + public PacketGroupStateChangeScNotify(int entryId, int groupId, PropStateEnum propState) : base(CmdIds.GroupStateChangeScNotify) + { + var notify = new GroupStateChangeScNotify() + { + GroupStateInfo = new GroupStateInfo() + { + EntryId = (uint)entryId, + GroupId = (uint)groupId, + GroupState = (uint)propState, + } + }; + + SetData(notify); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketInteractPropScRsp.cs b/GameServer/Server/Packet/Send/Scene/PacketInteractPropScRsp.cs new file mode 100644 index 00000000..a45cb774 --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketInteractPropScRsp.cs @@ -0,0 +1,20 @@ +using EggLink.DanhengServer.Game.Scene.Entity; +using EggLink.DanhengServer.Proto; + +namespace EggLink.DanhengServer.Server.Packet.Send.Scene +{ + public class PacketInteractPropScRsp : BasePacket + { + public PacketInteractPropScRsp(EntityProp? prop) : base(CmdIds.InteractPropScRsp) + { + var proto = new InteractPropScRsp(); + + if (prop != null) + { + proto.PropState = (uint)prop.State; + proto.PropEntityId = (uint)prop.EntityID; + } + SetData(proto); + } + } +} diff --git a/GameServer/Server/Packet/Send/Scene/PacketSceneEntityMoveScNotify.cs b/GameServer/Server/Packet/Send/Scene/PacketSceneEntityMoveScNotify.cs new file mode 100644 index 00000000..079d97d8 --- /dev/null +++ b/GameServer/Server/Packet/Send/Scene/PacketSceneEntityMoveScNotify.cs @@ -0,0 +1,28 @@ +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.Scene +{ + public class PacketSceneEntityMoveScNotify : BasePacket + { + public PacketSceneEntityMoveScNotify(PlayerInstance player) : base(CmdIds.SceneEntityMoveScNotify) + { + var proto = new SceneEntityMoveScNotify() + { + EntryId = (uint)player.Data.EntryId, + Motion = new MotionInfo() + { + Pos = player.Data.Pos!.ToProto(), + Rot = player.Data.Rot!.ToProto(), + }, + }; + + SetData(proto); + } + } +} diff --git a/README.md b/README.md index eac63c98..629a953a 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,6 @@

## Thanks -- Weedwacker - offers kcp processor +- Weedwacker - Provide a kcp implementation for C# +- [SqlSugar](https://github.com/donet5/SqlSugar) - Provide a ORM for C# +- [LunarCore](https://github.com/Melledy/LunarCore) - Some data structures and algorithms \ No newline at end of file diff --git a/WebServer/WebServer.csproj b/WebServer/WebServer.csproj index d84feb49..fb07be3e 100644 --- a/WebServer/WebServer.csproj +++ b/WebServer/WebServer.csproj @@ -12,6 +12,7 @@ +