Feature:Asynchronous Operation & Formatting Code

- Now the async operation is enabled!
- Code formatted by Resharper plugin <3
This commit is contained in:
Somebody
2024-07-22 17:12:03 +08:00
parent e983375620
commit 87d228eb79
793 changed files with 34764 additions and 40190 deletions

View File

@@ -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));
}
}
}