feat: server, client 디렉토리 조정 및 client 헤더 파일 추가. 시나리오
혹은 대화형 클라이언트로 수정
This commit is contained in:
parent
b887f15662
commit
1d103d7f16
12 changed files with 281 additions and 112 deletions
|
|
@ -14,19 +14,20 @@ find_package(OpenSSL REQUIRED)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
add_executable(Server
|
add_executable(Server
|
||||||
src/main.cpp
|
server/main.cpp
|
||||||
src/NetworkService.cpp
|
server/NetworkService.cpp
|
||||||
src/Session.cpp
|
server/Session.cpp
|
||||||
src/PacketHandler.cpp
|
server/PacketHandler.cpp
|
||||||
src/SessionManager.cpp
|
server/SessionManager.cpp
|
||||||
src/DatabaseManager.cpp
|
server/DatabaseManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(Server PRIVATE include)
|
target_include_directories(Server PRIVATE include)
|
||||||
target_link_libraries(Server PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto)
|
target_link_libraries(Server PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
|
||||||
add_executable(Client
|
add_executable(Client
|
||||||
tests/Client.cpp
|
client/Client.cpp
|
||||||
|
client/main.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(Client PRIVATE include)
|
target_include_directories(Client PRIVATE include)
|
||||||
target_link_libraries(Client PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto)
|
target_link_libraries(Client PRIVATE Boost::system OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
|
|
||||||
170
client/Client.cpp
Normal file
170
client/Client.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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::Login, &loginPkt, sizeof(loginPkt));
|
||||||
|
std::cout << "로그인 요청 보냄: " << nickname << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseClient::HandleSellResult() {
|
||||||
|
PKT_SC_SellResult res;
|
||||||
|
if (ReceivePayload(res)) {
|
||||||
|
currentLevel_ = 0;
|
||||||
|
currentGold_ = res.totalGold;
|
||||||
|
std::cout << ">> 판매 결과: " << res.earnedGold
|
||||||
|
<< " 골드 획득! (총 골드: " << currentGold_ << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveClient::Run() {
|
||||||
|
std::cout << "닉네임을 입력하세요: ";
|
||||||
|
std::cin >> nickname_;
|
||||||
|
Login(nickname_);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (currentLevel_ == 0) {
|
||||||
|
std::cout << "\n[현재 검 없음] 1. 검 구매 시도, 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);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_UpgradeResult)) {
|
||||||
|
HandleUpgradeResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (choice == 1) {
|
||||||
|
SendPacket(PacketID::CS_UpgradeSword);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_UpgradeResult)) {
|
||||||
|
HandleUpgradeResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 2) {
|
||||||
|
SendPacket(PacketID::CS_SellSword);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_SellResult)) {
|
||||||
|
HandleSellResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
void ScenarioClient::Run() {
|
||||||
|
std::ifstream is(scenarioFile_);
|
||||||
|
if (!is.is_open()) {
|
||||||
|
std::cerr << "시나리오 파일을 열 수 없습니다: " << scenarioFile_
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(is >> nickname_))
|
||||||
|
return;
|
||||||
|
Login(nickname_);
|
||||||
|
|
||||||
|
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);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_UpgradeResult)) {
|
||||||
|
HandleUpgradeResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (choice == 1) {
|
||||||
|
SendPacket(PacketID::CS_UpgradeSword);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_UpgradeResult)) {
|
||||||
|
HandleUpgradeResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 2) {
|
||||||
|
SendPacket(PacketID::CS_SellSword);
|
||||||
|
PacketHeader header;
|
||||||
|
if (ReceiveHeader(header) &&
|
||||||
|
header.id == static_cast<uint16_t>(PacketID::SC_SellResult)) {
|
||||||
|
HandleSellResult();
|
||||||
|
}
|
||||||
|
} else if (choice == 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
39
client/main.cpp
Normal file
39
client/main.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "Client.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (isScenario) {
|
||||||
|
if (scenarioFile.empty()) {
|
||||||
|
std::cerr
|
||||||
|
<< "시나리오 파일명이 필요합니다. 사용법: --scenario <파일경로>"
|
||||||
|
<< std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ScenarioClient client(io_context, "127.0.0.1", "30000", scenarioFile);
|
||||||
|
client.Run();
|
||||||
|
} else {
|
||||||
|
InteractiveClient client(io_context, "127.0.0.1", "30000");
|
||||||
|
client.Run();
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << "Exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
59
include/Client.h
Normal file
59
include/Client.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Packet.h"
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
class BaseClient {
|
||||||
|
public:
|
||||||
|
BaseClient(boost::asio::io_context &io_context, const std::string &host,
|
||||||
|
const std::string &port);
|
||||||
|
|
||||||
|
virtual ~BaseClient() = default;
|
||||||
|
|
||||||
|
void SendPacket(PacketID id, const void *payload = nullptr,
|
||||||
|
size_t payloadSize = 0);
|
||||||
|
|
||||||
|
bool ReceiveHeader(PacketHeader &header);
|
||||||
|
|
||||||
|
template <typename T> bool ReceivePayload(T &payload) {
|
||||||
|
try {
|
||||||
|
boost::asio::read(socket_, boost::asio::buffer(&payload, sizeof(T)));
|
||||||
|
return true;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Login(const std::string &nickname);
|
||||||
|
|
||||||
|
virtual void Run() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
tcp::socket socket_;
|
||||||
|
uint32_t currentLevel_ = 0;
|
||||||
|
uint64_t currentGold_ = 0;
|
||||||
|
std::string nickname_;
|
||||||
|
|
||||||
|
void HandleUpgradeResult();
|
||||||
|
void HandleSellResult();
|
||||||
|
};
|
||||||
|
|
||||||
|
class InteractiveClient : public BaseClient {
|
||||||
|
public:
|
||||||
|
using BaseClient::BaseClient;
|
||||||
|
void Run() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScenarioClient : public BaseClient {
|
||||||
|
public:
|
||||||
|
ScenarioClient(boost::asio::io_context &io_context, const std::string &host,
|
||||||
|
const std::string &port, const std::string &scenarioFile)
|
||||||
|
: BaseClient(io_context, host, port), scenarioFile_(scenarioFile) {}
|
||||||
|
void Run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string scenarioFile_;
|
||||||
|
};
|
||||||
|
|
@ -41,13 +41,11 @@ bool DatabaseManager::Init(boost::asio::io_context &io_context,
|
||||||
boost::asio::awaitable<void> DatabaseManager::Lock() {
|
boost::asio::awaitable<void> DatabaseManager::Lock() {
|
||||||
auto executor = co_await boost::asio::this_coro::executor;
|
auto executor = co_await boost::asio::this_coro::executor;
|
||||||
|
|
||||||
// 이 체크 로직 자체가 strand 위에서 안전하게 돌아야 함
|
|
||||||
co_await boost::asio::post(
|
co_await boost::asio::post(
|
||||||
boost::asio::bind_executor(*strand_, boost::asio::use_awaitable));
|
boost::asio::bind_executor(*strand_, boost::asio::use_awaitable));
|
||||||
|
|
||||||
if (!is_locked_) {
|
if (!is_locked_) {
|
||||||
is_locked_ = true;
|
is_locked_ = true;
|
||||||
// 즉시 획득
|
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +70,6 @@ void DatabaseManager::Unlock() {
|
||||||
} else {
|
} else {
|
||||||
auto next = waiting_queue_.front();
|
auto next = waiting_queue_.front();
|
||||||
waiting_queue_.pop();
|
waiting_queue_.pop();
|
||||||
// 다음 대기자를 깨움
|
|
||||||
next->cancel();
|
next->cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -80,7 +77,6 @@ void DatabaseManager::Unlock() {
|
||||||
|
|
||||||
boost::asio::awaitable<DatabaseManager::UserData>
|
boost::asio::awaitable<DatabaseManager::UserData>
|
||||||
DatabaseManager::LoadUser(std::string nickname) {
|
DatabaseManager::LoadUser(std::string nickname) {
|
||||||
// 줄 서기
|
|
||||||
co_await Lock();
|
co_await Lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -126,7 +122,6 @@ DatabaseManager::LoadUser(std::string nickname) {
|
||||||
boost::asio::awaitable<void> DatabaseManager::SaveUser(std::string nickname,
|
boost::asio::awaitable<void> DatabaseManager::SaveUser(std::string nickname,
|
||||||
uint64_t gold,
|
uint64_t gold,
|
||||||
uint32_t swordLevel) {
|
uint32_t swordLevel) {
|
||||||
// 줄 서기
|
|
||||||
co_await Lock();
|
co_await Lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -146,7 +141,6 @@ boost::asio::awaitable<void> DatabaseManager::SaveUser(std::string nickname,
|
||||||
Logger::Log("SaveUser 에러: ", e.what());
|
Logger::Log("SaveUser 에러: ", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 다음 사람 들어오세요
|
|
||||||
Unlock();
|
Unlock();
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,6 @@ PacketHandler::HandlePacket(std::shared_ptr<Session> session,
|
||||||
reinterpret_cast<const PKT_CS_Login *>(packet.payload.data());
|
reinterpret_cast<const PKT_CS_Login *>(packet.payload.data());
|
||||||
std::string nickname(loginPkt->nickname);
|
std::string nickname(loginPkt->nickname);
|
||||||
|
|
||||||
// DB에서 유저 정보 로드 (co_await 활용!)
|
|
||||||
auto userData = co_await DatabaseManager::GetInstance().LoadUser(nickname);
|
auto userData = co_await DatabaseManager::GetInstance().LoadUser(nickname);
|
||||||
|
|
||||||
session->SetNickname(userData.nickname);
|
session->SetNickname(userData.nickname);
|
||||||
|
|
@ -63,7 +63,6 @@ void Session::DoReadBody() {
|
||||||
socket_, boost::asio::buffer(packetBody_.data(), packetBody_.size()),
|
socket_, boost::asio::buffer(packetBody_.data(), packetBody_.size()),
|
||||||
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
|
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
// 논리 패킷 구성
|
|
||||||
Packet packet;
|
Packet packet;
|
||||||
packet.header = packetHeader_;
|
packet.header = packetHeader_;
|
||||||
packet.payload = packetBody_;
|
packet.payload = packetBody_;
|
||||||
|
|
@ -25,7 +25,6 @@ void SessionManager::Broadcast(PacketHeader header,
|
||||||
std::span<const uint8_t> body) {
|
std::span<const uint8_t> body) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
||||||
// 패킷 직렬화 수행 (헤더 + 바디)
|
|
||||||
std::vector<uint8_t> buffer(sizeof(PacketHeader) + body.size());
|
std::vector<uint8_t> buffer(sizeof(PacketHeader) + body.size());
|
||||||
std::memcpy(buffer.data(), &header, sizeof(PacketHeader));
|
std::memcpy(buffer.data(), &header, sizeof(PacketHeader));
|
||||||
std::memcpy(buffer.data() + sizeof(PacketHeader), body.data(), body.size());
|
std::memcpy(buffer.data() + sizeof(PacketHeader), body.data(), body.size());
|
||||||
5
test_scenario.txt
Normal file
5
test_scenario.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
TestUser
|
||||||
|
1
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
#include "Packet.h"
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using boost::asio::ip::tcp;
|
|
||||||
|
|
||||||
void SendPacket(tcp::socket &socket, PacketID id, const void *payload = nullptr,
|
|
||||||
size_t payloadSize = 0) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
std::cout << "사용법: " << argv[0] << " <닉네임>" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string nickname = argv[1];
|
|
||||||
|
|
||||||
try {
|
|
||||||
boost::asio::io_context io_context;
|
|
||||||
tcp::socket socket(io_context);
|
|
||||||
tcp::resolver resolver(io_context);
|
|
||||||
boost::asio::connect(socket, resolver.resolve("127.0.0.1", "30000"));
|
|
||||||
|
|
||||||
std::cout << "서버에 연결되었습니다! (닉네임: " << nickname << ")"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
// 1. 로그인 패킷 전송
|
|
||||||
PKT_CS_Login loginPkt;
|
|
||||||
std::memset(loginPkt.nickname, 0, sizeof(loginPkt.nickname));
|
|
||||||
std::strncpy(loginPkt.nickname, nickname.c_str(),
|
|
||||||
sizeof(loginPkt.nickname) - 1);
|
|
||||||
SendPacket(socket, PacketID::Login, &loginPkt, sizeof(loginPkt));
|
|
||||||
std::cout << "로그인 요청 보냄" << std::endl;
|
|
||||||
|
|
||||||
// 2. 강화 테스트 (5번 시도)
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
||||||
SendPacket(socket, PacketID::CS_UpgradeSword);
|
|
||||||
std::cout << i + 1 << "번째 강화 시도..." << std::endl;
|
|
||||||
|
|
||||||
// 결과 수신
|
|
||||||
PacketHeader resHeader;
|
|
||||||
boost::asio::read(socket,
|
|
||||||
boost::asio::buffer(&resHeader, sizeof(resHeader)));
|
|
||||||
|
|
||||||
if (resHeader.id == static_cast<uint16_t>(PacketID::SC_UpgradeResult)) {
|
|
||||||
PKT_SC_UpgradeResult res;
|
|
||||||
boost::asio::read(socket, boost::asio::buffer(&res, sizeof(res)));
|
|
||||||
|
|
||||||
std::string resultStr =
|
|
||||||
(res.result == 1) ? "성공" : (res.result == 0 ? "파괴!!!" : "실패");
|
|
||||||
std::cout << ">> 강화 결과: " << resultStr
|
|
||||||
<< " (현재레벨: " << res.currentLevel
|
|
||||||
<< ", 남은골드: " << res.currentGold << ")" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 판매 테스트
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
||||||
SendPacket(socket, PacketID::CS_SellSword);
|
|
||||||
std::cout << "검 판매 시도..." << std::endl;
|
|
||||||
|
|
||||||
// 결과 수신
|
|
||||||
PacketHeader sellResHeader;
|
|
||||||
boost::asio::read(
|
|
||||||
socket, boost::asio::buffer(&sellResHeader, sizeof(sellResHeader)));
|
|
||||||
if (sellResHeader.id == static_cast<uint16_t>(PacketID::SC_SellResult)) {
|
|
||||||
PKT_SC_SellResult res;
|
|
||||||
boost::asio::read(socket, boost::asio::buffer(&res, sizeof(res)));
|
|
||||||
std::cout << ">> 판매 결과: " << res.earnedGold
|
|
||||||
<< " 골드 획득! (총 골드: " << res.totalGold << ")"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
std::cerr << "Exception: " << e.what() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue