mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-02 12:16:03 +08:00
Feature: Move Proto to Proto project, Move Kcp & Connection to DanhengKcpSharp project
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\GameServer\GameServer.csproj" />
|
||||
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Util;
|
||||
|
||||
namespace EggLink.DanhengServer.Command.Command;
|
||||
@@ -38,10 +39,9 @@ public class CommandArg
|
||||
|
||||
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;
|
||||
}
|
||||
if (DanhengListener.Connections.Values.ToList()
|
||||
.Find(item => (item as Connection)?.Player?.Uid.ToString() == target) is Connection connection)
|
||||
Target = connection;
|
||||
}
|
||||
|
||||
public string Raw { get; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Reflection;
|
||||
using EggLink.DanhengServer.GameServer.Server;
|
||||
using EggLink.DanhengServer.Internationalization;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using Spectre.Console;
|
||||
|
||||
@@ -8,16 +9,16 @@ namespace EggLink.DanhengServer.Command.Command;
|
||||
|
||||
public class CommandManager
|
||||
{
|
||||
private const int MaxCommandHistory = 100;
|
||||
|
||||
private readonly List<string> _commandHistory = [];
|
||||
private int _historyIndex = -1;
|
||||
public static CommandManager? Instance { get; private set; }
|
||||
public Dictionary<string, ICommand> Commands { get; } = [];
|
||||
public Dictionary<string, CommandInfo> CommandInfo { get; } = [];
|
||||
public Logger Logger { get; } = new("CommandManager");
|
||||
public Connection? Target { get; set; }
|
||||
|
||||
private List<string> commandHistory = new();
|
||||
private int historyIndex = -1;
|
||||
private const int MaxCommandHistory = 100;
|
||||
|
||||
public void RegisterCommand()
|
||||
{
|
||||
Instance = this;
|
||||
@@ -48,18 +49,12 @@ public class CommandManager
|
||||
|
||||
if (string.IsNullOrEmpty(input)) continue;
|
||||
|
||||
if (input.StartsWith("/"))
|
||||
{
|
||||
input = input.Substring(1);
|
||||
}
|
||||
if (input.StartsWith("/")) input = input.Substring(1);
|
||||
|
||||
if (commandHistory.Count >= MaxCommandHistory)
|
||||
{
|
||||
commandHistory.RemoveAt(0);
|
||||
}
|
||||
if (_commandHistory.Count >= MaxCommandHistory) _commandHistory.RemoveAt(0);
|
||||
|
||||
if (commandHistory.Count == 0 || commandHistory.Last() != input) commandHistory.Add(input);
|
||||
historyIndex = commandHistory.Count;
|
||||
if (_commandHistory.Count == 0 || _commandHistory.Last() != input) _commandHistory.Add(input);
|
||||
_historyIndex = _commandHistory.Count;
|
||||
HandleCommand(input, new ConsoleCommandSender(Logger));
|
||||
}
|
||||
catch
|
||||
@@ -94,26 +89,26 @@ public class CommandManager
|
||||
}
|
||||
else if (keyInfo.Key == ConsoleKey.UpArrow)
|
||||
{
|
||||
if (historyIndex > 0)
|
||||
if (_historyIndex > 0)
|
||||
{
|
||||
historyIndex--;
|
||||
ReplaceInput(input, commandHistory[historyIndex]);
|
||||
_historyIndex--;
|
||||
ReplaceInput(input, _commandHistory[_historyIndex]);
|
||||
}
|
||||
}
|
||||
else if (keyInfo.Key == ConsoleKey.DownArrow)
|
||||
{
|
||||
if (historyIndex < commandHistory.Count - 1)
|
||||
if (_historyIndex < _commandHistory.Count - 1)
|
||||
{
|
||||
historyIndex++;
|
||||
ReplaceInput(input, commandHistory[historyIndex]);
|
||||
_historyIndex++;
|
||||
ReplaceInput(input, _commandHistory[_historyIndex]);
|
||||
}
|
||||
else if (historyIndex == commandHistory.Count - 1)
|
||||
else if (_historyIndex == _commandHistory.Count - 1)
|
||||
{
|
||||
historyIndex++;
|
||||
_historyIndex++;
|
||||
ReplaceInput(input, string.Empty);
|
||||
}
|
||||
}
|
||||
else // known issue: Ctrl + (Any Key but C) or other control key will cause display error
|
||||
else // known issue: Ctrl + (Any Key but C) or other control key will cause display error
|
||||
{
|
||||
input.Add(keyInfo.KeyChar);
|
||||
Console.Write(keyInfo.KeyChar);
|
||||
@@ -147,8 +142,8 @@ public class CommandManager
|
||||
if (cmd.StartsWith('@'))
|
||||
{
|
||||
var target = cmd[1..];
|
||||
var con = Listener.Connections.Values.ToList().Find(item => item.Player?.Uid.ToString() == target);
|
||||
if (con != null)
|
||||
if (DanhengListener.Connections.Values.ToList()
|
||||
.Find(item => (item as Connection)?.Player?.Uid.ToString() == target) is Connection con)
|
||||
{
|
||||
Target = con;
|
||||
sender.SendMsg(I18nManager.Translate("Game.Command.Notice.TargetFound", target,
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.3" />
|
||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.25.3" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.27.3" />
|
||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.27.3" />
|
||||
<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" />
|
||||
@@ -21,4 +21,8 @@
|
||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -179,7 +179,8 @@ public class NoticeTextCHS
|
||||
/// </summary>
|
||||
public class HeroTextCHS
|
||||
{
|
||||
public string Desc => "切换主角的性别/形态\n当切换性别时,genderId为1代表男性,2代表女性\n当切换形态时,8001代表毁灭命途,8003代表存护命途,8005代表同谐命途。\n注意,切换性别时会清空所有可选命途以及行迹,为不可逆操作!";
|
||||
public string Desc =>
|
||||
"切换主角的性别/形态\n当切换性别时,genderId为1代表男性,2代表女性\n当切换形态时,8001代表毁灭命途,8003代表存护命途,8005代表同谐命途。\n注意,切换性别时会清空所有可选命途以及行迹,为不可逆操作!";
|
||||
|
||||
public string Usage => "用法:/hero gender [genderId]\n\n用法:/hero type [typeId]";
|
||||
public string GenderNotSpecified => "性别不存在!";
|
||||
@@ -208,7 +209,8 @@ public class AvatarTextCHS
|
||||
{
|
||||
public string Desc => "设定玩家已有角色的属性\n设置行迹等级时,设置X级即设置所有行迹节点至X级,若大于此节点允许的最高等级,设置为最高等级\n注意:-1意为所有已拥有角色";
|
||||
|
||||
public string Usage => "用法:/avatar talent [角色ID/-1] [行迹等级]\n\n用法:/avatar get [角色ID]\n\n用法:/avatar rank [角色ID/-1] [星魂]\n\n用法:/avatar level [角色ID/-1] [角色等级]";
|
||||
public string Usage =>
|
||||
"用法:/avatar talent [角色ID/-1] [行迹等级]\n\n用法:/avatar get [角色ID]\n\n用法:/avatar rank [角色ID/-1] [星魂]\n\n用法:/avatar level [角色ID/-1] [角色等级]";
|
||||
|
||||
public string InvalidLevel => "{0}等级无效";
|
||||
public string AllAvatarsLevelSet => "已将全部角色 {0}等级设置为 {1}";
|
||||
@@ -236,7 +238,8 @@ public class GiveAllTextCHS
|
||||
{
|
||||
public string Desc => "给予玩家全部指定类型的物品\navatar意为角色,equipment意为光锥,relic意为遗器,unlock意为气泡、手机壁纸、头像";
|
||||
|
||||
public string Usage => "用法:/giveall avatar r<星魂> l<等级>\n\n用法:/giveall equipment r<叠影> l<等级> x<数量>\n\n用法:/giveall relic l<等级> x<数量>\n\n用法:/giveall unlock";
|
||||
public string Usage =>
|
||||
"用法:/giveall avatar r<星魂> l<等级>\n\n用法:/giveall equipment r<叠影> l<等级> x<数量>\n\n用法:/giveall relic l<等级> x<数量>\n\n用法:/giveall unlock";
|
||||
|
||||
public string GiveAllItems => "已给予所有 {0}, 各 {1} 个";
|
||||
}
|
||||
@@ -284,7 +287,8 @@ public class MissionTextCHS
|
||||
"使用 running 获取正在进行的任务以及可能卡住的任务,使用后可能会出现较长任务列表,请注意甄别\n" +
|
||||
"使用 reaccept 可重新进行指定主任务,请浏览 handbook 来获取主任务ID";
|
||||
|
||||
public string Usage => "用法:/mission pass\n\n用法:/mission finish [子任务ID]\n\n用法:/mission running\n\n用法:/mission reaccept [主任务ID]";
|
||||
public string Usage =>
|
||||
"用法:/mission pass\n\n用法:/mission finish [子任务ID]\n\n用法:/mission running\n\n用法:/mission reaccept [主任务ID]";
|
||||
|
||||
public string AllMissionsFinished => "所有任务已完成!";
|
||||
public string AllRunningMissionsFinished => "共 {0} 个进行中的任务已完成!";
|
||||
@@ -306,7 +310,8 @@ public class RelicTextCHS
|
||||
{
|
||||
public string Desc => "管理玩家的遗器\n主词条可选,副词条可选,但至少存在其中之一\n等级限制:1≤等级≤9999";
|
||||
|
||||
public string Usage => "用法:/relic <遗器ID> <主词条ID> <小词条ID1:小词条等级> <小词条ID2:小词条等级> <小词条ID3:小词条等级> <小词条ID4:小词条等级> l<等级> x<数量>";
|
||||
public string Usage =>
|
||||
"用法:/relic <遗器ID> <主词条ID> <小词条ID1:小词条等级> <小词条ID2:小词条等级> <小词条ID3:小词条等级> <小词条ID4:小词条等级> l<等级> x<数量>";
|
||||
|
||||
public string RelicNotFound => "遗器不存在!";
|
||||
public string InvalidMainAffixId => "主词条ID无效";
|
||||
@@ -331,7 +336,8 @@ public class RogueTextCHS
|
||||
{
|
||||
public string Desc => "管理玩家模拟宇宙中的数据\n-1意为所有祝福(已拥有祝福)\n使用 buff 来获取祝福\n使用 enhance 来强化祝福";
|
||||
|
||||
public string Usage => "用法:/rogue money [宇宙碎片数量]\n\n用法:/rogue buff [祝福ID/-1]\n\n用法:/rogue miracle [奇物ID]\n\n用法:/rogue enhance [祝福ID/-1]\n\n用法:/rogue unstuck - 脱离事件";
|
||||
public string Usage =>
|
||||
"用法:/rogue money [宇宙碎片数量]\n\n用法:/rogue buff [祝福ID/-1]\n\n用法:/rogue miracle [奇物ID]\n\n用法:/rogue enhance [祝福ID/-1]\n\n用法:/rogue unstuck - 脱离事件";
|
||||
|
||||
public string PlayerGainedMoney => "玩家已获得 {0} 宇宙碎片";
|
||||
public string PlayerGainedAllItems => "玩家已获得所有{0}";
|
||||
@@ -357,7 +363,8 @@ public class SceneTextCHS
|
||||
"使用 reload 来重新加载当前场景,并回到初始位置\n" +
|
||||
"使用 reset 来重置指定场景所有道具状态,要获取当前FloorId,请访问数据库 Player 表";
|
||||
|
||||
public string Usage => "用法:/scene prop [组ID] [道具ID] [状态]\n\n用法:/scene remove [实体ID]\n\n用法:/scene unlockall\n\n用法:/scene change [entryId]\n\n用法:/scene reload\n\n用法:/scene reset <floorId>";
|
||||
public string Usage =>
|
||||
"用法:/scene prop [组ID] [道具ID] [状态]\n\n用法:/scene remove [实体ID]\n\n用法:/scene unlockall\n\n用法:/scene change [entryId]\n\n用法:/scene reload\n\n用法:/scene reset <floorId>";
|
||||
|
||||
public string LoadedGroups => "已加载组: {0}";
|
||||
public string PropStateChanged => "道具: {0} 的状态已设置为 {1}";
|
||||
|
||||
@@ -179,7 +179,8 @@ public class NoticeTextCHT
|
||||
/// </summary>
|
||||
public class HeroTextCHT
|
||||
{
|
||||
public string Desc => "切換主角的性別/形態\n當切換性別時,genderId為1代表男性,2代表女性\n當切換形態時,8001代表毀滅命途,8003代表存護命途,8005代表同諧命途。\n注意,切換性別時會清空所有可選命途以及行跡,為不可逆操作!";
|
||||
public string Desc =>
|
||||
"切換主角的性別/形態\n當切換性別時,genderId為1代表男性,2代表女性\n當切換形態時,8001代表毀滅命途,8003代表存護命途,8005代表同諧命途。\n注意,切換性別時會清空所有可選命途以及行跡,為不可逆操作!";
|
||||
|
||||
public string Usage => "用法:/hero gender [genderId]\n\n用法:/hero type [typeId]";
|
||||
public string GenderNotSpecified => "性別不存在!";
|
||||
@@ -208,7 +209,8 @@ public class AvatarTextCHT
|
||||
{
|
||||
public string Desc => "設定玩家已有角色的屬性\n設置行跡等級時,設置X級即設置所有行跡節點至X級,若大於此節點允許的最高等級,設置為最高等級\n注意:-1意為所有已擁有角色";
|
||||
|
||||
public string Usage => "用法:/avatar talent [角色ID/-1] [行跡等級]\n\n用法:/avatar get [角色ID]\n\n用法:/avatar rank [角色ID/-1] [星魂]\n\n用法:/avatar level [角色ID/-1] [角色等級]";
|
||||
public string Usage =>
|
||||
"用法:/avatar talent [角色ID/-1] [行跡等級]\n\n用法:/avatar get [角色ID]\n\n用法:/avatar rank [角色ID/-1] [星魂]\n\n用法:/avatar level [角色ID/-1] [角色等級]";
|
||||
|
||||
public string InvalidLevel => "{0}等級無效";
|
||||
public string AllAvatarsLevelSet => "已將全部角色 {0}等級設置為 {1}";
|
||||
@@ -236,7 +238,8 @@ public class GiveAllTextCHT
|
||||
{
|
||||
public string Desc => "給予玩家全部指定類型的物品\navatar意為角色,equipment意為光錐,relic意為遺器,unlock意為氣泡、手機壁紙、頭像";
|
||||
|
||||
public string Usage => "用法:/giveall avatar r<星魂> l<等級>\n\n用法:/giveall equipment r<疊影> l<等級> x<數量>\n\n用法:/giveall relic l<等級> x<數量>\n\n用法:/giveall unlock";
|
||||
public string Usage =>
|
||||
"用法:/giveall avatar r<星魂> l<等級>\n\n用法:/giveall equipment r<疊影> l<等級> x<數量>\n\n用法:/giveall relic l<等級> x<數量>\n\n用法:/giveall unlock";
|
||||
|
||||
public string GiveAllItems => "已給予所有 {0}, 各 {1} 個";
|
||||
}
|
||||
@@ -284,7 +287,8 @@ public class MissionTextCHT
|
||||
"使用 running 獲取正在進行的任務以及可能卡住的任務,使用後可能會出現較長任務列表,請注意甄別\n" +
|
||||
"使用 reaccept 可重新進行指定主任務,請瀏覽 handbook 來獲取主任務ID";
|
||||
|
||||
public string Usage => "用法:/mission pass\n\n用法:/mission finish [子任務ID]\n\n用法:/mission running\n\n用法:/mission reaccept [主任務ID]";
|
||||
public string Usage =>
|
||||
"用法:/mission pass\n\n用法:/mission finish [子任務ID]\n\n用法:/mission running\n\n用法:/mission reaccept [主任務ID]";
|
||||
|
||||
public string AllMissionsFinished => "所有任務已完成!";
|
||||
public string AllRunningMissionsFinished => "共 {0} 個進行中的任務已完成!";
|
||||
@@ -306,7 +310,8 @@ public class RelicTextCHT
|
||||
{
|
||||
public string Desc => "管理玩家的遺器\n主詞條可選,副詞條可選,但至少存在其中之一\n等級限制:1≤等級≤9999";
|
||||
|
||||
public string Usage => "用法:/relic <遺器ID> <主詞條ID> <小詞條ID1:小詞條等級> <小詞條ID2:小詞條等級> <小詞條ID3:小詞條等級> <小詞條ID4:小詞條等級> l<等級> x<數量>";
|
||||
public string Usage =>
|
||||
"用法:/relic <遺器ID> <主詞條ID> <小詞條ID1:小詞條等級> <小詞條ID2:小詞條等級> <小詞條ID3:小詞條等級> <小詞條ID4:小詞條等級> l<等級> x<數量>";
|
||||
|
||||
public string RelicNotFound => "遺器不存在!";
|
||||
public string InvalidMainAffixId => "主詞條ID無效";
|
||||
@@ -331,7 +336,8 @@ public class RogueTextCHT
|
||||
{
|
||||
public string Desc => "管理玩家模擬宇宙中的數據\n-1意為所有祝福(已擁有祝福)\n使用 buff 來獲取祝福\n使用 enhance 來強化祝福";
|
||||
|
||||
public string Usage => "用法:/rogue money [宇宙碎片數量]\n\n用法:/rogue buff [祝福ID/-1]\n\n用法:/rogue miracle [奇物ID]\n\n用法:/rogue enhance [祝福ID/-1]\n\n用法:/rogue unstuck - 脫離事件";
|
||||
public string Usage =>
|
||||
"用法:/rogue money [宇宙碎片數量]\n\n用法:/rogue buff [祝福ID/-1]\n\n用法:/rogue miracle [奇物ID]\n\n用法:/rogue enhance [祝福ID/-1]\n\n用法:/rogue unstuck - 脫離事件";
|
||||
|
||||
public string PlayerGainedMoney => "玩家已獲得 {0} 宇宙碎片";
|
||||
public string PlayerGainedAllItems => "玩家已獲得所有{0}";
|
||||
@@ -357,7 +363,8 @@ public class SceneTextCHT
|
||||
"使用 reload 來重新加載當前場景,並回到初始位置\n" +
|
||||
"使用 reset 來重置指定場景所有道具狀態,要獲取當前FloorId,請訪問數據庫 Player 表";
|
||||
|
||||
public string Usage => "用法:/scene prop [組ID] [道具ID] [狀態]\n\n用法:/scene remove [實體ID]\n\n用法:/scene unlockall\n\n用法:/scene change [entryId]\n\n用法:/scene reload\n\n用法:/scene reset <floorId>";
|
||||
public string Usage =>
|
||||
"用法:/scene prop [組ID] [道具ID] [狀態]\n\n用法:/scene remove [實體ID]\n\n用法:/scene unlockall\n\n用法:/scene change [entryId]\n\n用法:/scene reload\n\n用法:/scene reset <floorId>";
|
||||
|
||||
public string LoadedGroups => "已加載組: {0}";
|
||||
public string PropStateChanged => "道具: {0} 的狀態已設置為 {1}";
|
||||
|
||||
@@ -143,8 +143,12 @@ public class ServerInfoTextEN
|
||||
public string LoadedItem => "Loaded {0}.";
|
||||
public string LoadedItems => "Loaded {0} {1}(s).";
|
||||
public string ServerRunning => "{0} server listening on {1}";
|
||||
public string ServerStarted => "Startup complete! Took {0}s, better than 99% of users. Type 'help' for command help"; // This is a meme, consider localizing in English
|
||||
public string MissionEnabled => "Mission system enabled. This feature is still in development and may not work as expected. Please report any bugs to the developers.";
|
||||
|
||||
public string ServerStarted =>
|
||||
"Startup complete! Took {0}s, better than 99% of users. Type 'help' for command help"; // This is a meme, consider localizing in English
|
||||
|
||||
public string MissionEnabled =>
|
||||
"Mission system enabled. This feature is still in development and may not work as expected. Please report any bugs to the developers.";
|
||||
|
||||
public string ConfigMissing => "{0} is missing. Please check your resource folder: {1}, {2} may not be available.";
|
||||
public string UnloadedItems => "Unloaded all {0}.";
|
||||
@@ -179,7 +183,8 @@ public class NoticeTextEN
|
||||
/// </summary>
|
||||
public class HeroTextEN
|
||||
{
|
||||
public string Desc => "Switch the gender/type of the main character\nWhen switch the gender, 1 means male, 2 means female\nWhen switch the type(path), 8001 means Destruction, 8003 means Preservation, 8005 means Harmony.\nNotice: Switch gender will clear all the paths and talents of main character, this operation is irreversible!";
|
||||
public string Desc =>
|
||||
"Switch the gender/type of the main character\nWhen switch the gender, 1 means male, 2 means female\nWhen switch the type(path), 8001 means Destruction, 8003 means Preservation, 8005 means Harmony.\nNotice: Switch gender will clear all the paths and talents of main character, this operation is irreversible!";
|
||||
|
||||
public string Usage => "Usage: /hero gender [genderId]\n\nUsage: /hero type [typeId]";
|
||||
|
||||
@@ -197,6 +202,7 @@ public class UnlockAllTextEN
|
||||
public string Desc =>
|
||||
"Unlock the objects in given category\n" +
|
||||
"Use '/unlockall mission' to finish all missions, and the target player will be kicked, after re-login, the player may be stuck in tutorial, please use with caution";
|
||||
|
||||
public string Usage => "Usage: /unlockall mission";
|
||||
public string AllMissionsUnlocked => "All missions have been unlocked!";
|
||||
}
|
||||
@@ -206,9 +212,11 @@ public class UnlockAllTextEN
|
||||
/// </summary>
|
||||
public class AvatarTextEN
|
||||
{
|
||||
public string Desc => "Set the properties of the avatars player owned\nWhen set talent level, set to X level means set all talent point to X level, if greater than the point max level, set to max level\nNotice: -1 means all owned avatars";
|
||||
public string Desc =>
|
||||
"Set the properties of the avatars player owned\nWhen set talent level, set to X level means set all talent point to X level, if greater than the point max level, set to max level\nNotice: -1 means all owned avatars";
|
||||
|
||||
public string Usage => "Usage: /avatar talent [Avatar ID/-1] [Talent Level]\n\nUsage: /avatar get [Avatar ID]\n\nUsage: /avatar rank [Avatar ID/-1] [Rank]\n\nUsage: /avatar level [Avatar ID/-1] [Avatar Level]";
|
||||
public string Usage =>
|
||||
"Usage: /avatar talent [Avatar ID/-1] [Talent Level]\n\nUsage: /avatar get [Avatar ID]\n\nUsage: /avatar rank [Avatar ID/-1] [Rank]\n\nUsage: /avatar level [Avatar ID/-1] [Avatar Level]";
|
||||
|
||||
public string InvalidLevel => "Invalid {0} level";
|
||||
public string AllAvatarsLevelSet => "Set all characters' {0} level to {1}";
|
||||
@@ -234,9 +242,11 @@ public class GiveTextEN
|
||||
/// </summary>
|
||||
public class GiveAllTextEN
|
||||
{
|
||||
public string Desc => "Give the player all specified types of items\navatar means characters, equipment means light cones, relic means relic(artifact), unlock means chatBubbles, avatar(head icon), wallpaper";
|
||||
public string Desc =>
|
||||
"Give the player all specified types of items\navatar means characters, equipment means light cones, relic means relic(artifact), unlock means chatBubbles, avatar(head icon), wallpaper";
|
||||
|
||||
public string Usage => "Usage: /giveall avatar r<rank> l<level>\n\nUsage: /giveall equipment r<rank> l<level> x<amount>\n\nUsage: /giveall relic l<level> x<amount>\n\nUsage: /giveall unlock";
|
||||
public string Usage =>
|
||||
"Usage: /giveall avatar r<rank> l<level>\n\nUsage: /giveall equipment r<rank> l<level> x<amount>\n\nUsage: /giveall relic l<level> x<amount>\n\nUsage: /giveall unlock";
|
||||
|
||||
public string GiveAllItems => "Gave all {0}, each {1} items";
|
||||
}
|
||||
@@ -285,7 +295,8 @@ public class MissionTextEN
|
||||
"Use 'running' to get the running mission and possible stuck missions, after use, a longer mission list may appear, please note that\n" +
|
||||
"Use 'reaccept' to re-accept given main mission, please find main mission id in handbook";
|
||||
|
||||
public string Usage => "Usage: /mission pass\n\nUsage: /mission finish [Sub mission ID]\n\nUsage: /mission running\n\nUsage: /mission reaccept [main mission id]";
|
||||
public string Usage =>
|
||||
"Usage: /mission pass\n\nUsage: /mission finish [Sub mission ID]\n\nUsage: /mission running\n\nUsage: /mission reaccept [main mission id]";
|
||||
|
||||
public string AllMissionsFinished => "All tasks have been completed!";
|
||||
public string AllRunningMissionsFinished => "A total of {0} ongoing tasks have been completed!";
|
||||
@@ -305,9 +316,11 @@ public class MissionTextEN
|
||||
/// </summary>
|
||||
public class RelicTextEN
|
||||
{
|
||||
public string Desc => "Manage player's relics\nmain affix optional, sub affix optional, but at least one of them exists\nLevel limit: 1≤Level≤9999";
|
||||
public string Desc =>
|
||||
"Manage player's relics\nmain affix optional, sub affix optional, but at least one of them exists\nLevel limit: 1≤Level≤9999";
|
||||
|
||||
public string Usage => "Usage: /relic <relic ID> <main affix ID> <sub affix ID1:sub affix level> <sub affix ID2:sub affix level> <sub affix ID3:sub affix level> <sub affix ID4:sub affix level> l<level> x<amount>";
|
||||
public string Usage =>
|
||||
"Usage: /relic <relic ID> <main affix ID> <sub affix ID1:sub affix level> <sub affix ID2:sub affix level> <sub affix ID3:sub affix level> <sub affix ID4:sub affix level> l<level> x<amount>";
|
||||
|
||||
public string RelicNotFound => "Relic does not exist!";
|
||||
public string InvalidMainAffixId => "Invalid main affix ID";
|
||||
@@ -330,9 +343,11 @@ public class ReloadTextEN
|
||||
/// </summary>
|
||||
public class RogueTextEN
|
||||
{
|
||||
public string Desc => "Manage player's data in the simulated universe\n-1 means all blessings (all owned blessings)\nUse 'buff' to get blessings\nUse 'enhance' to enhance blessings";
|
||||
public string Desc =>
|
||||
"Manage player's data in the simulated universe\n-1 means all blessings (all owned blessings)\nUse 'buff' to get blessings\nUse 'enhance' to enhance blessings";
|
||||
|
||||
public string Usage => "Usage: /rogue money [Universe Debris Amount]\n\nUsage: /rogue buff [Blessing Id/-1]\n\nUsage: /rogue miracle [Miracle ID]\n\nUsage: /rogue enhance [Blessing ID/-1]\n\nUsage: /rogue unstuck - Leave event";
|
||||
public string Usage =>
|
||||
"Usage: /rogue money [Universe Debris Amount]\n\nUsage: /rogue buff [Blessing Id/-1]\n\nUsage: /rogue miracle [Miracle ID]\n\nUsage: /rogue enhance [Blessing ID/-1]\n\nUsage: /rogue unstuck - Leave event";
|
||||
|
||||
public string PlayerGainedMoney => "Player gained {0} universe debris";
|
||||
public string PlayerGainedAllItems => "Player gained all {0}";
|
||||
@@ -358,7 +373,8 @@ public class SceneTextEN
|
||||
"Use 'reload' to reload the current scene and return to the initial position.\n" +
|
||||
"Use 'reset' to reset the state of all props in the specified scene. For the current FloorId, refer to the Player table in the database.";
|
||||
|
||||
public string Usage => "Usage: /scene prop [groupId] [propId] [state]\n\nUsage: /scene remove [entityId]\n\nUsage: /scene unlockall\n\nUsage: /scene change [entryId]\n\nUsage: /scene reload\n\nUsage: /scene reset <floorId>";
|
||||
public string Usage =>
|
||||
"Usage: /scene prop [groupId] [propId] [state]\n\nUsage: /scene remove [entityId]\n\nUsage: /scene unlockall\n\nUsage: /scene change [entryId]\n\nUsage: /scene reload\n\nUsage: /scene reset <floorId>";
|
||||
|
||||
public string LoadedGroups => "Loaded groups: {0}";
|
||||
public string PropStateChanged => "Prop: {0} state set to {1}";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Spectre.Console;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace EggLink.DanhengServer.Util;
|
||||
|
||||
@@ -14,8 +14,8 @@ public class Logger(string moduleName)
|
||||
lock (_lock)
|
||||
{
|
||||
AnsiConsole.Write(new Markup($"[[[bold deepskyblue3_1]{DateTime.Now:HH:mm:ss}[/]]] " +
|
||||
$"[[[gray]{ModuleName}[/]]] [[[{((ConsoleColor)level)}]{level}[/]]] {message.Replace("[", "[[").Replace("]", "]]")}\n"));
|
||||
|
||||
$"[[[gray]{ModuleName}[/]]] [[[{(ConsoleColor)level}]{level}[/]]] {message.Replace("[", "[[").Replace("]", "]]")}\n"));
|
||||
|
||||
var logMessage = $"[{DateTime.Now:HH:mm:ss}] [{ModuleName}] [{level}] {message}";
|
||||
PluginEventCommon.InvokeOnConsoleLog(logMessage);
|
||||
WriteToFile(logMessage);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using EggLink.DanhengServer.Util;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet;
|
||||
namespace EggLink.DanhengServer.Kcp;
|
||||
|
||||
public class BasePacket(ushort cmdId)
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet;
|
||||
namespace EggLink.DanhengServer.Kcp;
|
||||
|
||||
public class CmdIds
|
||||
{
|
||||
165
DanhengKcpSharp/DanhengConnection.cs
Normal file
165
DanhengKcpSharp/DanhengConnection.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using EggLink.DanhengServer.Enums;
|
||||
using EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
|
||||
namespace EggLink.DanhengServer.Kcp;
|
||||
|
||||
public class DanhengConnection
|
||||
{
|
||||
public const int MAX_MSG_SIZE = 16384;
|
||||
public const int HANDSHAKE_SIZE = 20;
|
||||
public static readonly List<int> BannedPackets = [];
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
public static readonly Dictionary<string, string> LogMap = [];
|
||||
|
||||
public static readonly List<int> IgnoreLog =
|
||||
[
|
||||
CmdIds.PlayerHeartBeatCsReq, CmdIds.PlayerHeartBeatScRsp, CmdIds.SceneEntityMoveCsReq,
|
||||
CmdIds.SceneEntityMoveScRsp, CmdIds.GetShopListCsReq, CmdIds.GetShopListScRsp
|
||||
];
|
||||
|
||||
protected readonly CancellationTokenSource CancelToken;
|
||||
protected readonly KcpConversation Conversation;
|
||||
public readonly IPEndPoint RemoteEndPoint;
|
||||
|
||||
public string DebugFile = "";
|
||||
public bool IsOnline = true;
|
||||
public StreamWriter? Writer;
|
||||
|
||||
public DanhengConnection(KcpConversation conversation, IPEndPoint remote)
|
||||
{
|
||||
Conversation = conversation;
|
||||
RemoteEndPoint = remote;
|
||||
CancelToken = new CancellationTokenSource();
|
||||
Start();
|
||||
}
|
||||
|
||||
public long? ConversationId => Conversation.ConversationId;
|
||||
|
||||
public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE;
|
||||
//public PlayerInstance? Player { get; set; }
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
Logger.Info($"New connection from {RemoteEndPoint}.");
|
||||
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
||||
}
|
||||
|
||||
public virtual void Stop()
|
||||
{
|
||||
//Player?.OnLogoutAsync();
|
||||
//Listener.UnregisterConnection(this);
|
||||
Conversation.Dispose();
|
||||
try
|
||||
{
|
||||
CancelToken.Cancel();
|
||||
CancelToken.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
IsOnline = false;
|
||||
}
|
||||
|
||||
public void LogPacket(string sendOrRecv, ushort opcode, byte[] payload)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
|
||||
if (IgnoreLog.Contains(opcode)) return;
|
||||
var typ = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SingleOrDefault(assembly => assembly.GetName().Name == "DanhengProto")!.GetTypes()
|
||||
.First(t => t.Name == $"{LogMap[opcode.ToString()]}"); //get the type using the packet name
|
||||
var descriptor =
|
||||
typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)?.GetValue(
|
||||
null, null) as MessageDescriptor; // get the static property Descriptor
|
||||
var packet = descriptor?.Parser.ParseFrom(payload);
|
||||
var formatter = JsonFormatter.Default;
|
||||
var asJson = formatter.Format(packet);
|
||||
var output = $"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})\r\n{asJson}";
|
||||
#if DEBUG
|
||||
Logger.Debug(output);
|
||||
#endif
|
||||
if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile)
|
||||
{
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
var output = $"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})";
|
||||
#if DEBUG
|
||||
Logger.Debug(output);
|
||||
#endif
|
||||
if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile)
|
||||
{
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StreamWriter GetWriter()
|
||||
{
|
||||
// Create the file if it doesn't exist
|
||||
var file = new FileInfo(DebugFile);
|
||||
if (!file.Exists)
|
||||
{
|
||||
Directory.CreateDirectory(file.DirectoryName!);
|
||||
File.Create(DebugFile).Dispose();
|
||||
}
|
||||
|
||||
Writer ??= new StreamWriter(DebugFile, true);
|
||||
return Writer;
|
||||
}
|
||||
|
||||
public async Task SendPacket(byte[] packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = await Conversation.SendAsync(packet, CancelToken.Token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacket(BasePacket packet)
|
||||
{
|
||||
// Test
|
||||
if (packet.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 (BannedPackets.Contains(packet.CmdId)) return;
|
||||
LogPacket("Send", packet.CmdId, packet.Data);
|
||||
// Header
|
||||
var packetBytes = packet.BuildPacket();
|
||||
|
||||
try
|
||||
{
|
||||
_ = await Conversation.SendAsync(packetBytes, CancelToken.Token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacket(int cmdId)
|
||||
{
|
||||
await SendPacket(new BasePacket((ushort)cmdId));
|
||||
}
|
||||
}
|
||||
19
DanhengKcpSharp/DanhengKcpSharp.csproj
Normal file
19
DanhengKcpSharp/DanhengKcpSharp.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>DanhengKcpSharp</AssemblyName>
|
||||
<RootNamespace>EggLink.DanhengServer.Kcp</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.27.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
147
DanhengKcpSharp/DanhengListener.cs
Normal file
147
DanhengKcpSharp/DanhengListener.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using EggLink.DanhengServer.Internationalization;
|
||||
using EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
using EggLink.DanhengServer.Util;
|
||||
|
||||
namespace EggLink.DanhengServer.Kcp;
|
||||
|
||||
public class DanhengListener
|
||||
{
|
||||
private static UdpClient? UDPClient;
|
||||
private static IPEndPoint? ListenAddress;
|
||||
private static IKcpTransport<IKcpMultiplexConnection>? KCPTransport;
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
public static readonly SortedList<long, DanhengConnection> Connections = [];
|
||||
|
||||
private static readonly KcpConversationOptions ConvOpt = new()
|
||||
{
|
||||
StreamMode = false,
|
||||
Mtu = 1400,
|
||||
ReceiveWindow = 256,
|
||||
SendWindow = 256,
|
||||
NoDelay = true,
|
||||
UpdateInterval = 100,
|
||||
KeepAliveOptions = new KcpKeepAliveOptions(1000, 30000)
|
||||
};
|
||||
|
||||
public static Type BaseConnection { get; set; } = typeof(DanhengConnection);
|
||||
|
||||
private static Socket? UDPListener => UDPClient?.Client;
|
||||
private static IKcpMultiplexConnection? Multiplex => KCPTransport?.Connection;
|
||||
private static uint PORT => ConfigManager.Config.GameServer.PublicPort;
|
||||
|
||||
public static DanhengConnection? GetConnectionByEndPoint(IPEndPoint ep)
|
||||
{
|
||||
return Connections.Values.FirstOrDefault(c => c.RemoteEndPoint.Equals(ep));
|
||||
}
|
||||
|
||||
public static void StartListener()
|
||||
{
|
||||
ListenAddress = new IPEndPoint(IPAddress.Parse(ConfigManager.Config.GameServer.PublicAddress), (int)PORT);
|
||||
UDPClient = new UdpClient(ListenAddress);
|
||||
if (UDPListener == null) return;
|
||||
KCPTransport = KcpSocketTransport.CreateMultiplexConnection(UDPClient, 1400);
|
||||
KCPTransport.Start();
|
||||
Logger.Info(I18nManager.Translate("Server.ServerInfo.ServerRunning", I18nManager.Translate("Word.Game"),
|
||||
ConfigManager.Config.GameServer.GetDisplayAddress()));
|
||||
}
|
||||
|
||||
private static void RegisterConnection(DanhengConnection con)
|
||||
{
|
||||
if (!con.ConversationId.HasValue) return;
|
||||
Connections[con.ConversationId.Value] = con;
|
||||
}
|
||||
|
||||
public static void UnregisterConnection(DanhengConnection con)
|
||||
{
|
||||
if (!con.ConversationId.HasValue) return;
|
||||
var convId = con.ConversationId.Value;
|
||||
if (Connections.Remove(convId))
|
||||
{
|
||||
Multiplex?.UnregisterConversation(convId);
|
||||
Logger.Info($"Connection with {con.RemoteEndPoint} has been closed");
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task HandleHandshake(UdpReceiveResult rcv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var con = GetConnectionByEndPoint(rcv.RemoteEndPoint);
|
||||
await using MemoryStream? ms = new(rcv.Buffer);
|
||||
using BinaryReader? br = new(ms);
|
||||
var code = br.ReadInt32BE();
|
||||
br.ReadUInt32();
|
||||
br.ReadUInt32();
|
||||
var enet = br.ReadInt32BE();
|
||||
br.ReadUInt32();
|
||||
switch (code)
|
||||
{
|
||||
case 0x000000FF:
|
||||
if (con != null)
|
||||
{
|
||||
Logger.Info($"Duplicate handshake from {con.RemoteEndPoint}");
|
||||
return;
|
||||
}
|
||||
|
||||
await AcceptConnection(rcv, enet);
|
||||
break;
|
||||
case 0x00000194:
|
||||
if (con == null)
|
||||
{
|
||||
Logger.Info($"Inexistent connection asked for disconnect from {rcv.RemoteEndPoint}");
|
||||
return;
|
||||
}
|
||||
|
||||
await SendDisconnectPacket(con, 5);
|
||||
break;
|
||||
default:
|
||||
Logger.Error($"Invalid handshake code received {code}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Failed to handle handshake: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task AcceptConnection(UdpReceiveResult rcv, int enet)
|
||||
{
|
||||
var convId = Connections.GetNextAvailableIndex();
|
||||
var convo = Multiplex?.CreateConversation(convId, rcv.RemoteEndPoint, ConvOpt);
|
||||
if (convo == null) return;
|
||||
var con = (DanhengConnection)Activator.CreateInstance(BaseConnection, [convo, rcv.RemoteEndPoint])!;
|
||||
RegisterConnection(con);
|
||||
await SendHandshakeResponse(con, enet);
|
||||
}
|
||||
|
||||
private static async Task SendHandshakeResponse(DanhengConnection user, int enet)
|
||||
{
|
||||
if (user == null || UDPClient == null || !user.ConversationId.HasValue) return;
|
||||
var convId = user.ConversationId.Value;
|
||||
await using MemoryStream? ms = new();
|
||||
await using BinaryWriter? bw = new(ms);
|
||||
bw.WriteInt32BE(0x00000145);
|
||||
bw.WriteConvID(convId);
|
||||
bw.WriteInt32BE(enet);
|
||||
bw.WriteInt32BE(0x14514545);
|
||||
var data = ms.ToArray();
|
||||
await UDPClient.SendAsync(data, data.Length, user.RemoteEndPoint);
|
||||
}
|
||||
|
||||
public static async Task SendDisconnectPacket(DanhengConnection user, int code)
|
||||
{
|
||||
if (user == null || UDPClient == null || !user.ConversationId.HasValue) return;
|
||||
var convId = user.ConversationId.Value;
|
||||
await using MemoryStream? ms = new();
|
||||
await using BinaryWriter? bw = new(ms);
|
||||
bw.WriteInt32BE(0x00000194);
|
||||
bw.WriteConvID(convId);
|
||||
bw.WriteInt32BE(code);
|
||||
bw.WriteInt32BE(0x19419494);
|
||||
var data = ms.ToArray();
|
||||
await UDPClient.SendAsync(data, data.Length, user.RemoteEndPoint);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using System.Buffers;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class ArrayMemoryOwner : IMemoryOwner<byte>
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal class AsyncAutoResetEvent<T> : IValueTaskSource<T>
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class DefaultArrayPoolBufferAllocator : IKcpBufferPool
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// The buffer pool to rent buffers from.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A conversation or a channel over the transport.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal interface IKcpConversationUpdateNotificationSource
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// An instance that can produce exceptions in background jobs.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Net;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplex many channels or conversations over the same transport.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Net;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplex many channels or conversations over the same transport.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Net;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A transport to send and receive packets.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A transport instance for upper-level connections.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpAcknowledgeList
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal readonly struct KcpBuffer
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// The options to use when renting buffers from the pool.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal enum KcpCommand : byte
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
partial class KcpConversation
|
||||
{
|
||||
@@ -3,9 +3,9 @@ using LinkedListOfBufferItem = KcpSharp.NetstandardShim.LinkedList<KcpSharp.KcpS
|
||||
using LinkedListNodeOfBufferItem = KcpSharp.NetstandardShim.LinkedListNode<KcpSharp.KcpSendReceiveBufferItem>;
|
||||
#else
|
||||
using LinkedListOfBufferItem =
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.GameServer.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.Kcp.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
using LinkedListNodeOfBufferItem =
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.GameServer.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.Kcp.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
#endif
|
||||
using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
@@ -13,7 +13,7 @@ using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using EggLink.DanhengServer.Util;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A reliable channel over an unreliable transport implemented in KCP protocol.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to control the behaviors of <see cref="KcpConversation" />.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// The result of a receive or peek operation.
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpConversationUpdateActivation : IValueTaskSource<KcpConversationUpdateNotification>, IDisposable
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal readonly struct KcpConversationUpdateNotification : IDisposable
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for <see cref="IKcpExceptionProducer{T}" />.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal static class KcpGlobalVars
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Options for customized keep-alive functionality.
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplex many channels or conversations over the same transport.
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal readonly struct KcpPacketHeader : IEquatable<KcpPacketHeader>
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
[Flags]
|
||||
internal enum KcpProbeType
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// An unreliable channel with a conversation ID.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Options used to control the behaviors of <see cref="KcpRawChannelOptions" />.
|
||||
@@ -3,14 +3,14 @@ using LinkedListOfQueueItem = KcpSharp.NetstandardShim.LinkedList<KcpSharp.KcpBu
|
||||
using LinkedListNodeOfQueueItem = KcpSharp.NetstandardShim.LinkedListNode<KcpSharp.KcpBuffer>;
|
||||
#else
|
||||
using LinkedListOfQueueItem =
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer>;
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer>;
|
||||
using LinkedListNodeOfQueueItem =
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer>;
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer>;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpRawReceiveQueue : IValueTaskSource<KcpConversationReceiveResult>, IDisposable
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpRawSendOperation : IValueTaskSource<bool>, IDisposable
|
||||
{
|
||||
@@ -3,15 +3,15 @@ using LinkedListOfQueueItem = KcpSharp.NetstandardShim.LinkedList<(KcpSharp.KcpB
|
||||
using LinkedListNodeOfQueueItem = KcpSharp.NetstandardShim.LinkedListNode<(KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
#else
|
||||
using LinkedListOfQueueItem =
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
using LinkedListNodeOfQueueItem =
|
||||
System.Collections.Generic.LinkedListNode<(EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer Data, byte Fragment
|
||||
System.Collections.Generic.LinkedListNode<(EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer Data, byte Fragment
|
||||
)>;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpReceiveQueue : IValueTaskSource<KcpConversationReceiveResult>, IValueTaskSource<int>,
|
||||
IValueTaskSource<bool>, IDisposable
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Options for sending receive window size notification.
|
||||
@@ -3,7 +3,7 @@ using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// The buffer rented and owned by KcpSharp.
|
||||
@@ -3,12 +3,12 @@ using LinkedListOfQueueItem = KcpSharp.NetstandardShim.LinkedList<(KcpSharp.KcpB
|
||||
using LinkedListNodeOfQueueItem = KcpSharp.NetstandardShim.LinkedListNode<(KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
#else
|
||||
using LinkedListOfQueueItem =
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpSendQueue : IValueTaskSource<bool>, IValueTaskSource, IDisposable
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal struct KcpSendReceiveBufferItem
|
||||
{
|
||||
@@ -3,12 +3,12 @@ using LinkedListOfBufferItem = KcpSharp.NetstandardShim.LinkedList<KcpSharp.KcpS
|
||||
using LinkedListNodeOfBufferItem = KcpSharp.NetstandardShim.LinkedListNode<KcpSharp.KcpSendReceiveBufferItem>;
|
||||
#else
|
||||
using LinkedListNodeOfBufferItem =
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.GameServer.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
System.Collections.Generic.LinkedListNode<EggLink.DanhengServer.Kcp.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
using LinkedListOfBufferItem =
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.GameServer.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
System.Collections.Generic.LinkedList<EggLink.DanhengServer.Kcp.KcpSharp.KcpSendReceiveBufferItem>;
|
||||
#endif
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal struct KcpSendReceiveBufferItemCache
|
||||
{
|
||||
@@ -3,13 +3,13 @@ using LinkedListOfQueueItem = KcpSharp.NetstandardShim.LinkedList<(KcpSharp.KcpB
|
||||
using LinkedListNodeOfQueueItem = KcpSharp.NetstandardShim.LinkedListNode<(KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
#else
|
||||
using LinkedListNodeOfQueueItem =
|
||||
System.Collections.Generic.LinkedListNode<(EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer Data, byte Fragment
|
||||
System.Collections.Generic.LinkedListNode<(EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer Data, byte Fragment
|
||||
)>;
|
||||
using LinkedListOfQueueItem =
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.GameServer.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
System.Collections.Generic.LinkedList<(EggLink.DanhengServer.Kcp.KcpSharp.KcpBuffer Data, byte Fragment)>;
|
||||
#endif
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpSendReceiveQueueItemCache
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal readonly struct KcpSendSegmentStats
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods to create socket transports for KCP conversations.
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// Socket transport for KCP conversation.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpSocketTransportForMultiplexConnection<T> : KcpSocketTransport<KcpMultiplexConnection<T>>,
|
||||
IKcpTransport<IKcpMultiplexConnection<T>>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal sealed class KcpSocketTransportForRawChannel : KcpSocketTransport<KcpRawChannel>, IKcpTransport<KcpRawChannel>
|
||||
{
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Buffers;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using EggLink.DanhengServer.GameServer.Server;
|
||||
using EggLink.DanhengServer.Util;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A Socket transport for upper-level connections.
|
||||
@@ -123,8 +122,8 @@ public abstract class KcpSocketTransport<T> : IKcpTransport, IDisposable where T
|
||||
|
||||
if (bytesReceived != 0 && bytesReceived <= _mtu)
|
||||
{
|
||||
if (bytesReceived == Listener.HANDSHAKE_SIZE)
|
||||
await Listener.HandleHandshake(result);
|
||||
if (bytesReceived == DanhengConnection.HANDSHAKE_SIZE)
|
||||
await DanhengListener.HandleHandshake(result);
|
||||
else if (!error)
|
||||
await connection.InputPakcetAsync(result, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
/// <summary>
|
||||
/// A stream wrapper of <see cref="KcpConversation" />.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
namespace EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace EggLink.DanhengServer.Enums;
|
||||
namespace EggLink.DanhengServer.Kcp;
|
||||
|
||||
public enum SessionStateEnum
|
||||
{
|
||||
@@ -19,9 +19,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command", "Command\Command.csproj", "{B52B8DE8-E63E-4CE4-A75F-C17A97C86D89}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Command", "Command\Command.csproj", "{B52B8DE8-E63E-4CE4-A75F-C17A97C86D89}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Program", "Program\Program.csproj", "{71D8488F-CAED-48EE-BD5C-F325FBAB991F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Program", "Program\Program.csproj", "{71D8488F-CAED-48EE-BD5C-F325FBAB991F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto", "Proto\Proto.csproj", "{8A0ECA1A-167B-4B97-BF79-3665AF654A52}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DanhengKcpSharp", "DanhengKcpSharp\DanhengKcpSharp.csproj", "{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -49,6 +53,14 @@ Global
|
||||
{71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{71D8488F-CAED-48EE-BD5C-F325FBAB991F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A0ECA1A-167B-4B97-BF79-3665AF654A52}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD7EFAA3-C655-40EE-8F6A-A8E2DA3B0FCB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,37 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EggLink.DanhengServer.Data.Config;
|
||||
using EggLink.DanhengServer.Data.Config;
|
||||
using EggLink.DanhengServer.Data.Excel;
|
||||
using EggLink.DanhengServer.Enums.Mission;
|
||||
using EggLink.DanhengServer.GameServer.Game.Player;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Game.Mission.FinishType.Handler
|
||||
namespace EggLink.DanhengServer.GameServer.Game.Mission.FinishType.Handler;
|
||||
|
||||
[MissionFinishType(MissionFinishTypeEnum.FinishQuest)]
|
||||
public class MissionHandlerFinishQuest : MissionFinishTypeHandler
|
||||
{
|
||||
[MissionFinishType(MissionFinishTypeEnum.FinishQuest)]
|
||||
public class MissionHandlerFinishQuest : MissionFinishTypeHandler
|
||||
public override async ValueTask HandleMissionFinishType(PlayerInstance player, SubMissionInfo info, object? arg)
|
||||
{
|
||||
public override async ValueTask HandleMissionFinishType(PlayerInstance player, SubMissionInfo info, object? arg)
|
||||
{
|
||||
// this type wont be used in mission
|
||||
await ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override async ValueTask HandleQuestFinishType(PlayerInstance player, QuestDataExcel quest,
|
||||
FinishWayExcel excel, object? arg)
|
||||
{
|
||||
var questCount = 0;
|
||||
foreach (var qid in excel.ParamIntList)
|
||||
{
|
||||
var status = player.QuestManager?.GetQuestStatus(qid);
|
||||
if (status == QuestStatus.QuestFinish || status == QuestStatus.QuestClose)
|
||||
questCount++;
|
||||
}
|
||||
|
||||
await player.QuestManager!.UpdateQuestProgress(quest.QuestID, questCount);
|
||||
}
|
||||
// this type wont be used in mission
|
||||
await ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask HandleQuestFinishType(PlayerInstance player, QuestDataExcel quest,
|
||||
FinishWayExcel excel, object? arg)
|
||||
{
|
||||
var questCount = 0;
|
||||
foreach (var qid in excel.ParamIntList)
|
||||
{
|
||||
var status = player.QuestManager?.GetQuestStatus(qid);
|
||||
if (status == QuestStatus.QuestFinish || status == QuestStatus.QuestClose)
|
||||
questCount++;
|
||||
}
|
||||
|
||||
await player.QuestManager!.UpdateQuestProgress(quest.QuestID, questCount);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Player;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using static EggLink.DanhengServer.GameServer.Plugin.Event.PluginEvent;
|
||||
|
||||
@@ -43,7 +43,8 @@ public class QuestManager(PlayerInstance player) : BasePlayerManager(player)
|
||||
var accept2 = true;
|
||||
|
||||
foreach (var questId in quest.UnlockParamList)
|
||||
if (GetQuestStatus(questId) != QuestStatus.QuestFinish && GetQuestStatus(questId) != QuestStatus.QuestClose)
|
||||
if (GetQuestStatus(questId) != QuestStatus.QuestFinish &&
|
||||
GetQuestStatus(questId) != QuestStatus.QuestClose)
|
||||
{
|
||||
accept2 = false;
|
||||
break;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\WebServer\WebServer.csproj" />
|
||||
<ProjectReference Include="..\DanhengKcpSharp\DanhengKcpSharp.csproj" />
|
||||
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
del /s /f ..\Common\Proto\*.cs
|
||||
del /s /f ..\Proto\*.cs
|
||||
cd OriginalProto
|
||||
protoc ".\*" --csharp_out=..\..\Common\Proto\
|
||||
protoc ".\*" --csharp_out=..\..\Proto\
|
||||
@@ -1,130 +1,41 @@
|
||||
using System.Buffers;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using EggLink.DanhengServer.Enums;
|
||||
using EggLink.DanhengServer.GameServer.Game.Player;
|
||||
using EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Kcp.KcpSharp;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server;
|
||||
|
||||
public class Connection
|
||||
public class Connection : DanhengConnection
|
||||
{
|
||||
public static readonly List<int> BANNED_PACKETS = [];
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
public static readonly Dictionary<string, string> LogMap = [];
|
||||
|
||||
public static readonly List<int> IgnoreLog =
|
||||
[
|
||||
CmdIds.PlayerHeartBeatCsReq, CmdIds.PlayerHeartBeatScRsp, CmdIds.SceneEntityMoveCsReq,
|
||||
CmdIds.SceneEntityMoveScRsp, CmdIds.GetShopListCsReq, CmdIds.GetShopListScRsp
|
||||
];
|
||||
|
||||
private readonly CancellationTokenSource CancelToken;
|
||||
private readonly KcpConversation Conversation;
|
||||
public readonly IPEndPoint RemoteEndPoint;
|
||||
|
||||
public string DebugFile = "";
|
||||
public bool IsOnline = true;
|
||||
public StreamWriter? writer;
|
||||
|
||||
public Connection(KcpConversation conversation, IPEndPoint remote)
|
||||
public Connection(KcpConversation conversation, IPEndPoint remote) : base(conversation, remote)
|
||||
{
|
||||
Conversation = conversation;
|
||||
RemoteEndPoint = remote;
|
||||
CancelToken = new CancellationTokenSource();
|
||||
Start();
|
||||
}
|
||||
|
||||
public long? ConversationID => Conversation.ConversationId;
|
||||
public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE;
|
||||
public PlayerInstance? Player { get; set; }
|
||||
|
||||
private async void Start()
|
||||
public override async void Start()
|
||||
{
|
||||
Logger.Info($"New connection from {RemoteEndPoint}.");
|
||||
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
||||
await ReceiveLoop();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
public override void Stop()
|
||||
{
|
||||
Player?.OnLogoutAsync();
|
||||
Listener.UnregisterConnection(this);
|
||||
Conversation.Dispose();
|
||||
try
|
||||
{
|
||||
CancelToken.Cancel();
|
||||
CancelToken.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
DanhengListener.UnregisterConnection(this);
|
||||
base.Stop();
|
||||
|
||||
IsOnline = false;
|
||||
}
|
||||
|
||||
public void LogPacket(string sendOrRecv, ushort opcode, byte[] payload)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
|
||||
if (IgnoreLog.Contains(opcode)) return;
|
||||
#pragma warning disable CS8600
|
||||
var typ = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SingleOrDefault(assembly => assembly.GetName().Name == "DanhengCommon")!.GetTypes()
|
||||
.First(t => t.Name == $"{LogMap[opcode.ToString()]}"); //get the type using the packet name
|
||||
var descriptor =
|
||||
(MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)!.GetValue(
|
||||
null, null); // get the static property Descriptor
|
||||
var packet = descriptor!.Parser.ParseFrom(payload);
|
||||
#pragma warning restore CS8600
|
||||
var formatter = JsonFormatter.Default;
|
||||
var asJson = formatter.Format(packet);
|
||||
var output = $"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})\r\n{asJson}";
|
||||
#if DEBUG
|
||||
Logger.Debug(output);
|
||||
#endif
|
||||
if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile)
|
||||
{
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
var output = $"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})";
|
||||
#if DEBUG
|
||||
Logger.Debug(output);
|
||||
#endif
|
||||
if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile)
|
||||
{
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StreamWriter GetWriter()
|
||||
{
|
||||
// Create the file if it doesn't exist
|
||||
var file = new FileInfo(DebugFile);
|
||||
if (!file.Exists)
|
||||
{
|
||||
Directory.CreateDirectory(file.DirectoryName!);
|
||||
File.Create(DebugFile).Dispose();
|
||||
}
|
||||
|
||||
writer ??= new StreamWriter(DebugFile, true);
|
||||
return writer;
|
||||
}
|
||||
|
||||
private async Task ReceiveLoop()
|
||||
protected async Task ReceiveLoop()
|
||||
{
|
||||
while (!CancelToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -136,7 +47,7 @@ public class Connection
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.BytesReceived > Listener.MAX_MSG_SIZE)
|
||||
if (result.BytesReceived > MAX_MSG_SIZE)
|
||||
{
|
||||
// The message is too large.
|
||||
Logger.Error("Packet too large");
|
||||
@@ -175,8 +86,8 @@ public class Connection
|
||||
{
|
||||
var gamePacket = data.ToArray();
|
||||
|
||||
await using MemoryStream? ms = new(gamePacket);
|
||||
using BinaryReader? br = new(ms);
|
||||
await using MemoryStream ms = new(gamePacket);
|
||||
using BinaryReader br = new(ms);
|
||||
|
||||
// Handle
|
||||
try
|
||||
@@ -186,10 +97,10 @@ public class Connection
|
||||
// Length
|
||||
if (br.BaseStream.Length - br.BaseStream.Position < 12) return;
|
||||
// Packet sanity check
|
||||
var Magic1 = br.ReadUInt32BE();
|
||||
if (Magic1 != 0x9D74C714)
|
||||
var magic1 = br.ReadUInt32BE();
|
||||
if (magic1 != 0x9D74C714)
|
||||
{
|
||||
Logger.Error($"Bad Data Package Received: got 0x{Magic1:X}, expect 0x9D74C714");
|
||||
Logger.Error($"Bad Data Package Received: got 0x{magic1:X}, expect 0x9D74C714");
|
||||
return; // Bad packet
|
||||
}
|
||||
|
||||
@@ -244,34 +155,4 @@ public class Connection
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task SendPacket(BasePacket packet)
|
||||
{
|
||||
// Test
|
||||
if (packet.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(packet.CmdId)) return;
|
||||
LogPacket("Send", packet.CmdId, packet.Data);
|
||||
// Header
|
||||
var packetBytes = packet.BuildPacket();
|
||||
|
||||
try
|
||||
{
|
||||
_ = await Conversation.SendAsync(packetBytes, CancelToken.Token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacket(int cmdId)
|
||||
{
|
||||
await SendPacket(new BasePacket((ushort)cmdId));
|
||||
}
|
||||
}
|
||||
@@ -1,154 +1,14 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using EggLink.DanhengServer.Enums;
|
||||
using EggLink.DanhengServer.GameServer.KcpSharp;
|
||||
using EggLink.DanhengServer.Internationalization;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using EggLink.DanhengServer.Enums;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server;
|
||||
|
||||
public class Listener
|
||||
public class Listener : DanhengListener
|
||||
{
|
||||
public const int MAX_MSG_SIZE = 16384;
|
||||
public const int HANDSHAKE_SIZE = 20;
|
||||
private static UdpClient? UDPClient;
|
||||
private static IPEndPoint? ListenAddress;
|
||||
private static IKcpTransport<IKcpMultiplexConnection>? KCPTransport;
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
public static readonly SortedList<long, Connection> Connections = [];
|
||||
|
||||
private static readonly KcpConversationOptions ConvOpt = new()
|
||||
{
|
||||
StreamMode = false,
|
||||
Mtu = 1400,
|
||||
ReceiveWindow = 256,
|
||||
SendWindow = 256,
|
||||
NoDelay = true,
|
||||
UpdateInterval = 100,
|
||||
KeepAliveOptions = new KcpKeepAliveOptions(1000, 30000)
|
||||
};
|
||||
|
||||
private static Socket? UDPListener => UDPClient?.Client;
|
||||
private static IKcpMultiplexConnection? Multiplex => KCPTransport?.Connection;
|
||||
private static uint PORT => ConfigManager.Config.GameServer.PublicPort;
|
||||
|
||||
public static Connection? GetConnectionByEndPoint(IPEndPoint ep)
|
||||
{
|
||||
return Connections.Values.FirstOrDefault(c => c.RemoteEndPoint.Equals(ep));
|
||||
}
|
||||
|
||||
public static void StartListener()
|
||||
{
|
||||
ListenAddress = new IPEndPoint(IPAddress.Parse(ConfigManager.Config.GameServer.PublicAddress), (int)PORT);
|
||||
UDPClient = new UdpClient(ListenAddress);
|
||||
if (UDPListener == null) return;
|
||||
KCPTransport = KcpSocketTransport.CreateMultiplexConnection(UDPClient, 1400);
|
||||
KCPTransport.Start();
|
||||
Logger.Info(I18nManager.Translate("Server.ServerInfo.ServerRunning", I18nManager.Translate("Word.Game"),
|
||||
ConfigManager.Config.GameServer.GetDisplayAddress()));
|
||||
}
|
||||
|
||||
private static void RegisterConnection(Connection con)
|
||||
{
|
||||
if (!con.ConversationID.HasValue) return;
|
||||
Connections[con.ConversationID.Value] = con;
|
||||
}
|
||||
|
||||
public static void UnregisterConnection(Connection con)
|
||||
{
|
||||
if (!con.ConversationID.HasValue) return;
|
||||
var convId = con.ConversationID.Value;
|
||||
if (Connections.Remove(convId))
|
||||
{
|
||||
Multiplex?.UnregisterConversation(convId);
|
||||
Logger.Info($"Connection with {con.RemoteEndPoint} has been closed");
|
||||
}
|
||||
}
|
||||
|
||||
public static Connection? GetActiveConnection(int uid)
|
||||
{
|
||||
var con = Connections.Values.FirstOrDefault(c => c.Player?.Uid == uid && c.State == SessionStateEnum.ACTIVE);
|
||||
var con = Connections.Values.FirstOrDefault(c =>
|
||||
(c as Connection)?.Player?.Uid == uid && c.State == SessionStateEnum.ACTIVE) as Connection;
|
||||
return con;
|
||||
}
|
||||
|
||||
public static async Task HandleHandshake(UdpReceiveResult rcv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var con = GetConnectionByEndPoint(rcv.RemoteEndPoint);
|
||||
await using MemoryStream? ms = new(rcv.Buffer);
|
||||
using BinaryReader? br = new(ms);
|
||||
var code = br.ReadInt32BE();
|
||||
br.ReadUInt32();
|
||||
br.ReadUInt32();
|
||||
var enet = br.ReadInt32BE();
|
||||
br.ReadUInt32();
|
||||
switch (code)
|
||||
{
|
||||
case 0x000000FF:
|
||||
if (con != null)
|
||||
{
|
||||
Logger.Info($"Duplicate handshake from {con.RemoteEndPoint}");
|
||||
return;
|
||||
}
|
||||
|
||||
await AcceptConnection(rcv, enet);
|
||||
break;
|
||||
case 0x00000194:
|
||||
if (con == null)
|
||||
{
|
||||
Logger.Info($"Inexistent connection asked for disconnect from {rcv.RemoteEndPoint}");
|
||||
return;
|
||||
}
|
||||
|
||||
await SendDisconnectPacket(con, 5);
|
||||
break;
|
||||
default:
|
||||
Logger.Error($"Invalid handshake code received {code}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Failed to handle handshake: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task AcceptConnection(UdpReceiveResult rcv, int enet)
|
||||
{
|
||||
var convId = Connections.GetNextAvailableIndex();
|
||||
var convo = Multiplex?.CreateConversation(convId, rcv.RemoteEndPoint, ConvOpt);
|
||||
if (convo == null) return;
|
||||
Connection? con = new(convo, rcv.RemoteEndPoint);
|
||||
RegisterConnection(con);
|
||||
await SendHandshakeResponse(con, enet);
|
||||
}
|
||||
|
||||
private static async Task SendHandshakeResponse(Connection user, int enet)
|
||||
{
|
||||
if (user == null || UDPClient == null || !user.ConversationID.HasValue) return;
|
||||
var convId = user.ConversationID.Value;
|
||||
await using MemoryStream? ms = new();
|
||||
using BinaryWriter? bw = new(ms);
|
||||
bw.WriteInt32BE(0x00000145);
|
||||
bw.WriteConvID(convId);
|
||||
bw.WriteInt32BE(enet);
|
||||
bw.WriteInt32BE(0x14514545);
|
||||
var data = ms.ToArray();
|
||||
await UDPClient.SendAsync(data, data.Length, user.RemoteEndPoint);
|
||||
}
|
||||
|
||||
public static async Task SendDisconnectPacket(Connection user, int code)
|
||||
{
|
||||
if (user == null || UDPClient == null || !user.ConversationID.HasValue) return;
|
||||
var convId = user.ConversationID.Value;
|
||||
await using MemoryStream? ms = new();
|
||||
using BinaryWriter? bw = new(ms);
|
||||
bw.WriteInt32BE(0x00000194);
|
||||
bw.WriteConvID(convId);
|
||||
bw.WriteInt32BE(code);
|
||||
bw.WriteInt32BE(0x19419494);
|
||||
var data = ms.ToArray();
|
||||
await UDPClient.SendAsync(data, data.Length, user.RemoteEndPoint);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public static class HandlerManager
|
||||
var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly
|
||||
foreach (var cls in classes)
|
||||
{
|
||||
var attribute = (Opcode)Attribute.GetCustomAttribute(cls, typeof(Opcode))!;
|
||||
var attribute = (Opcode?)Attribute.GetCustomAttribute(cls, typeof(Opcode));
|
||||
|
||||
if (attribute != null) handlers.Add(attribute.CmdId, (Handler)Activator.CreateInstance(cls)!);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Activity;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Activity;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Activity;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Activity;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Activity;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Game.Activity.Activities;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Activity;
|
||||
|
||||
@@ -4,6 +4,7 @@ using EggLink.DanhengServer.Database.Inventory;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Activity;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Player;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Activity;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
[Opcode(CmdIds.GetCurAssistCsReq)]
|
||||
public class HandlerGetCurAssistCsReq : Handler
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using EggLink.DanhengServer.Data;
|
||||
using EggLink.DanhengServer.Enums.Avatar;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EggLink.DanhengServer.Proto;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Player;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using EggLink.DanhengServer.Enums.Mission;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Avatar;
|
||||
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Player;
|
||||
using EggLink.DanhengServer.Kcp;
|
||||
using EggLink.DanhengServer.Proto;
|
||||
|
||||
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Avatar;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user