PacketGenerator #1

Eunho Bae·2022년 5월 6일
0
  • PDL.xml에 패킷 구조를 정의해두고 파싱할 예정 (Packet Definition List)
  • 자동화 코드를 만들 때
    1) 하드코딩
    2) 재사용 할 수 있는 부분 찾기
    3) 자동화하기

코드

PacketGenerator

PDL.xml

<?xml version="1.0" encoding="utf-8" ?>
<PDL>
	<packet name="PlayerInfoReq">
		<long name="playerId"/>
		<string name="name"/>
		<list name="skill">
			<int name="id"/>
			<short name="level"/>
			<float name="duration"/>
		</list>
	</packet>
</PDL>

PacketFormat.cs

 class PacketFormat
    {
		// @: 여러 줄에 걸쳐서 문자열 입력하고 싶을때
		// {0} : 패킷이름
		// {1} : 멤버 변수들
		// {2} : 멤버 변수 Read
		// {3} : 멤버변수 Write
		public static string packetFormat =
@"
class {0}
{{
	{1}	

    public void Read(ArraySegment<byte> segment)
    {{
		ushort pos = 0;

		ReadOnlySpan<byte> s = new ReadOnlySpan<byte>(segment.Array, segment.Offset, segment.Count);

		pos += sizeof(ushort);
		pos += sizeof(ushort);

		{2}
	}}

    public ArraySegment<byte> Write()
    {{
		ArraySegment<byte> segment = SendBufferHelper.Open(4096); // SendBuffer의 _buffer

		ushort size = 0;
		bool success = true;

		Span<byte> s = new Span<byte>(segment.Array, segment.Offset, segment.Count);

		size += sizeof(ushort); // 2 : 패킷 size 사이즈
		success &= BitConverter.TryWriteBytes(s.Slice(size, s.Length - size), (ushort)PacketID.{0}); // Slice가 Span<byte> 만들어서 반환하면 packetId를 그 범위에 바이트로 변환해서 씀
		size += sizeof(ushort); // packetId 사이즈

		{3}
		
		success &= BitConverter.TryWriteBytes(s, size);

		if (success == false)
			return null; // 반환해서 밖에서 null체크

		return SendBufferHelper.Close(size); // 최종 패킷 크기
	}}
}}
";

		// {0} 변수 형식
		// {1} 변수 이름
		public static string memberFormat =
@"public {0} {1}";

		// {0} : 변수 이름
		// {1} : To~ 변수 형식
		// {2} : 변수 형식
		public static string readFormat =
@"this.{0} = BitConverter.{1}(s.Slice(pos, s.Length - pos));
pos += sizeof({2});";

		// {0} 변수 이름
		public static string readStringFormat =
@"ushort {0}Len = BitConverter.ToUInt16(s.Slice(count, s.Length - count));
count += sizeof(ushort);
this.{0} = Encoding.Unicode.GetString(s.Slice(count, {0}Len));
count += {0}Len;";

		// {0} 변수 이름
		// {1} 변수 형식
		public static string writeFormat =
@"success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), this.{0});
count += sizeof({1});";

		// {0} 변수 이름
		public static string writeStringFormat =
@"ushort {0}Len = (ushort)Encoding.Unicode.GetBytes(this.{0}, 0, this.{0}.Length, segment.Array, segment.Offset + count + sizeof(ushort));
success &= BitConverter.TryWriteBytes(s.Slice(count, s.Length - count), {0}Len);
count += sizeof(ushort);
count += {0}Len;";
	}

Program.cs

   class Program
    {
        static void Main(string[] args)
        {
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                IgnoreComments = true, // 주석 무시
                IgnoreWhitespace = true // 스페이스바 무시
            };

            // using으로 감싸주면 중괄호 빠져나갈때 자동으로 Dispose 호출
            using (XmlReader r = XmlReader.Create("PDL.xml", settings))
            {
                r.MoveToContent(); // content 노드로 순간이동. 코멘트나 스페이스 등은 건너뜀

                while(r.Read()) // 줄 단위로 string으로 읽어들임
                {
                    if (r.Depth == 1 && r.NodeType == XmlNodeType.Element) // packet의 depth는 1이 될 것. (<PDL> 밑줄). Element는 시작부분 <packet ...> EndElement는 </packet>
                        ParsePacket(r);

                    // Console.WriteLine(r.Name + " " + r["name"]);
                }
            }
        }

        public static void ParsePacket(XmlReader r)
        {
            if (r.NodeType == XmlNodeType.EndElement)
                return;

            if (r.Name.ToLower() != "packet")
            {
                Console.WriteLine("Packet 노드 invaild");
                return;
            }

            string packetName = r["name"]; // PlayerInfoReq가 들어감

            if(string.IsNullOrEmpty(packetName))
            {
                Console.WriteLine("Packet 이름 없음");
                return;
            }

            ParseMembers(r);
        }

        public static void ParseMembers(XmlReader r)
        {
            string packetName = r["name"];
            int depth = r.Depth + 1; // <long name = "playerId"/> 부분으로

            while(r.Read())
            {
                if (r.Depth != depth) // </packet>에 다다르면
                    break;

                string memberName = r["name"];
                if(string.IsNullOrEmpty(memberName))
                {
                    Console.WriteLine("member 이름 없음");
                    return;
                }

                string memberType = r.Name.ToLower();
                switch(memberType) // case에 따라 자동화를 어떻게 해줄지 고민
                {
                    case "bool":
                    case "byte":
                    case "short":
                    case "ushort":
                    case "int":
                    case "long":
                    case "float":
                    case "double":
                    case "string":
                    case "list":
                        break;
                    default:
                        break;
                }
            }
        }
    }
profile
개인 공부 정리

0개의 댓글

관련 채용 정보