이번 포스트에서는 이전의 PacketManager를 일일이 만들어주던 부분을 자동화하도록 수정하겠습니다.
또 클라이언트와 서버의 패킷의 역할을 분리하여 동일한 패킷이 아닌
각기 다른 패킷을 사용하도록 수정하겠습니다.
또한 패킷에 차이점을 두어
- 클라이언트에서 서버로 전송하는 패킷의 경우 앞에 C_ 를 수식
- 서버에서 클라이언트로 전송하는 경우 앞에 S_ 를 수식
위와 같이 구성하여 최종적으로 각 진영의 PackManager에서 Register를 진행(=등록)하도록 하겠습니다.
1) 패킷 구조 xml
<?xml version="1.0" encoding="utf-8" ?>
<PDL>
<packet name ="C_PlayerInfoRequire">
<long name = "_playerID"/>
<byte name = "_nameByte"/>
<string name = "_name"/>
<list name = "Stat">
<int name = "_statID"/>
<short name = "_level"/>
<float name = "_connectDuration"/>
<list name = "Attribute">
<int name="att"/>
</list>
</list>
</packet>
<packet name ="S_Test">
<int name ="_TestNum"/>
</packet>
</PDL>
기존의 패킷 구조를 위와 같이 변경하였습니다.
이전의 예제에서도 클라이언트가 서버로 전달하는 패킷의 경우에 데이터가 많이 포함되었으므로
비교적 덜 사용하는 Test를 서버->클라이언트의 패킷으로 사용합니다.
2) PacketFormat
class PacketFormat
{
// {0} 패킷 등록
public static string managerFormat =
@" using ServerCore;
using System;
using System.Collections.Generic;
class PacketManager
{{
static PacketManager _instance;
public static PacketManager Instance
{{
get
{{
if(_instance == null)
_instance = new PacketManager();
return _instance;
}}
}}
Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>> _onRecv = new Dictionary<ushort, Action<PacketSession, ArraySegment<byte>>>();
Dictionary<ushort, Action<PacketSession, IPacket>> _handler = new Dictionary<ushort, Action<PacketSession, IPacket>>();
public void Register()
{{
{0}
}}
public void OnRecvPacket(PacketSession session, ArraySegment<byte> buffer)
{{
ushort count = 0;
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
count += 2;
ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
count += 2;
Action<PacketSession, ArraySegment<byte>> action = null;
if (_onRecv.TryGetValue(id, out action))
action.Invoke(session, buffer);
}}
void MakePacket<T>(PacketSession session, ArraySegment<byte> buffer) where T : IPacket, new()
{{
T packet = new T();
packet.Read(buffer);
Action<PacketSession, IPacket> action = null;
if (_handler.TryGetValue(packet.Protocol, out action))
action.Invoke(session, packet);
}}
}}
";
// {0} 패킷 이름
public static string managerRegisterFormat =
@"
_onRecv.Add((ushort)PacketID.{0}, MakePacket<{0}>);
_handler.Add((ushort)PacketID.{0}, PacketHandler.{0}Handler);
";
.
.
.
}
기존의 PacketFormat에 서버와 클라이언트에 맞는 PacketManager를 생성하기 위한 규격을 추가하였고,
마찬가지로 Register를 위한 규격을 따로 추가하였습니다.
3) PacketGenerator
class PacketGenerator
{
.
.
.
static string serverRegister;
static string clientRegister;
static void Main(string[] args)
{
.
.
.
using (XmlReader reader = XmlReader.Create(pdlPath, settings))
{
.
.
.
string FileText = string.Format(PacketFormat.fileFormat, packetEnums, genPackets);
File.WriteAllText("GenPackets.cs", FileText);
string clientManagerText = string.Format(PacketFormat.managerFormat, clientRegister);
File.WriteAllText("ClientPacketManager.cs", clientManagerText);
string serverManagerText = string.Format(PacketFormat.managerFormat, serverRegister);
File.WriteAllText("ServerPacketManager.cs", serverManagerText);
}
}
// XML 파일이 패킷 구조를 정의한 XML인지 판단하는 함수
static public void ParsePacket(XmlReader reader)
{
.
.
.
if (packetName.StartsWith("S_") || packetName.StartsWith("s_"))
{
clientRegister += string.Format(
PacketFormat.managerRegisterFormat,
packetName
) + Environment.NewLine;
}
else
{
serverRegister += string.Format(
PacketFormat.managerRegisterFormat,
packetName
) + Environment.NewLine;
}
}
.
.
.
}
PacketGenerator에 ServerPacketManager와 ClientPacketManager를 위한 변수를 추가하고
각각의 스크립트를 만들기 위한 과정을 추가하였습니다.
4) GeneratePacket.bat
.
.
.
XCOPY /Y ClientPacketManager.cs "../../DummyClient/Packet"
XCOPY /Y ServerPacketManager.cs "../../Server/Packet"
패킷을 자동생성하는 실행파일의 코드에 위와 같이 코드를 추가하였습니다.
생성한 각각의 PacketManager.cs 파일을 인자로 넣어준 경로에 복사하게 됩니다.
5) Client - PacketHandler
class PacketHandler
{
// 인자는 어떤 세션에서 , 어떤 패킷이 조립되었는지를 의미함
public static void S_TestHandler(PacketSession session, IPacket packet)
{
}
}
서버에서 온 패킷을 클라이언트에서 오는 S_ 가 수식된 패킷을 받는 경우
처리하기 위한 PacketHandler를 구성하였습니다.
또한 기존의 Client에 포함된 ServerSession에서 패킷의 이름 차이로 발생하는 에러를 없애고자
ServerSession 코드에서 사용하는 기존의 패킷 이름을 전부 교체하였습니다.
6) Server - PacketHandler
class PacketHandler
{
// 인자는 어떤 세션에서 , 어떤 패킷이 조립되었는지를 의미함
public static void C_PlayerInfoRequireHandler(PacketSession session, IPacket packet)
{
C_PlayerInfoRequire playerInfoRequire = packet as C_PlayerInfoRequire;
Console.WriteLine($"{playerInfoRequire._name}`s PlayerID is {playerInfoRequire._playerID}");
foreach (C_PlayerInfoRequire.Stat stat in playerInfoRequire._statList)
{
Console.WriteLine($"{playerInfoRequire._name}`s StatID is {stat._statID}");
Console.WriteLine($"{playerInfoRequire._name}`s Level is {stat._level}");
Console.WriteLine($"{playerInfoRequire._name}`s ConnectDuration is {stat._connectDuration}");
}
}
}
서버 역시 클라이언트에서 온 C_ 가 수식된 패킷을 처리하기 위한 PacketHandler를 만들었습니다.
PacketGenerator를 빌드 -> bat 파일을 실행한 후
최종적으로 솔루션을 빌드한 후의 최종 결과는 다음과 같습니다.
'C#' 카테고리의 다른 글
채팅 테스트 #2 (0) | 2025.01.21 |
---|---|
채팅 테스트 #1 (0) | 2025.01.21 |
Packet Generator #5 (0) | 2025.01.20 |
Packet Generator #4 (0) | 2025.01.08 |
Packet Generator #3 (0) | 2025.01.08 |