25.10.13 개발일지 / C# 명코파크 (1일차)

2025. 11. 13. 14:10·LMS 7/개발일지

1일차 계획

1. 서버 - 클라이언트와 공유하는 공통 프로토콜에 대한 디렉토리 정리

2. 클라이언트와 서버 간 공통 프로토콜 정의


1. 디렉토리 구성

MCO_PARK

└ MCO_PARK.sln(솔루션)

└ CommonProtocol(공통 프로토콜)

└ 추가 예정

└ Server(서버)

└ 추가 예정

└ Client(클라이언트)

└ 추가 예정


2. 공통 프로토콜 정의

CommonProtocol

 

<MessageType.cs>

namespace CommonProtocol
{
    // 클라이언트-서버 간 주고 받을 메시지 타입을 정의
    public enum MessageType : uint
    {
        LOGIN_REQ = 1, // 로그인 요청
        LOGIN_ACK, // 로그인 요청에 대한 회답
        INPUT, // 클라이언트의 키 입력(좌우 및 점프)
        STATE_SHORT, // 상태 전파(짧은 주기, 간단히)
        STATE_LONG, // 상태 전파(긴 주기, 복잡하게)
        STAR_GET, // 별 획득
        DOOR_OPEN, // 문 열림
        GAME_CLEAR, // 게임 클리어
        ERROR // 에러
    }
}

 

<Header.cs>

using System;

namespace CommonProtocol
{
    // 헤더를 초기화 및 바이트를 담은 버퍼를 반환 받는 메서드 정의
    public class Header
    {
        public uint MsgId {  get; set; } // 메시지 ID(순서에 따라 증가)
        public MessageType MsgType { get; set; } // 메시지 타입(구별요소)
        public uint BodyLength { get; set; } // 바디 길이(실제 메시지 길이)

        public Header() { }

        public Header(byte[] bytes) // 각 필드 초기화
        {
            MsgId = BitConverter.ToUInt32(bytes, 0);
            MsgType = (MessageType)BitConverter.ToUInt32(bytes, 4);
            BodyLength = BitConverter.ToUInt32(bytes, 8);
        }

        public byte[] GetBytes() // 각 필드에 대한 바이트를 담은 버퍼를 반환
        {
            byte[] buf = new byte[12];
            Array.Copy(BitConverter.GetBytes(MsgId), 0, buf, 0, 4);
            Array.Copy(BitConverter.GetBytes((uint)MsgType), 0, buf, 4, 4);
            Array.Copy(BitConverter.GetBytes(BodyLength), 0, buf, 8, 4);
            return buf;
        }
    }
}

 

<Packet.cs>

using System.Text;
using System.Text.Json;
using System.Net.Sockets;

namespace CommonProtocol
{
    // Header와 Body를 구분하여 Send 및 Read를 하는 유틸 메서드 정의
    public static class Packet
    {
        // 전송을 위한 유틸 메서드
        public static async Task SendAsync(NetworkStream stream, MessageType type, object bodyObj, uint msgId = 0)
        {
            // 바디 바이트를 먼저 생성
            string json = JsonSerializer.Serialize(bodyObj);
            byte[] bodyBytes = Encoding.UTF8.GetBytes(json);

            // 구한 바디의 길이와 함께 헤더를 초기화
            var header = new Header
            {
                MsgId = msgId,
                MsgType = type,
                BodyLength = (uint)bodyBytes.Length
            };
            // 헤더 바이트도 생성
            byte[] headerBytes = header.GetBytes();

            // 비동기적으로 헤더와 바디를 전송
            await stream.WriteAsync(headerBytes, 0, headerBytes.Length);
            await stream.WriteAsync(bodyBytes, 0, bodyBytes.Length);
        }

        // 수신을 위한 메서드
        // 수신헬퍼 메서드(ReadExactAsync)를 호출 후 헤더와 바디로 파싱하여 반환함
        public static async Task<(Header, string)> ReceiveAsync(NetworkStream stream)
        {
            byte[] headerBuf = new byte[12];
            await ReadExactAsync(stream, headerBuf, 12);
            var header = new Header(headerBuf);

            byte[] bodyBuf = new byte[header.BodyLength];
            await ReadExactAsync(stream, bodyBuf, (int)header.BodyLength);
            string body = Encoding.UTF8.GetString(bodyBuf);

            return (header, body);
        }
        // 정해진 크기(size)만큼 반복해서 실제 수신하는 헬퍼 메서드
        public static async Task ReadExactAsync(NetworkStream stream, byte[] buf, int size)
        {
            int read = 0;
            while (read < size)
            {
                int r = await stream.ReadAsync(buf, read, size - read);
                if (r == 0) throw new Exception("연결 끊김");
                read += r;
            }
        }
    }
}

 

<PlayerState.cs>

namespace CommonProtocol
{
    // 플레이어의 상태를 나타내는 클래스 정의
    public class PlayerState
    {
        public int Id { get; set; } // ID(유저 실 구별요소)
        public string Nickname { get; set; } = ""; // 닉네임
        public float x { get; set; } // 유저의 x 좌표
        public float y { get; set; } // 유저의 y 좌표
        public bool HasStar { get; set; } // 유저의 별 보유여부
        public bool IsCleared { get; set; } // 유저의 클리어 여부
    }
}

 

<MapState.cs>

namespace CommonProtocol
{
    // 맵 상태 나타내는 클래스 정의
    public class MapState
    {
        public Door Door { get; set; } = new();
        public List<Star> Stars { get; set; } = new();
    }
    // 문 상태
    public class Door
    {
        public float x { get; set; } // 문의 x 좌표
        public float y { get; set; } // 문의 y 좌표
        public bool IsOpen { get; set; } // 문의 열림 상태
    }
    // 별 상태
    public class Star
    {
        public int Id { get; set; } // 별의 ID(실 구별요소)
        public float x { get; set; } // 별의 x 좌표
        public float y { get; set; } // 별의 y 좌표
        public bool IsTaken { get; set; } // 별이 획득됐는지 여부
        public int? OwnerId { get; set; } // 별을 누가 가지고 있는지 여부
    }
}

 

<KeyState.cs>

namespace CommonProtocol
{
    // 유저가 누른 키의 상태를 나타내는 클래스 정의
    public class KeyState
    {
        public bool Left { get; set; } // 왼쪽 키
        public bool Right { get; set; } // 오른쪽 키
        public bool Jump { get; set; } // 점프 키
    }
}

 

<GameEvent.cs>

namespace CommonProtocol
{
    // 별 획득, 문 열림, 게임 클리어에 대한 이벤트 클래스 정의
    public abstract class GameEvent // 상속될 추상클래스
    {
        public string Type { get; set; } = "";
        public int ServerTick { get; set; }
    }

    public class StarGetEvent : GameEvent // 별 획득에 따른 이벤트
    {
        public int PlayerId { get; set; }
        public int StarId { get; set; }

        public StarGetEvent(int tick, int pid, int sid)
        {
            Type = "STAR_GET";
            ServerTick = tick;
            PlayerId = pid;
            StarId = sid;
        }
    }

    public class DoorOpenEvent : GameEvent // 문 열림에 따른 이벤트
    {
        public int OpnerId { get; set; }
        public DoorOpenEvent(int tick, int id)
        {
            Type = "DOOR_OPEN";
            ServerTick = tick;
            OpnerId = id;
        }
    }

    public class GameClearEvent : GameEvent // 게임 클리어에 따른 이벤트
    {
        public GameClearEvent(int tick)
        {
            Type = "GAME_CLEAR";
            ServerTick = tick;
        }
    }
}

Git 초기커밋

https://github.com/mmmmz986/MCO_PARK.git


내일 Packet 부분은 Body랑 Util로 쪼개야 할 듯

'LMS 7 > 개발일지' 카테고리의 다른 글

25.10.15 개발일지 / C# 명코파크 (3일차)  (0) 2025.11.13
25.10.14 개발일지 / C# 명코파크 (2일차)  (0) 2025.11.13
25.10.09 개발일지 / C# TRex - TCP IP 네트워크 구상  (0) 2025.11.13
25.10.08 개발일지 / C# T-Rex Endless Runner - 3 / 주요 메서드 정리 및 영상  (0) 2025.11.13
25.10.07 개발일지 / C# T-Rex Endless Runner - 2 / 이벤트, 필드, 메서드 등 멤버 정의  (0) 2025.11.13
'LMS 7/개발일지' 카테고리의 다른 글
  • 25.10.15 개발일지 / C# 명코파크 (3일차)
  • 25.10.14 개발일지 / C# 명코파크 (2일차)
  • 25.10.09 개발일지 / C# TRex - TCP IP 네트워크 구상
  • 25.10.08 개발일지 / C# T-Rex Endless Runner - 3 / 주요 메서드 정리 및 영상
m_Dev
m_Dev
  • m_Dev
    m_Dev
    m_Dev
  • 전체
    오늘
    어제
    • 분류 전체보기
      • MAIN STUDY
        • 정보보안
        • 빅데이터
        • 정보처리
        • 컴퓨터 구조
        • 기타
      • JOB
        • Study
        • Project
      • LMS 7
        • 개발일지
      • FRAMEWORK
        • Qt
        • MFC
        • Winform
        • WPF
        • MAUI
      • NETWORK
        • Study
        • Assignment
      • PYTHON
        • Set
        • Study
        • Assignment
        • Project
      • C
        • Set
        • Study
        • Assignment
        • Project
      • C++
        • Set
        • Study
        • Assignment
        • Project
      • C#
        • Set
        • Study
        • Assignment
        • Project
      • DATABASE
        • MySQL
        • SQLite
      • IDE
        • VisualStudioCode
        • VisualStudio
        • Pycharm
        • Colab
      • 기타
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
m_Dev
25.10.13 개발일지 / C# 명코파크 (1일차)
상단으로

티스토리툴바