From cd192d4ec4c9cdd374c60e4187f57e64719b004e Mon Sep 17 00:00:00 2001 From: bumpsoo Date: Sun, 8 Feb 2026 14:59:16 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20c++=20pragma=20=EB=B0=A9=EC=8B=9D=20=3D?= =?UTF-8?q?>=20protocol=20buffer=EB=A1=9C=20=EC=A7=81=EB=A0=AC=ED=99=94=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=ED=81=B4=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=96=B8=ED=8A=B8=20python=20gui=20=EC=95=B1=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 23 ++-- Dockerfile | 3 +- client/.gitignore | 3 + client/Client.cpp | 233 --------------------------------------- client/README.md | 19 ++++ client/main.cpp | 44 -------- client/main.py | 196 ++++++++++++++++++++++++++++++++ client/requirements.txt | 4 + include/Client.h | 21 ++-- include/Packet.h | 42 ------- include/Session.h | 9 +- proto/Protocol.proto | 31 ++++++ server/PacketHandler.cpp | 42 +++---- setup.sh | 6 +- 14 files changed, 305 insertions(+), 371 deletions(-) create mode 100644 client/.gitignore delete mode 100644 client/Client.cpp create mode 100644 client/README.md delete mode 100644 client/main.cpp create mode 100644 client/main.py create mode 100644 client/requirements.txt create mode 100644 proto/Protocol.proto diff --git a/CMakeLists.txt b/CMakeLists.txt index 26f9ed8..39a12bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,10 +9,17 @@ add_compile_options(-Wall -Wextra -Wpedantic) # Boost 및 의존성 찾기 find_package(Boost REQUIRED COMPONENTS system) find_package(OpenSSL REQUIRED) +find_package(Protobuf REQUIRED) # 컴파일 명령 추출 활성화 (LSP/IDE 용) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# proto 컴파일 +file(GLOB PROTO_FILES "${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto") +protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES}) + +add_custom_target(generate_proto DEPENDS ${PROTO_SRCS} ${PROTO_HDRS}) + add_executable(Server server/main.cpp server/NetworkService.cpp @@ -20,14 +27,14 @@ add_executable(Server server/PacketHandler.cpp server/SessionManager.cpp server/DatabaseManager.cpp + ${PROTO_SRCS} ${PROTO_HDRS} ) -target_include_directories(Server PRIVATE include) -target_link_libraries(Server PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto) - -add_executable(Client - client/Client.cpp - client/main.cpp +target_include_directories(Server PRIVATE include ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(Server PRIVATE + Boost::system + OpenSSL::SSL + OpenSSL::Crypto + protobuf::libprotobuf ) -target_include_directories(Client PRIVATE include) -target_link_libraries(Client PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto) + diff --git a/Dockerfile b/Dockerfile index 4a19a78..9146547 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,8 @@ RUN apt-get update && apt-get install -y \ make \ g++ \ libboost-all-dev \ - libssl-dev + libssl-dev \ + protobuf-compiler COPY . /app WORKDIR /app diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..ff5a986 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,3 @@ +Protocol_pb2.py +venv +__pycache__ \ No newline at end of file diff --git a/client/Client.cpp b/client/Client.cpp deleted file mode 100644 index 3c7f413..0000000 --- a/client/Client.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include "Client.h" -#include -#include - -BaseClient::BaseClient(boost::asio::io_context &io_context, - const std::string &host, const std::string &port) - : socket_(io_context) { - tcp::resolver resolver(io_context); - boost::asio::connect(socket_, resolver.resolve(host, port)); -} - -void BaseClient::SendPacket(PacketID id, const void *payload, - size_t payloadSize) { - PacketHeader header; - header.id = static_cast(id); - header.size = static_cast(payloadSize); - - std::vector buffer(sizeof(PacketHeader) + payloadSize); - std::memcpy(buffer.data(), &header, sizeof(PacketHeader)); - if (payload && payloadSize > 0) { - std::memcpy(buffer.data() + sizeof(PacketHeader), payload, payloadSize); - } - - boost::asio::write(socket_, boost::asio::buffer(buffer)); -} - -bool BaseClient::ReceiveHeader(PacketHeader &header) { - try { - boost::asio::read(socket_, boost::asio::buffer(&header, sizeof(header))); - return true; - } catch (...) { - return false; - } -} - -bool BaseClient::Login(const std::string &nickname) { - PKT_CS_Login loginPkt; - std::memset(loginPkt.nickname, 0, sizeof(loginPkt.nickname)); - std::strncpy(loginPkt.nickname, nickname.c_str(), - sizeof(loginPkt.nickname) - 1); - SendPacket(PacketID::CS_Login, &loginPkt, sizeof(loginPkt)); - std::cout << "로그인 요청 보냄: " << nickname << std::endl; - - // 로그인 결과 대기 - PacketHeader header; - if (!ReceiveHeader(header) || - header.id != static_cast(PacketID::SC_LoginResult)) { - std::cerr << "로그인 응답을 받지 못했습니다." << std::endl; - return false; - } - - PKT_SC_LoginResult res; - if (!ReceivePayload(res)) { - std::cerr << "로그인 응답 수신 실패" << std::endl; - return false; - } - - if (res.result == 1) { - std::cout << "로그인 성공!" << std::endl; - return true; - } else if (res.result == 0) { - std::cerr << "로그인 실패: 이미 접속 중인 유저입니다." << std::endl; - return false; - } else { - std::cerr << "로그인 실패: 알 수 없는 오류" << std::endl; - return false; - } -} - -void BaseClient::StartReceive() { - receiveThread_ = std::jthread(&BaseClient::ReceiveLoop, this); -} - -void BaseClient::StopReceive() { - boost::system::error_code ec; - socket_.shutdown(tcp::socket::shutdown_both, ec); - socket_.close(ec); - receiveThread_.request_stop(); -} - -void BaseClient::ReceiveLoop(std::stop_token stopToken) { - try { - while (!stopToken.stop_requested()) { - PacketHeader header; - if (!ReceiveHeader(header)) - break; - HandlePacket(header); - } - } catch (...) { - } -} - -void BaseClient::HandlePacket(const PacketHeader &header) { - PacketID id = static_cast(header.id); - switch (id) { - case PacketID::Chat: { - std::vector buffer(header.size + 1, 0); - boost::asio::read(socket_, boost::asio::buffer(buffer.data(), header.size)); - std::cout << "\n" << buffer.data() << std::endl; - } break; - case PacketID::SC_UpgradeResult: - HandleUpgradeResult(); - break; - case PacketID::SC_SellResult: - HandleSellResult(); - break; - default: - // 처리하지 않는 패킷은 페이로드만 건너뜀 - if (header.size > 0) { - std::vector dummy(header.size); - boost::asio::read(socket_, - boost::asio::buffer(dummy.data(), header.size)); - } - break; - } -} - -void BaseClient::HandleUpgradeResult() { - PKT_SC_UpgradeResult res; - if (ReceivePayload(res)) { - currentLevel_ = res.currentLevel; - currentGold_ = res.currentGold; - // 강화 결과 출력은 채팅으로 - } -} - -void BaseClient::HandleSellResult() { - PKT_SC_SellResult res; - if (ReceivePayload(res)) { - currentLevel_ = 0; - currentGold_ = res.totalGold; - // 판매 결과는 본인에게만 - std::cout << "\n>> 판매 완료: " << res.earnedGold << " 골드 획득!" - << std::endl; - } -} - -void InteractiveClient::Run() { - std::cout << "닉네임을 입력하세요: "; - std::cin >> nickname_; - if (!Login(nickname_)) { - std::cerr << "로그인에 실패하여 종료합니다." << std::endl; - return; - } - - // 로그인 후 비동기 수신 시작 - StartReceive(); - - while (true) { - if (currentLevel_ == 0) { - std::cout << "\n[현재 검 없음, 골드: " << currentGold_ - << "] 1. 검 구매 시도 (1000골드), 2. 종료\n입력: "; - } else { - std::cout << "\n[현재 검 레벨: " << currentLevel_ - << ", 골드: " << currentGold_ - << "] 1. 강화 시도, 2. 판매, 3. 종료\n입력: "; - } - - int choice; - if (!(std::cin >> choice)) - break; - - if (currentLevel_ == 0) { - if (choice == 1) { - SendPacket(PacketID::CS_UpgradeSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 2) { - break; - } - } else { - if (choice == 1) { - SendPacket(PacketID::CS_UpgradeSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 2) { - SendPacket(PacketID::CS_SellSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 3) { - break; - } - } - } - - StopReceive(); -} - -#include - -void ScenarioClient::Run() { - std::ifstream is(scenarioFile_); - if (!is.is_open()) { - std::cerr << "시나리오 파일을 열 수 없습니다: " << scenarioFile_ - << std::endl; - return; - } - - if (!(is >> nickname_)) - return; - if (!Login(nickname_)) { - std::cerr << "로그인에 실패하여 종료합니다." << std::endl; - return; - } - - // 비동기 수신 시작 - StartReceive(); - - std::string line; - while (is >> line) { - int choice = std::stoi(line); - std::cout << "\n시나리오 입력 실행: " << choice << std::endl; - - if (currentLevel_ == 0) { - if (choice == 1) { - SendPacket(PacketID::CS_UpgradeSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 2) { - break; - } - } else { - if (choice == 1) { - SendPacket(PacketID::CS_UpgradeSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 2) { - SendPacket(PacketID::CS_SellSword); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } else if (choice == 3) { - break; - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - StopReceive(); -} diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..02253ea --- /dev/null +++ b/client/README.md @@ -0,0 +1,19 @@ +## c++ cli 클라이언트 대신하는 python gui client + +- tkinter 사용 +- protocol buffer 사용 + +## protocol buffer 빌드 + +```bash +protoc --python_out=. Protocol.proto -I ../proto +``` + +## python 설치 및 실행 + +```bash +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +python main.py +``` \ No newline at end of file diff --git a/client/main.cpp b/client/main.cpp deleted file mode 100644 index 170acb8..0000000 --- a/client/main.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "Client.h" -#include - -int main(int argc, char *argv[]) { - bool isScenario = false; - std::string nickname; - std::string scenarioFile; - - for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; - if (arg == "--scenario" && i + 1 < argc) { - isScenario = true; - scenarioFile = argv[++i]; - } else { - nickname = arg; - } - } - - try { - boost::asio::io_context io_context; - const char *env_host = std::getenv("SERVER_HOST"); - const char *env_port = std::getenv("SERVER_PORT"); - std::string host = env_host ? env_host : "127.0.0.1"; - std::string port = env_port ? env_port : "30000"; - - if (isScenario) { - if (scenarioFile.empty()) { - std::cerr - << "시나리오 파일명이 필요합니다. 사용법: --scenario <파일경로>" - << std::endl; - return 1; - } - ScenarioClient client(io_context, host, port, scenarioFile); - client.Run(); - } else { - InteractiveClient client(io_context, host, port); - client.Run(); - } - } catch (std::exception &e) { - std::cerr << "Exception: " << e.what() << "\n"; - } - - return 0; -} diff --git a/client/main.py b/client/main.py new file mode 100644 index 0000000..10df3b2 --- /dev/null +++ b/client/main.py @@ -0,0 +1,196 @@ +import socket +import threading +import struct +import customtkinter as ctk +from enum import IntEnum +import Protocol_pb2 as Protocol + +class PacketID(IntEnum): + Ping = 1 + CS_Login = 10 + SC_LoginResult = 11 + Chat = 20 + CS_UpgradeSword = 30 + SC_UpgradeResult = 31 + CS_SellSword = 35 + SC_SellResult = 36 + CS_RankingRequest = 40 + SC_RankingList = 41 + +class SwordGameClient(ctk.CTk): + def __init__(self): + super().__init__() + + self.title("C++ Sword Game - Python Client") + self.geometry("1200x800") + ctk.set_appearance_mode("dark") + + # 네트워크 관련 + self.socket = None + self.connected = False + self.nickname = "" + + # 게임 정보 + self.current_level = 0 + self.current_gold = 0 + + # UI 초기화 + self.setup_login_ui() + + def setup_login_ui(self): + self.login_frame = ctk.CTkFrame(self) + self.login_frame.pack(expand=True) + + self.label = ctk.CTkLabel(self.login_frame, text="검 키우기 온라인", font=("Arial", 24, "bold")) + self.label.pack(pady=20) + + self.nickname_entry = ctk.CTkEntry(self.login_frame, placeholder_text="닉네임 입력") + self.nickname_entry.pack(pady=10) + + self.login_button = ctk.CTkButton(self.login_frame, text="접속", command=self.connect_to_server) + self.login_button.pack(pady=20) + + def setup_game_ui(self): + for widget in self.winfo_children(): + widget.destroy() + + # 메인 레이아웃 + self.grid_columnconfigure(0, weight=1) + self.grid_columnconfigure(1, weight=2) + self.grid_rowconfigure(0, weight=1) + + # 왼쪽: 유저 정보 창 + self.info_frame = ctk.CTkFrame(self) + self.info_frame.grid(row=0, column=0, padx=20, pady=20, sticky="nsew") + + self.user_label = ctk.CTkLabel(self.info_frame, text=f"플레이어: {self.nickname}", font=("Arial", 16)) + self.user_label.pack(pady=10) + + self.gold_label = ctk.CTkLabel(self.info_frame, text="골드: 0", font=("Arial", 18, "bold"), text_color="#FFD700") + self.gold_label.pack(pady=5) + + self.level_label = ctk.CTkLabel(self.info_frame, text="검 레벨: 0", font=("Arial", 20, "bold")) + self.level_label.pack(pady=10) + + self.upgrade_btn = ctk.CTkButton(self.info_frame, text="강화 시도", fg_color="#28a745", hover_color="#218838", command=self.send_upgrade) + self.upgrade_btn.pack(pady=10, fill="x", padx=20) + + self.sell_btn = ctk.CTkButton(self.info_frame, text="판매 하기", fg_color="#dc3545", hover_color="#c82333", command=self.send_sell) + self.sell_btn.pack(pady=10, fill="x", padx=20) + + # 오른쪽: 채팅 창 + self.chat_frame = ctk.CTkFrame(self) + self.chat_frame.grid(row=0, column=1, padx=20, pady=20, sticky="nsew") + + self.chat_box = ctk.CTkTextbox(self.chat_frame, state="disabled") + self.chat_box.pack(expand=True, fill="both", padx=10, pady=10) + + self.chat_entry = ctk.CTkEntry(self.chat_frame, placeholder_text="메시지 입력...") + self.chat_entry.pack(fill="x", padx=10, pady=(0, 10)) + self.chat_entry.bind("", lambda e: self.send_chat()) + + # TODO: 채팅창 오른 쪽에 랭킹? + + def connect_to_server(self): + self.nickname = self.nickname_entry.get() + if not self.nickname: + return + + try: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect(("127.0.0.1", 30000)) + self.connected = True + + threading.Thread(target=self.receive_loop, daemon=True).start() + + pkt = Protocol.CS_Login() + pkt.nickname = self.nickname + self.send_packet(PacketID.CS_Login, pkt) + + except Exception as e: + print(f"Connection failed: {e}") + self.nickname_entry.configure(placeholder_text="연결 실패!") + + def send_packet(self, packet_id, protobuf_obj=None): + if not self.socket: return + + payload = b"" + if protobuf_obj: + payload = protobuf_obj.SerializeToString() + + # Header: size (2 bytes) + id (2 bytes), Little Endian + header = struct.pack(' 0: + payload = self.socket.recv(size) + + self.handle_packet(pkt_id, payload) + except Exception as e: + print(f"Receive error: {e}") + finally: + self.connected = False + + def handle_packet(self, pkt_id, payload): + if pkt_id == PacketID.SC_LoginResult: + res = Protocol.SC_LoginResult() + res.ParseFromString(payload) + if res.success: + self.after(0, self.setup_game_ui) + else: + print("Login Failed") + + elif pkt_id == PacketID.SC_UpgradeResult: + res = Protocol.SC_UpgradeResult() + res.ParseFromString(payload) + self.current_level = res.current_level + self.current_gold = res.current_gold + self.after(0, self.update_stats) + + elif pkt_id == PacketID.SC_SellResult: + res = Protocol.SC_SellResult() + res.ParseFromString(payload) + self.current_level = 0 + self.current_gold = res.total_gold + self.after(0, self.update_stats) + self.after(0, lambda: self.log_chat(f"[시스템] 검 판매 완료! {res.earned_gold} 골드 획득.")) + + elif pkt_id == PacketID.Chat: + msg = payload.decode('utf-8') + self.after(0, lambda: self.log_chat(msg)) + + def update_stats(self): + self.level_label.configure(text=f"검 레벨: {self.current_level}") + self.gold_label.configure(text=f"골드: {self.current_gold:,}") + + def send_upgrade(self): + self.send_packet(PacketID.CS_UpgradeSword) + + def send_sell(self): + self.send_packet(PacketID.CS_SellSword) + + def send_chat(self): + msg = self.chat_entry.get() + if msg: + header = struct.pack(' #include #include #include +namespace google::protobuf { +class Message; +} + using boost::asio::ip::tcp; class BaseClient { @@ -15,20 +18,10 @@ public: virtual ~BaseClient() = default; - void SendPacket(PacketID id, const void *payload = nullptr, - size_t payloadSize = 0); + void SendPacket(PacketID id, const google::protobuf::Message *pkt = nullptr); bool ReceiveHeader(PacketHeader &header); - template bool ReceivePayload(T &payload) { - try { - boost::asio::read(socket_, boost::asio::buffer(&payload, sizeof(T))); - return true; - } catch (...) { - return false; - } - } - bool Login(const std::string &nickname); // 비동기 수신 시작 @@ -48,8 +41,8 @@ protected: void ReceiveLoop(std::stop_token stopToken); void HandlePacket(const PacketHeader &header); - void HandleUpgradeResult(); - void HandleSellResult(); + void HandleUpgradeResult(const PacketHeader &header); + void HandleSellResult(const PacketHeader &header); }; class InteractiveClient : public BaseClient { diff --git a/include/Packet.h b/include/Packet.h index d73e4dd..a83127b 100644 --- a/include/Packet.h +++ b/include/Packet.h @@ -46,45 +46,3 @@ namespace GameConfig { const uint32_t MAX_SWORD_LEVEL = 20; const uint64_t INITIAL_GOLD = 10000; } // namespace GameConfig - -// 고정 크기 데이터 전송을 위한 구조체 정의 -#pragma pack(push, 1) -struct PKT_CS_Login { - // 유저 닉네임 (최대 32자) - char nickname[32]; -}; - -struct PKT_SC_LoginResult { - // 0: 이미 접속 중, 1: 성공 - uint8_t result; -}; - -struct PKT_SC_UpgradeResult { - // 0: 파괴, 1: 성공, 2: 실패 - uint8_t result; - // 현재 강화 레벨 - uint32_t currentLevel; - // 현재 보유 골드 - uint64_t currentGold; -}; - -struct PKT_SC_SellResult { - // 판매 후 획득 골드 - uint64_t earnedGold; - // 현재 총 골드 - uint64_t totalGold; -}; - -struct RankingEntry { - // 유저 이름 (최대 32자) - char username[32]; - // 검 레벨 - uint32_t swordLevel; -}; - -struct PKT_SC_RankingList { - // 랭킹 리스트 개수 - uint32_t count; - // 이후 RankingEntry[count] 만큼 데이터가 따라옴 -}; -#pragma pack(pop) diff --git a/include/Session.h b/include/Session.h index 9a25f19..3d7e1fc 100644 --- a/include/Session.h +++ b/include/Session.h @@ -16,14 +16,15 @@ public: void Start(); void Send(std::span data); - template void SendPacket(PacketID id, const T &payload) { + template void SendPacket(PacketID id, const T &pkt) { + uint16_t size = static_cast(pkt.ByteSizeLong()); PacketHeader header; header.id = static_cast(id); - header.size = sizeof(T); + header.size = size; - std::vector buffer(sizeof(PacketHeader) + sizeof(T)); + std::vector buffer(sizeof(PacketHeader) + size); std::memcpy(buffer.data(), &header, sizeof(PacketHeader)); - std::memcpy(buffer.data() + sizeof(PacketHeader), &payload, sizeof(T)); + pkt.SerializeToArray(buffer.data() + sizeof(PacketHeader), size); Send(buffer); } diff --git a/proto/Protocol.proto b/proto/Protocol.proto new file mode 100644 index 0000000..08eaf1e --- /dev/null +++ b/proto/Protocol.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package Protocol; + +message CS_Login { + string nickname = 1; +} + +message SC_LoginResult { + bool success = 1; +} + +message SC_UpgradeResult { + uint32 result = 1; + uint32 current_level = 2; + uint64 current_gold = 3; +} + +message SC_SellResult { + uint64 earned_gold = 1; + uint64 total_gold = 2; +} + +message RankingEntry { + string nickname = 1; + uint32 level = 2; +} + +message SC_RankingList { + repeated RankingEntry entries = 1; +} diff --git a/server/PacketHandler.cpp b/server/PacketHandler.cpp index c294e33..291d020 100644 --- a/server/PacketHandler.cpp +++ b/server/PacketHandler.cpp @@ -1,6 +1,7 @@ #include "PacketHandler.h" #include "DatabaseManager.h" #include "Logger.h" +#include "Protocol.pb.h" #include "SessionManager.h" #include "SwordLogic.h" #include @@ -17,27 +18,25 @@ PacketHandler::HandlePacket(std::shared_ptr session, } break; case PacketID::CS_Login: { - if (packet.payload.size() < sizeof(PKT_CS_Login)) { - Logger::Log("로그인 패킷 크기가 올바르지 않습니다."); + Protocol::CS_Login pkt; + if (!pkt.ParseFromArray(packet.payload.data(), packet.payload.size())) { + Logger::Log("로그인 패킷 파싱 실패"); co_return; } - const PKT_CS_Login *loginPkt = - reinterpret_cast(packet.payload.data()); - std::string nickname(loginPkt->nickname); - + std::string nickname = pkt.nickname(); auto userData = co_await DatabaseManager::GetInstance().LoadUser(nickname); // 골드와 검 레벨 설정 (닉네임은 TryJoin에서 설정) session->SetGold(userData.gold); session->SetSwordLevel(userData.swordLevel); - PKT_SC_LoginResult loginResult; + Protocol::SC_LoginResult loginResult; // 중복 체크 if (!SessionManager::GetInstance().TryJoin(session, userData.nickname)) { Logger::Log("중복 로그인 거부: ", nickname); - loginResult.result = 0; + loginResult.set_success(false); session->SendPacket(PacketID::SC_LoginResult, loginResult); co_return; } @@ -46,7 +45,7 @@ PacketHandler::HandlePacket(std::shared_ptr session, " (Gold: ", session->GetGold(), ", Level: ", session->GetSwordLevel(), ")"); - loginResult.result = 1; + loginResult.set_success(true); session->SendPacket(PacketID::SC_LoginResult, loginResult); } break; @@ -55,14 +54,14 @@ PacketHandler::HandlePacket(std::shared_ptr session, uint64_t cost = SwordLogic::GetUpgradeCost(currentLevel); uint64_t currentGold = session->GetGold(); - PKT_SC_UpgradeResult res; + Protocol::SC_UpgradeResult res; if (currentGold < cost) { // 골드 부족 - res.result = 2; + res.set_result(2); } else { session->SetGold(currentGold - cost); uint8_t result = SwordLogic::TryUpgrade(currentLevel); - res.result = result; + res.set_result(result); // 성공 if (result == 1) { @@ -76,8 +75,8 @@ PacketHandler::HandlePacket(std::shared_ptr session, session->GetNickname(), session->GetGold(), session->GetSwordLevel()); } - res.currentLevel = session->GetSwordLevel(); - res.currentGold = session->GetGold(); + res.set_current_level(session->GetSwordLevel()); + res.set_current_gold(session->GetGold()); // 결과 패킷 전송 session->SendPacket(PacketID::SC_UpgradeResult, res); @@ -86,9 +85,9 @@ PacketHandler::HandlePacket(std::shared_ptr session, if (currentGold >= cost) { uint32_t attemptedLevel = currentLevel + 1; std::string resultText; - if (res.result == 1) { + if (res.result() == 1) { resultText = "를 달성했습니다!"; - } else if (res.result == 0) { + } else if (res.result() == 0) { resultText = " 시도 중 검이 파괴되었습니다!"; } else { resultText = " 달성에 실패했습니다!"; @@ -105,8 +104,9 @@ PacketHandler::HandlePacket(std::shared_ptr session, std::vector chatPayload(chatMsg.begin(), chatMsg.end()); SessionManager::GetInstance().Broadcast(chatHeader, chatPayload); - Logger::Log("강화 시도 [", session->GetNickname(), "]: ", (int)res.result, - " (레벨: ", currentLevel, "->", res.currentLevel, ")"); + Logger::Log("강화 시도 [", session->GetNickname(), + "]: ", (int)res.result(), " (레벨: ", currentLevel, "->", + res.current_level(), ")"); } } break; @@ -121,9 +121,9 @@ PacketHandler::HandlePacket(std::shared_ptr session, co_await DatabaseManager::GetInstance().SaveUser( session->GetNickname(), session->GetGold(), session->GetSwordLevel()); - PKT_SC_SellResult res; - res.earnedGold = price; - res.totalGold = newGold; + Protocol::SC_SellResult res; + res.set_earned_gold(price); + res.set_total_gold(newGold); session->SendPacket(PacketID::SC_SellResult, res); diff --git a/setup.sh b/setup.sh index 60eaa56..737e95d 100755 --- a/setup.sh +++ b/setup.sh @@ -2,17 +2,15 @@ set -e -echo "Starting project setup..." - if [ ! -d "build" ]; then mkdir build - echo "Created build directory." fi cd build cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -echo "CMake configuration complete." +cmake --build . --target generate_proto cd .. ln -sf build/compile_commands.json compile_commands.json +echo "완료"