#include "Client.h" #include "Protocol.pb.h" #include #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 google::protobuf::Message *pkt) { uint16_t payloadSize = 0; if (pkt) { payloadSize = static_cast(pkt->ByteSizeLong()); } PacketHeader header; header.id = static_cast(id); header.size = payloadSize; std::vector buffer(sizeof(PacketHeader) + payloadSize); std::memcpy(buffer.data(), &header, sizeof(PacketHeader)); if (pkt) { pkt->SerializeToArray(buffer.data() + sizeof(PacketHeader), 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) { Protocol::CS_Login loginPkt; loginPkt.set_nickname(nickname); SendPacket(PacketID::CS_Login, &loginPkt); std::cout << "로그인 요청 보냄: " << nickname << std::endl; // 로그인 결과 대기 PacketHeader header; if (!ReceiveHeader(header) || header.id != static_cast(PacketID::SC_LoginResult)) { std::cerr << "로그인 응답을 받지 못했습니다." << std::endl; return false; } Protocol::SC_LoginResult res; std::vector payload(header.size); boost::asio::read(socket_, boost::asio::buffer(payload.data(), header.size)); if (!res.ParseFromArray(payload.data(), header.size)) { std::cerr << "로그인 응답 파싱 실패" << std::endl; return false; } if (res.success()) { std::cout << "로그인 성공!" << std::endl; return true; } 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(header); break; case PacketID::SC_SellResult: HandleSellResult(header); 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(const PacketHeader &header) { Protocol::SC_UpgradeResult res; std::vector payload(header.size); boost::asio::read(socket_, boost::asio::buffer(payload.data(), header.size)); if (res.ParseFromArray(payload.data(), header.size)) { currentLevel_ = res.current_level(); currentGold_ = res.current_gold(); // 강화 결과 출력은 채팅으로 } } void BaseClient::HandleSellResult(const PacketHeader &header) { Protocol::SC_SellResult res; std::vector payload(header.size); boost::asio::read(socket_, boost::asio::buffer(payload.data(), header.size)); if (res.ParseFromArray(payload.data(), header.size)) { currentLevel_ = 0; currentGold_ = res.total_gold(); // 판매 결과는 본인에게만 std::cout << "\n>> 판매 완료: " << res.earned_gold() << " 골드 획득!" << 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(); }