C#

Packet Generator #6

monstro 2025. 1. 20. 19:47
728x90
반응형

이번 포스트에서는 이전의 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 파일을 실행한 후

최종적으로 솔루션을 빌드한 후의 최종 결과는 다음과 같습니다.

728x90
반응형

'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