From b9c5a229893da7b9a729c274e94a07223017f2c5 Mon Sep 17 00:00:00 2001 From: bumpsoo Date: Thu, 5 Feb 2026 12:20:54 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B0=95=ED=99=94=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=EC=9C=BC=EB=A1=9C=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EA=B0=99=EC=9D=B4=20=EA=B3=B5=EC=9C=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/Client.cpp | 108 +++++++++++++++++++++++++-------------- include/Client.h | 12 +++++ server/PacketHandler.cpp | 30 +++++++++-- 3 files changed, 109 insertions(+), 41 deletions(-) diff --git a/client/Client.cpp b/client/Client.cpp index 80cb17a..b6ce909 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -67,16 +67,60 @@ bool BaseClient::Login(const std::string &nickname) { } } +void BaseClient::StartReceive() { + isRunning_ = true; + receiveThread_ = std::jthread(&BaseClient::ReceiveLoop, this); +} + +void BaseClient::StopReceive() { + isRunning_ = false; + socket_.close(); + receiveThread_.request_stop(); +} + +void BaseClient::ReceiveLoop(std::stop_token stopToken) { + try { + while (!stopToken.stop_requested() && isRunning_) { + 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; - std::string resultStr = - (res.result == 1) ? "성공" : (res.result == 0 ? "파괴!!!" : "실패"); - std::cout << ">> 강화 결과: " << resultStr - << " (현재레벨: " << currentLevel_ - << ", 남은골드: " << currentGold_ << ")" << std::endl; + // 강화 결과 출력은 채팅으로 } } @@ -85,8 +129,9 @@ void BaseClient::HandleSellResult() { if (ReceivePayload(res)) { currentLevel_ = 0; currentGold_ = res.totalGold; - std::cout << ">> 판매 결과: " << res.earnedGold - << " 골드 획득! (총 골드: " << currentGold_ << ")" << std::endl; + // 판매 결과는 본인에게만 + std::cout << "\n>> 판매 완료: " << res.earnedGold << " 골드 획득!" + << std::endl; } } @@ -98,9 +143,13 @@ void InteractiveClient::Run() { return; } + // 로그인 후 비동기 수신 시작 + StartReceive(); + while (true) { if (currentLevel_ == 0) { - std::cout << "\n[현재 검 없음] 1. 검 구매 시도, 2. 종료\n입력: "; + std::cout << "\n[현재 검 없음, 골드: " << currentGold_ + << "] 1. 검 구매 시도 (1000골드), 2. 종료\n입력: "; } else { std::cout << "\n[현재 검 레벨: " << currentLevel_ << ", 골드: " << currentGold_ @@ -114,34 +163,24 @@ void InteractiveClient::Run() { if (currentLevel_ == 0) { if (choice == 1) { SendPacket(PacketID::CS_UpgradeSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_UpgradeResult)) { - HandleUpgradeResult(); - } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else if (choice == 2) { break; } } else { if (choice == 1) { SendPacket(PacketID::CS_UpgradeSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_UpgradeResult)) { - HandleUpgradeResult(); - } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else if (choice == 2) { SendPacket(PacketID::CS_SellSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_SellResult)) { - HandleSellResult(); - } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else if (choice == 3) { break; } } } + + StopReceive(); } #include @@ -161,6 +200,9 @@ void ScenarioClient::Run() { return; } + // 비동기 수신 시작 + StartReceive(); + std::string line; while (is >> line) { int choice = std::stoi(line); @@ -169,33 +211,23 @@ void ScenarioClient::Run() { if (currentLevel_ == 0) { if (choice == 1) { SendPacket(PacketID::CS_UpgradeSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_UpgradeResult)) { - HandleUpgradeResult(); - } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else if (choice == 2) { break; } } else { if (choice == 1) { SendPacket(PacketID::CS_UpgradeSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_UpgradeResult)) { - HandleUpgradeResult(); - } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else if (choice == 2) { SendPacket(PacketID::CS_SellSword); - PacketHeader header; - if (ReceiveHeader(header) && - header.id == static_cast(PacketID::SC_SellResult)) { - HandleSellResult(); - } + 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/include/Client.h b/include/Client.h index 2ad893b..3379ecd 100644 --- a/include/Client.h +++ b/include/Client.h @@ -1,8 +1,10 @@ #pragma once #include "Packet.h" +#include #include #include +#include using boost::asio::ip::tcp; @@ -29,6 +31,10 @@ public: bool Login(const std::string &nickname); + // 비동기 수신 시작 + void StartReceive(); + void StopReceive(); + virtual void Run() = 0; protected: @@ -37,6 +43,12 @@ protected: uint64_t currentGold_ = 0; std::string nickname_; + std::jthread receiveThread_; + std::atomic isRunning_{false}; + + void ReceiveLoop(std::stop_token stopToken); + void HandlePacket(const PacketHeader &header); + void HandleUpgradeResult(); void HandleSellResult(); }; diff --git a/server/PacketHandler.cpp b/server/PacketHandler.cpp index af3633f..b4a3dd0 100644 --- a/server/PacketHandler.cpp +++ b/server/PacketHandler.cpp @@ -103,6 +103,7 @@ PacketHandler::HandlePacket(std::shared_ptr session, res.currentLevel = session->GetSwordLevel(); res.currentGold = session->GetGold(); + // 결과 패킷 전송 PacketHeader header; header.id = static_cast(PacketID::SC_UpgradeResult); header.size = sizeof(PKT_SC_UpgradeResult); @@ -112,11 +113,34 @@ PacketHandler::HandlePacket(std::shared_ptr session, std::memcpy(buffer.data(), &header, sizeof(PacketHeader)); std::memcpy(buffer.data() + sizeof(PacketHeader), &res, sizeof(PKT_SC_UpgradeResult)); - session->Send(buffer); - Logger::Log("강화 시도 [", session->GetNickname(), "]: ", (int)res.result, - " (레벨: ", currentLevel, "->", res.currentLevel, ")"); + // 브로드캐스트 + if (currentGold >= cost) { + uint32_t attemptedLevel = currentLevel + 1; + std::string resultText; + if (res.result == 1) { + resultText = "를 달성했습니다!"; + } else if (res.result == 0) { + resultText = " 시도 중 검이 파괴되었습니다!"; + } else { + resultText = " 달성에 실패했습니다!"; + } + + std::string chatMsg = "채팅] " + session->GetNickname() + + " 님이 검 강화 단계 " + + std::to_string(attemptedLevel) + resultText; + + PacketHeader chatHeader; + chatHeader.id = static_cast(PacketID::Chat); + chatHeader.size = static_cast(chatMsg.size()); + + std::vector chatPayload(chatMsg.begin(), chatMsg.end()); + SessionManager::GetInstance().Broadcast(chatHeader, chatPayload); + + Logger::Log("강화 시도 [", session->GetNickname(), "]: ", (int)res.result, + " (레벨: ", currentLevel, "->", res.currentLevel, ")"); + } } break; case PacketID::CS_SellSword: {