mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-02 20:26:03 +08:00
implement basic scene and cocoon ( not drop )
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.48.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace EggLink.DanhengServer.Data.Config
|
||||
public bool Loaded = false;
|
||||
public Dictionary<int, GroupInfo> Groups = [];
|
||||
|
||||
private Dictionary<int, PropInfo> CachedTeleports = [];
|
||||
private List<PropInfo> UnlockedCheckpoints = []; // DEBUG
|
||||
public Dictionary<int, PropInfo> CachedTeleports = [];
|
||||
public List<PropInfo> 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))
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
27
Common/Data/Excel/CocoonConfigExcel.cs
Normal file
27
Common/Data/Excel/CocoonConfigExcel.cs
Normal file
@@ -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<int> StageIDList { get; set; } = [];
|
||||
public List<int> DropList { get; set; } = [];
|
||||
|
||||
public override int GetId()
|
||||
{
|
||||
return (ID * 100) + WorldLevel;
|
||||
}
|
||||
|
||||
public override void Loaded()
|
||||
{
|
||||
GameData.CocoonConfigData.Add(GetId(), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Common/Data/Excel/InteractConfigExcel.cs
Normal file
28
Common/Data/Excel/InteractConfigExcel.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Common/Data/Excel/MazePropExcel.cs
Normal file
53
Common/Data/Excel/MazePropExcel.cs
Normal file
@@ -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<PropStateEnum> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Common/Data/Excel/NPCDataExcel.cs
Normal file
24
Common/Data/Excel/NPCDataExcel.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Common/Data/Excel/NPCMonsterDataExcel.cs
Normal file
25
Common/Data/Excel/NPCMonsterDataExcel.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Common/Data/Excel/QuestDataExcel.cs
Normal file
26
Common/Data/Excel/QuestDataExcel.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<StageMonsterList> MonsterList { get; set; } = new List<StageMonsterList>();
|
||||
public List<StageMonsterList> 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
|
||||
|
||||
@@ -7,9 +7,15 @@ namespace EggLink.DanhengServer.Data
|
||||
public static class GameData
|
||||
{
|
||||
public static Dictionary<int, AvatarConfigExcel> AvatarConfigData { get; private set; } = [];
|
||||
public static Dictionary<int, CocoonConfigExcel> CocoonConfigData { get; private set; } = [];
|
||||
public static Dictionary<int, StageConfigExcel> StageConfigData { get; private set; } = [];
|
||||
public static Dictionary<int, MapEntranceExcel> MapEntranceData { get; private set; } = [];
|
||||
public static Dictionary<int, MazePlaneExcel> MazePlaneData { get; private set; } = [];
|
||||
public static Dictionary<int, MazePropExcel> MazePropData { get; private set; } = [];
|
||||
public static Dictionary<int, InteractConfigExcel> InteractConfigData { get; private set; } = [];
|
||||
public static Dictionary<int, NPCMonsterDataExcel> NpcMonsterDataData { get; private set; } = [];
|
||||
public static Dictionary<int, NPCDataExcel> NpcDataData { get; private set; } = [];
|
||||
public static Dictionary<int, QuestDataExcel> QuestDataData { get; private set; } = [];
|
||||
|
||||
public static Dictionary<string, FloorInfo> FloorInfoData { get; private set; } = [];
|
||||
|
||||
|
||||
@@ -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<JObject>(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
|
||||
|
||||
@@ -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<AvatarInfo>? Avatars { get; set; }
|
||||
|
||||
public List<int> 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<int, int> SkillTree { get; set; } = [];
|
||||
public int EquipId { get; set; } = 0;
|
||||
public Dictionary<int, int> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T>().Where(it => (it as BaseDatabaseData).Uid == uid).First();
|
||||
} catch
|
||||
lock (_lock)
|
||||
{
|
||||
return sqlSugarScope?.Queryable<T>().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<T>().ToList();
|
||||
} catch
|
||||
lock (_lock)
|
||||
{
|
||||
return sqlSugarScope?.Queryable<T>().ToList();
|
||||
}
|
||||
} catch(Exception e)
|
||||
{
|
||||
logger.Error("Unsupported type");
|
||||
logger.Error("Unsupported type", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveInstance<T>(T instance) where T : class, new()
|
||||
{
|
||||
sqlSugarScope?.Insertable(instance).ExecuteCommand();
|
||||
lock (_lock)
|
||||
{
|
||||
sqlSugarScope?.Insertable(instance).ExecuteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateInstance<T>(T instance) where T : class, new()
|
||||
{
|
||||
sqlSugarScope?.Updateable(instance).ExecuteCommand();
|
||||
lock (_lock)
|
||||
{
|
||||
sqlSugarScope?.Updateable(instance).ExecuteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteInstance<T>(T instance) where T : class, new()
|
||||
{
|
||||
sqlSugarScope?.Deleteable(instance).ExecuteCommand();
|
||||
lock (_lock)
|
||||
{
|
||||
sqlSugarScope?.Deleteable(instance).ExecuteCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<int, LineupInfo> 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<int>? BaseAvatars { get; set; }
|
||||
public int LeaderAvatarId { get; set; }
|
||||
public List<AvatarInfo>? 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<AvatarData>(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<int, LineupInfo>? Lineups { get; set; } = []; // 9 * 4
|
||||
public int BaseAvatarId { get; set; }
|
||||
public int AssistUid { get; set; }
|
||||
public int SpecialAvatarId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
35
Common/Enums/PropTypeEnum.cs
Normal file
35
Common/Enums/PropTypeEnum.cs
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<T> (this List<T> values)
|
||||
{
|
||||
var index = new Random().Next(values.Count);
|
||||
return values[index];
|
||||
}
|
||||
|
||||
public static string ToArrayString(this List<string> list)
|
||||
{
|
||||
return list.JoinFormat(", ", "");
|
||||
}
|
||||
public static string ToJsonString(this Dictionary<string, string> dic)
|
||||
{
|
||||
return JsonConvert.SerializeObject(dic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
GameServer/Command/Cmd/CommandGive.cs
Normal file
31
GameServer/Command/Cmd/CommandGive.cs
Normal file
@@ -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 <item> l<level> r<rank> p<promotion> x<amount>")]
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
73
GameServer/Command/CommandArg.cs
Normal file
73
GameServer/Command/CommandArg.cs
Normal file
@@ -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<string> BasicArgs { get; } = [];
|
||||
public Dictionary<string, string> 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()}.";
|
||||
}
|
||||
}
|
||||
}
|
||||
34
GameServer/Command/CommandInfo.cs
Normal file
34
GameServer/Command/CommandInfo.cs
Normal file
@@ -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<string> 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<string> Alias { get; } = [];
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class CommandMethod(List<CommandCondition> conditions) : Attribute
|
||||
{
|
||||
public List<CommandCondition> Conditions { get; } = conditions;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class CommandDefault : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
public class CommandCondition
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public string ShouldBe { get; set; } = "";
|
||||
}
|
||||
}
|
||||
122
GameServer/Command/CommandManager.cs
Normal file
122
GameServer/Command/CommandManager.cs
Normal file
@@ -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<string, ICommand> Commands { get; } = [];
|
||||
public Dictionary<string, CommandInfo> 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<CommandInfo>();
|
||||
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<string>("> ");
|
||||
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<CommandMethod>();
|
||||
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<CommandDefault>();
|
||||
if (attr != null)
|
||||
{
|
||||
method.Invoke(command, [arg]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info($"Command {cmd} not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
GameServer/Command/CommandSender.cs
Normal file
22
GameServer/Command/CommandSender.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
GameServer/Command/ICommand.cs
Normal file
12
GameServer/Command/ICommand.cs
Normal file
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
GameServer/Game/Battle/BattleInstance.cs
Normal file
56
GameServer/Game/Battle/BattleInstance.cs
Normal file
@@ -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<StageConfigExcel> 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<StageConfigExcel> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
152
GameServer/Game/Battle/BattleManager.cs
Normal file
152
GameServer/Game/Battle/BattleManager.cs
Normal file
@@ -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<StageConfigExcel> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<int, LineupInfo> 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<LineupInfoJson>(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)
|
||||
|
||||
@@ -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<PlayerUnlockData>(Uid);
|
||||
if (unlock == null)
|
||||
@@ -83,7 +101,11 @@ namespace EggLink.DanhengServer.Game.Player
|
||||
unlock = DatabaseHelper.Instance?.GetInstance<PlayerUnlockData>(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<EntityProp>(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
|
||||
|
||||
45
GameServer/Game/Scene/Entity/EntityMonster.cs
Normal file
45
GameServer/Game/Scene/Entity/EntityMonster.cs
Normal file
@@ -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,
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
GameServer/Game/Scene/Entity/EntityNpc.cs
Normal file
62
GameServer/Game/Scene/Entity/EntityNpc.cs
Normal file
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
49
GameServer/Game/Scene/Entity/EntityProp.cs
Normal file
49
GameServer/Game/Scene/Entity/EntityProp.cs
Normal file
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
20
GameServer/Game/Scene/Entity/IGameEntity.cs
Normal file
20
GameServer/Game/Scene/Entity/IGameEntity.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
128
GameServer/Game/Scene/SceneEntityLoader.cs
Normal file
128
GameServer/Game/Scene/SceneEntityLoader.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<int, AvatarSceneInfo> AvatarInfo = [];
|
||||
public int LeaderAvatarId;
|
||||
public Dictionary<int, IGameEntity> Entities = [];
|
||||
public List<EntityProp> 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<AvatarData>(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<T> GetEntitiesInGroup<T>(int groupID)
|
||||
{
|
||||
List<T> 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<SceneEntityGroupInfo> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,23 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Server\Packet\Recv\Rogue\" />
|
||||
<Folder Include="Server\Packet\Recv\Achievement\" />
|
||||
<Folder Include="Server\Packet\Recv\ChessRogue\" />
|
||||
<Folder Include="Server\Packet\Recv\Tutorial\" />
|
||||
<Folder Include="Server\Packet\Recv\Quest\" />
|
||||
<Folder Include="Server\Packet\Recv\Mail\" />
|
||||
<Folder Include="Server\Packet\Recv\Friend\" />
|
||||
<Folder Include="Server\Packet\Recv\Battle\" />
|
||||
<Folder Include="Server\Packet\Recv\Others\" />
|
||||
<Folder Include="Server\Packet\Send\Achievement\" />
|
||||
<Folder Include="Server\Packet\Send\Battle\" />
|
||||
<Folder Include="Server\Packet\Send\ChessRogue\" />
|
||||
<Folder Include="Server\Packet\Send\Friend\" />
|
||||
<Folder Include="Server\Packet\Send\Mail\" />
|
||||
<Folder Include="Server\Packet\Send\Others\" />
|
||||
<Folder Include="Server\Packet\Send\Quest\" />
|
||||
<Folder Include="Server\Packet\Send\Rogue\" />
|
||||
<Folder Include="Server\Packet\Send\Tutorial\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Spectre.Console" Version="0.48.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
|
||||
|
||||
@@ -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<JObject>(File.ReadAllText("LogMap.json"))!.Properties().ToList().ForEach(x => Connection.LogMap.Add(x.Name, x.Value.ToString()));
|
||||
#endif
|
||||
CommandManager.Start();
|
||||
}
|
||||
|
||||
public static ConfigContainer GetConfig()
|
||||
|
||||
@@ -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<int> BANNED_PACKETS = [];
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
#if DEBUG
|
||||
private static readonly Dictionary<string, string> LogMap = [];
|
||||
public static readonly Dictionary<string, string> 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<JObject>(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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace EggLink.DanhengServer.Server
|
||||
private static UdpClient? UDPClient;
|
||||
private static IPEndPoint? ListenAddress;
|
||||
private static IKcpTransport<IKcpMultiplexConnection>? KCPTransport;
|
||||
private static readonly CancellationTokenSource CancelToken = new();
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
private static IKcpMultiplexConnection? Multiplex => KCPTransport?.Connection;
|
||||
public static readonly SortedList<long, Connection> Connections = [];
|
||||
|
||||
@@ -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!));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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!));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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!));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<AccountData>(long.Parse(req.AccountUid));
|
||||
if (account == null)
|
||||
{
|
||||
connection.SendPacket(new PacketPlayerGetTokenScRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
connection.State = SessionState.WAITING_FOR_LOGIN;
|
||||
var pd = DatabaseHelper.Instance?.GetInstance<PlayerData>(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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,6 @@
|
||||
</p>
|
||||
|
||||
## 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
|
||||
@@ -12,6 +12,7 @@
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.3" />
|
||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.25.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.48.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
|
||||
|
||||
Reference in New Issue
Block a user