mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-03 04:36:03 +08:00
Feature:Asynchronous Operation & Formatting Code
- Now the async operation is enabled! - Code formatted by Resharper plugin <3
This commit is contained in:
@@ -1,35 +1,35 @@
|
||||
using System.Buffers;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using EggLink.DanhengServer.Common.Enums;
|
||||
using EggLink.DanhengServer.Game.Player;
|
||||
using EggLink.DanhengServer.KcpSharp;
|
||||
using EggLink.DanhengServer.Server.Packet;
|
||||
using EggLink.DanhengServer.Util;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using KcpSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace EggLink.DanhengServer.Server;
|
||||
public partial class Connection
|
||||
|
||||
public class Connection
|
||||
{
|
||||
public long? ConversationID => Conversation.ConversationId;
|
||||
private readonly KcpConversation Conversation;
|
||||
private readonly CancellationTokenSource CancelToken;
|
||||
public readonly IPEndPoint RemoteEndPoint;
|
||||
public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE;
|
||||
public PlayerInstance? Player { get; set; }
|
||||
public static readonly List<int> BANNED_PACKETS = [];
|
||||
public bool IsOnline = true;
|
||||
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];
|
||||
|
||||
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 StreamWriter? writer = null;
|
||||
public bool IsOnline = true;
|
||||
public StreamWriter? writer;
|
||||
|
||||
public Connection(KcpConversation conversation, IPEndPoint remote)
|
||||
{
|
||||
@@ -39,12 +39,17 @@ public partial class Connection
|
||||
Start();
|
||||
}
|
||||
|
||||
public long? ConversationID => Conversation.ConversationId;
|
||||
public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE;
|
||||
public PlayerInstance? Player { get; set; }
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
Logger.Info($"New connection from {RemoteEndPoint}.");
|
||||
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
||||
await ReceiveLoop();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Player?.OnLogoutAsync();
|
||||
@@ -55,7 +60,10 @@ public partial class Connection
|
||||
CancelToken.Cancel();
|
||||
CancelToken.Dispose();
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
IsOnline = false;
|
||||
}
|
||||
|
||||
@@ -64,25 +72,25 @@ public partial class Connection
|
||||
try
|
||||
{
|
||||
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
|
||||
if (IgnoreLog.Contains(opcode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IgnoreLog.Contains(opcode)) return;
|
||||
#pragma warning disable CS8600
|
||||
Type? 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
|
||||
MessageDescriptor? descriptor = (MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)!.GetValue(null, null); // get the static property Descriptor
|
||||
IMessage? packet = descriptor!.Parser.ParseFrom(payload);
|
||||
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
|
||||
JsonFormatter? formatter = JsonFormatter.Default;
|
||||
string? asJson = formatter.Format(packet);
|
||||
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)
|
||||
{
|
||||
StreamWriter? sw = GetWriter();
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
@@ -95,7 +103,7 @@ public partial class Connection
|
||||
#endif
|
||||
if (DebugFile != "" && ConfigManager.Config.ServerOption.SavePersonalDebugFile)
|
||||
{
|
||||
StreamWriter? sw = GetWriter();
|
||||
var sw = GetWriter();
|
||||
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
||||
sw.Flush();
|
||||
}
|
||||
@@ -121,12 +129,13 @@ public partial class Connection
|
||||
while (!CancelToken.IsCancellationRequested)
|
||||
{
|
||||
// WaitToReceiveAsync call completes when there is at least one message is received or the transport is closed.
|
||||
KcpConversationReceiveResult result = await Conversation.WaitToReceiveAsync(CancelToken.Token);
|
||||
var result = await Conversation.WaitToReceiveAsync(CancelToken.Token);
|
||||
if (result.TransportClosed)
|
||||
{
|
||||
Logger.Debug("Connection was closed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.BytesReceived > Listener.MAX_MSG_SIZE)
|
||||
{
|
||||
// The message is too large.
|
||||
@@ -135,7 +144,7 @@ public partial class Connection
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(result.BytesReceived);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(result.BytesReceived);
|
||||
try
|
||||
{
|
||||
// TryReceive should not return false here, unless the transport is closed.
|
||||
@@ -145,6 +154,7 @@ public partial class Connection
|
||||
Logger.Error("Failed to receive packet");
|
||||
break;
|
||||
}
|
||||
|
||||
await ProcessMessageAsync(buffer.AsMemory(0, result.BytesReceived));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -156,13 +166,14 @@ public partial class Connection
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Stop();
|
||||
}
|
||||
|
||||
// DO THE PROCESSING OF THE GAME PACKET
|
||||
private async Task ProcessMessageAsync(Memory<byte> data)
|
||||
{
|
||||
byte[] gamePacket = data.ToArray();
|
||||
var gamePacket = data.ToArray();
|
||||
|
||||
await using MemoryStream? ms = new(gamePacket);
|
||||
using BinaryReader? br = new(ms);
|
||||
@@ -173,27 +184,24 @@ public partial class Connection
|
||||
while (br.BaseStream.Position < br.BaseStream.Length)
|
||||
{
|
||||
// Length
|
||||
if (br.BaseStream.Length - br.BaseStream.Position < 12)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (br.BaseStream.Length - br.BaseStream.Position < 12) return;
|
||||
// Packet sanity check
|
||||
uint Magic1 = br.ReadUInt32BE();
|
||||
var Magic1 = br.ReadUInt32BE();
|
||||
if (Magic1 != 0x9D74C714)
|
||||
{
|
||||
Logger.Error($"Bad Data Package Received: got 0x{Magic1:X}, expect 0x9D74C714");
|
||||
return; // Bad packet
|
||||
}
|
||||
// Data
|
||||
ushort opcode = br.ReadUInt16BE();
|
||||
ushort headerLength = br.ReadUInt16BE();
|
||||
uint payloadLength = br.ReadUInt32BE();
|
||||
byte[] header = br.ReadBytes(headerLength);
|
||||
byte[] payload = br.ReadBytes((int)payloadLength);
|
||||
LogPacket("Recv", opcode, payload);
|
||||
HandlePacket(opcode, header, payload);
|
||||
}
|
||||
|
||||
// Data
|
||||
var opcode = br.ReadUInt16BE();
|
||||
var headerLength = br.ReadUInt16BE();
|
||||
var payloadLength = br.ReadUInt32BE();
|
||||
var header = br.ReadBytes(headerLength);
|
||||
var payload = br.ReadBytes((int)payloadLength);
|
||||
LogPacket("Recv", opcode, payload);
|
||||
await HandlePacket(opcode, header, payload);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -205,44 +213,39 @@ public partial class Connection
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandlePacket(ushort opcode, byte[] header, byte[] payload)
|
||||
private async Task<bool> HandlePacket(ushort opcode, byte[] header, byte[] payload)
|
||||
{
|
||||
// Find the Handler for this opcode
|
||||
Handler? handler = HandlerManager.GetHandler(opcode);
|
||||
var handler = HandlerManager.GetHandler(opcode);
|
||||
if (handler != null)
|
||||
{
|
||||
// Handle
|
||||
// Make sure session is ready for packets
|
||||
SessionStateEnum state = State;
|
||||
switch ((int)opcode)
|
||||
var state = State;
|
||||
switch (opcode)
|
||||
{
|
||||
case CmdIds.PlayerGetTokenCsReq:
|
||||
{
|
||||
if (state != SessionStateEnum.WAITING_FOR_TOKEN)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
goto default;
|
||||
}
|
||||
{
|
||||
if (state != SessionStateEnum.WAITING_FOR_TOKEN) return true;
|
||||
goto default;
|
||||
}
|
||||
case CmdIds.PlayerLoginCsReq:
|
||||
{
|
||||
if (state != SessionStateEnum.WAITING_FOR_LOGIN)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
goto default;
|
||||
}
|
||||
{
|
||||
if (state != SessionStateEnum.WAITING_FOR_LOGIN) return true;
|
||||
goto default;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
handler.OnHandle(this, header, payload);
|
||||
|
||||
await handler.OnHandle(this, header, payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SendPacket(BasePacket packet)
|
||||
public async Task SendPacket(BasePacket packet)
|
||||
{
|
||||
// Test
|
||||
if (packet.CmdId <= 0)
|
||||
@@ -252,27 +255,23 @@ public partial class Connection
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (BANNED_PACKETS.Contains(packet.CmdId)) return;
|
||||
LogPacket("Send", packet.CmdId, packet.Data);
|
||||
// Header
|
||||
byte[] packetBytes = packet.BuildPacket();
|
||||
var packetBytes = packet.BuildPacket();
|
||||
|
||||
try
|
||||
{
|
||||
#pragma warning disable CA2012
|
||||
_ = Conversation.SendAsync(packetBytes, CancelToken.Token);
|
||||
#pragma warning restore CA2012
|
||||
} catch
|
||||
_ = await Conversation.SendAsync(packetBytes, CancelToken.Token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void SendPacket(int cmdId)
|
||||
public async Task SendPacket(int cmdId)
|
||||
{
|
||||
SendPacket(new BasePacket((ushort)cmdId));
|
||||
await SendPacket(new BasePacket((ushort)cmdId));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user