233 lines
6.5 KiB
C++
233 lines
6.5 KiB
C++
#include "Client.h"
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
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<uint16_t>(id);
|
|
header.size = static_cast<uint16_t>(payloadSize);
|
|
|
|
std::vector<uint8_t> 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<uint16_t>(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<PacketID>(header.id);
|
|
switch (id) {
|
|
case PacketID::Chat: {
|
|
std::vector<char> 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<uint8_t> 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 <fstream>
|
|
|
|
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();
|
|
}
|