Files
KianaBH3/GameServer/Server/Connection.cs
2025-06-14 11:15:32 +08:00

189 lines
7.3 KiB
C#

using KianaBH.GameServer.Game.Player;
using KianaBH.GameServer.Server.Packet;
using KianaBH.KcpSharp;
using KianaBH.KcpSharp.Base;
using KianaBH.Proto;
using KianaBH.Util;
using KianaBH.Util.Extensions;
using KianaBH.Util.Security;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using System.Buffers;
using System.Net;
using System.Reflection;
using Newtonsoft.Json.Linq;
using System.Reflection.PortableExecutable;
using System.Net.Sockets;
using System.Reflection.Emit;
namespace KianaBH.GameServer.Server;
public class Connection(KcpConversation conversation, IPEndPoint remote) : KcpConnection(conversation, remote)
{
private static readonly Logger Logger = new("GameServer");
public PlayerInstance? Player { get; set; }
private static readonly HashSet<string> DummyPacketNames =
[
"AddGoodfeelReq", "ArkPlusActivityGetDataReq", "BuffAssistGetActivityReq", "BwWorldCampActivityGetDataReq",
"ChatworldBeastGetActivityReq", "ChatworldGetPrayInfoReq", "ClientReportReq", "GetAdventureGroupReq",
"GetArmadaDataReq", "GetArmadaStageScoreActivityReq", "GetAskAddFriendListReq", "GetAssistantFrozenListReq",
"GetAvatarMissionActivityReq", "GetBattlePassMissionPanelReq", "GetBlackListReq", "GetCardProductInfoReq",
"GetChapterActivityDataReq", "GetChapterCompensationInfoReq", "GetChatgroupListReq", "GetClientMailDataReq",
"GetConsignedOrderDataReq", "GetCurrencyExchangeInfoReq", "GetExtractReforgeActivityReq",
"GetFarmActivityDataReq", "GetFriendListReq", "GetFriendRemarkListReq", "GetGachaDisplayReq",
"GetGardenScheduleReq", "GetGobackReq", "GetGratuityActivityReq", "GetMasterPupilCardReq",
"GetMasterPupilDataReq", "GetMasterPupilMainDataReq", "GetMosaicActivityReq", "GetNewbieActivityReq",
"GetNinjaActivityReq", "GetOfflineResourceDataReq", "GetOpenworldQuestActivityReq",
"GetRaffleActivityReq", "GetRankScheduleDataReq", "GetRecommendFriendListReq", "GetRecommendGoodsReq",
"GetRoomDataReq", "GetRpgTaleReq", "GetScratchTicketReq", "GetSecurityPasswordReq", "GetShoppingMallListReq",
"GetStageChapterReq", "GetSupportActivityReq", "GetSurveyDataReq",
"GetTradingCardActivityReq", "GetTvtActivityReq", "GetWeeklyRoutineActivityReq", "GrandKeyActivateSkillReq",
"MassiveWarGetActivityReq", "OpenworldGetMechaTeamReq", "OpenworldHuntActivityGetDataReq",
"PjmsGetAchievementDataReq", "PjmsGetConditionDataReq", "PjmsGetCurWorldReq", "PjmsGetStoryDataReq",
"ReunionCookGetActivityReq", "SimplifiedGodWarGetActivityReq",
"StageInnerDataReportReq", "SusannaTrialGetActivityReq", "ThemeWantedRefreshTicketReq",
"UpdateMissionProgressReq", "WaveRushGetActivityReq"
];
public override async void Start()
{
Logger.Info($"New connection from {RemoteEndPoint}.");
State = SessionStateEnum.WAITING_FOR_TOKEN;
await ReceiveLoop();
}
public override async void Stop(bool isServerStop = false)
{
//if (isServerStop) await Player!.SendPacket(new PacketPlayerKickOutScNotify(KickType.KickLoginWhiteTimeout));
Player?.OnLogoutAsync();
KcpListener.UnregisterConnection(this);
base.Stop(isServerStop);
}
protected async Task ReceiveLoop()
{
while (!CancelToken.IsCancellationRequested)
{
// WaitToReceiveAsync call completes when there is at least one message is received or the transport is closed.
var result = await Conversation.WaitToReceiveAsync(CancelToken.Token);
if (result.TransportClosed)
{
Logger.Debug("Connection was closed");
break;
}
var buffer = ArrayPool<byte>.Shared.Rent(result.BytesReceived);
try
{
// TryReceive should not return false here, unless the transport is closed.
// So we don't need to check for result.TransportClosed.
if (!Conversation.TryReceive(buffer, out result))
{
Logger.Error("Failed to receive packet");
break;
}
await ProcessMessageAsync(buffer.AsMemory(0, result.BytesReceived));
}
catch (Exception ex)
{
Logger.Error("Packet parse error", ex);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
Stop();
}
// DO THE PROCESSING OF THE GAME PACKET
private async Task ProcessMessageAsync(Memory<byte> data)
{
var gamePacket = data.ToArray();
await using MemoryStream ms = new(gamePacket);
using BinaryReader br = new(ms);
// Handle
try
{
while (br.BaseStream.Position < br.BaseStream.Length)
{
// Length
if (br.BaseStream.Length - br.BaseStream.Position < 32) return;
// Packet sanity check
var headMagic = br.ReadUInt32BE();
if (headMagic != 0x01234567)
{
Logger.Error($"Bad Data Package Received: got 0x{headMagic:X}, expect 0x01234567");
return; // Bad packet
}
var packetVersion = br.ReadUInt16BE();
var ClientVersion = br.ReadUInt16BE();
var PacketId = br.ReadUInt32BE();
var UserId = br.ReadUInt32BE();
var UserIp = br.ReadUInt32BE();
var Sign = br.ReadUInt32BE();
var SignType = br.ReadUInt16BE();
var CmdId = br.ReadUInt16BE();
var HeaderLength = br.ReadUInt16BE();
var BodyLength = br.ReadUInt32BE();
// Data
var header = br.ReadBytes(HeaderLength);
var Body = br.ReadBytes((int)BodyLength);
var TailMagic = br.ReadUInt32BE();
LogPacket("Recv", CmdId, Body);
await HandlePacket(CmdId, header, Body);
}
}
catch (Exception e)
{
Logger.Error(e.Message, e);
}
}
private async Task HandlePacket(ushort opcode, byte[] header, byte[] payload)
{
var packetName = LogMap.GetValueOrDefault(opcode);
if (DummyPacketNames.Contains(packetName!))
{
await SendDummy(packetName!);
Logger.Info($"[Dummy] Send Dummy {packetName}");
return;
}
// Find the Handler for this opcode
var handler = HandlerManager.GetHandler(opcode);
if (handler != null)
{
// Handle
// Make sure session is ready for packets
var state = State;
try
{
await handler.OnHandle(this, header, payload);
}
catch (Exception e)
{
Logger.Error(e.Message, e);
}
return;
}
}
private async Task SendDummy(string packetName)
{
var respName = packetName.Replace("Req", "Rsp"); // Get the response packet name
if (respName == packetName) return; // do not send rsp when resp name = recv name
var respOpcode = LogMap.FirstOrDefault(x => x.Value == respName).Key; // Get the response opcode
// Send Rsp
await SendPacket(respOpcode);
}
}