동일한 인터페이스를 갖는 패킷과 해당 패킷의 처리 함수들을 자동으로 생성하는 프로그램
<PDL>
<packet name="C_PlayerInfoReq">
<byte name="testByte"/>
<long name="playerID"/>
<string name="name"/>
<list name="skill">
<int name="id"/>
<short name="level"/>
<float name="duration"/>
<list name="attribute">
<int name="value"/>
</list>
</list>
</packet>
<packet name="S_Test">
<int name="testInt"/>
</packet>
</PDL>
xml이나 json 파일에 패킷의 데이터 구조를 정의해 두고
해당 파일을 파싱하여 생성 시작함
c#의 경우 xml 라이브러리를 내장하고 있어 xml을 사용하는 것이 편함
클라이언트 단에서 송신하는 패킷은 "C", 서버 단에서 송신하는 패킷은 "S"를 접두사를 가짐
이를 통해 클라이언트와 서버의 패킷 코드를 이원화하여 보안 강화
using (XmlReader r = XmlReader.Create(pdlPath, settings))
{
r.MoveToContent();
while (r.Read())
{
if (r.Depth == 1 && r.NodeType == XmlNodeType.Element)
{
ParsePacket(r);
}
}
File.WriteAllText("GenPackets.cs",
string.Format(PacketFormat.fileFormat, packetEnums, genPackets));
File.WriteAllText("ClientPacketManager.cs",
string.Format(PacketFormat.managerFormat, clientRegister));
File.WriteAllText("ServerPacketManager.cs",
string.Format(PacketFormat.managerFormat, serverRegister));
}
생성해야할 클래스들의 코드 데이터를 string으로 저장해둔 후, WriteAllText을 사용하여 생성
// {0} 패킷 이름과 번호 목록
// {1} 패킷 목록
public static string fileFormat =
@"using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using ServerCore;
public enum PacketID
{{
{0}
}}
interface IPacket
{{
ushort Protocol {{ get; }}
void Read(ArraySegment<byte> segment);
ArraySegment<byte> Write();
}}
{1}
";
위와 같이 패킷마다 달라지는 부분들을 포맷팅을 사용하여 넣어줄 수 있도록 미리 정의 해둠
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()
{
_onRecv.Add((ushort)PacketID.C_PlayerInfoReq, MakePacket<C_PlayerInfoReq>);
_handler.Add((ushort) PacketID.C_PlayerInfoReq, PacketHandler.C_PlayerInfoReqHandler);
}
public void OnRecvPacket(PacketSession session, ArraySegment<byte> buffer)
{
ushort count = 0;
ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset);
count += 2;
ushort packetID = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count);
count += 2;
if (_onRecv.TryGetValue(packetID, out var action))
{
action.Invoke(session, buffer);
}
}
void MakePacket<T>(PacketSession session, ArraySegment<byte> buffer) where T: IPacket, new()
{
T packet = new T();
packet.Read(buffer);
if (_handler.TryGetValue(packet.Protocol, out var action))
{
action.Invoke(session, packet);
}
}
자동으로 생성된 패킷은 마찬가지로 자동 생성된 패킷 매니저 클래스가 처리할 수 있도록 함
_onRecv에는 패킷 id를 가지고 받았을 때 콜백할 함수를 등록해두고,
_handler에는 실제적으로 패킷을 처리할 함수를 등록하여 사용
PlayerInfoReq 요청을 처리하는 C_PlayerInfoReqHandler 함수부터 사람이 코딩
START ../../PacketGenerator/bin/PacketGenerator.exe ../../PacketGenerator/PDL.xml
XCOPY /Y GenPackets.cs "../../DummyClient/Packet"
XCOPY /Y GenPackets.cs "../../Server/Packet"
XCOPY /Y ClientPacketManager.cs "../../DummyClient/Packet"
XCOPY /Y ServerPacketManager.cs "../../Server/Packet"
batch 파일을 이용하여 client와 server 코드 폴더 각각에
생성된 코드들을 자동으로 복사할 수 있도록 함