mirror of
https://github.com/MikuLeaks/KianaBH3.git
synced 2025-12-12 13:04:33 +01:00
189 lines
7.3 KiB
C#
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);
|
|
}
|
|
} |