mirror of
https://github.com/MikuLeaks/KianaBH3.git
synced 2025-12-13 05:14:46 +01:00
165 lines
5.3 KiB
C#
165 lines
5.3 KiB
C#
using KianaBH.KcpSharp.Base;
|
|
using KianaBH.Proto;
|
|
using KianaBH.Util;
|
|
using KianaBH.Util.Security;
|
|
using Google.Protobuf;
|
|
using Google.Protobuf.Reflection;
|
|
using System.Collections.Concurrent;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
|
|
namespace KianaBH.KcpSharp;
|
|
|
|
public class KcpConnection
|
|
{
|
|
public const int HANDSHAKE_SIZE = 20;
|
|
public static readonly ConcurrentBag<int> BannedPackets = [];
|
|
private static readonly Logger Logger = new("GameServer");
|
|
public static readonly ConcurrentDictionary<int, string> LogMap = [];
|
|
|
|
public static readonly ConcurrentBag<int> IgnoreLog =
|
|
[
|
|
//CmdIds.PlayerHeartBeatCsReq, CmdIds.PlayerHeartBeatScRsp,
|
|
//CmdIds.SceneEntityMoveCsReq, CmdIds.SceneEntityMoveScRsp,
|
|
//CmdIds.ClientDownloadDataScNotify
|
|
];
|
|
|
|
protected readonly CancellationTokenSource CancelToken;
|
|
protected readonly KcpConversation Conversation;
|
|
public readonly IPEndPoint RemoteEndPoint;
|
|
|
|
public string DebugFile = "";
|
|
public bool IsOnline = true;
|
|
public StreamWriter? Writer;
|
|
|
|
public KcpConnection(KcpConversation conversation, IPEndPoint remote)
|
|
{
|
|
Conversation = conversation;
|
|
RemoteEndPoint = remote;
|
|
CancelToken = new CancellationTokenSource();
|
|
Start();
|
|
}
|
|
|
|
public byte[]? XorKey { get; set; }
|
|
public ulong ClientSecretKeySeed { get; set; }
|
|
|
|
public long? ConversationId => Conversation.ConversationId;
|
|
|
|
public SessionStateEnum State { get; set; } = SessionStateEnum.INACTIVE;
|
|
|
|
public virtual void Start()
|
|
{
|
|
Logger.Info($"New connection from {RemoteEndPoint}.");
|
|
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
|
}
|
|
|
|
public virtual void Stop(bool isServerStop = false)
|
|
{
|
|
Conversation.Dispose();
|
|
try
|
|
{
|
|
CancelToken.Cancel();
|
|
CancelToken.Dispose();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
IsOnline = false;
|
|
}
|
|
|
|
public void LogPacket(string sendOrRecv, ushort opcode, byte[] payload)
|
|
{
|
|
if (!ConfigManager.Config.ServerOption.EnableDebug) return;
|
|
try
|
|
{
|
|
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
|
|
if (IgnoreLog.Contains(opcode)) return;
|
|
if (!ConfigManager.Config.ServerOption.DebugDetailMessage) throw new Exception(); // go to catch block
|
|
var typ = AppDomain.CurrentDomain.GetAssemblies()
|
|
.SingleOrDefault(assembly => assembly.GetName().Name == "KianaProto")!.GetTypes()
|
|
.First(t => t.Name == $"{LogMap[opcode]}"); //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]}({opcode})\r\n{asJson}";
|
|
if (ConfigManager.Config.ServerOption.DebugMessage)
|
|
Logger.Debug(output);
|
|
if (DebugFile == "" || !ConfigManager.Config.ServerOption.SavePersonalDebugFile) return;
|
|
var sw = GetWriter();
|
|
sw.WriteLine($"[{DateTime.Now:HH:mm:ss}] [GameServer] [DEBUG] " + output);
|
|
sw.Flush();
|
|
}
|
|
catch
|
|
{
|
|
var output = $"{sendOrRecv}: {LogMap.GetValueOrDefault(opcode, "UnknownPacket")}({opcode})";
|
|
if (ConfigManager.Config.ServerOption.DebugMessage)
|
|
Logger.Debug(output);
|
|
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.Body);
|
|
// Header
|
|
var packetBytes = packet.BuildPacket();
|
|
|
|
try
|
|
{
|
|
await SendPacket(packetBytes);
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
public async Task SendPacket(int cmdId)
|
|
{
|
|
await SendPacket(new BasePacket((ushort)cmdId));
|
|
}
|
|
} |