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 @@
+