This commit is contained in:
bumpsoo 2026-02-03 09:44:08 +00:00
commit b887f15662
21 changed files with 1038 additions and 0 deletions

42
include/DatabaseManager.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/mysql.hpp>
#include <memory>
#include <queue>
#include <string>
class DatabaseManager {
public:
static DatabaseManager &GetInstance();
bool Init(boost::asio::io_context &io_context, const std::string &host,
uint16_t port, const std::string &user, const std::string &password,
const std::string &db);
struct UserData {
std::string nickname;
uint64_t gold;
uint32_t swordLevel;
};
boost::asio::awaitable<UserData> LoadUser(std::string nickname);
boost::asio::awaitable<void> SaveUser(std::string nickname, uint64_t gold,
uint32_t swordLevel);
private:
DatabaseManager() = default;
// 비동기 Mutex 역할을 할 함수들
boost::asio::awaitable<void> Lock();
void Unlock();
std::unique_ptr<boost::mysql::tcp_ssl_connection> conn_;
std::unique_ptr<boost::asio::strand<boost::asio::io_context::executor_type>>
strand_;
// 비동기 락 시스템을 위한 상태 변수
bool is_locked_ = false;
std::queue<std::shared_ptr<boost::asio::steady_timer>> waiting_queue_;
};

15
include/Logger.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include <iostream>
#include <mutex>
class Logger {
public:
// 가변 인자 템플릿과 Fold Expression을 사용하여 여러 인자를 안전하게 출력
template <typename... Args> static void Log(Args &&...args) {
std::lock_guard<std::mutex> lock(mutex_);
(std::cout << ... << std::forward<Args>(args)) << std::endl;
}
private:
static inline std::mutex mutex_;
};

25
include/NetworkService.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <boost/asio.hpp>
#include <thread>
#include <vector>
using boost::asio::ip::tcp;
class NetworkService {
public:
NetworkService(uint16_t port, int threadCount);
~NetworkService();
void Run();
void Stop();
private:
void DoAccept();
boost::asio::io_context io_context_;
std::vector<std::jthread> threads_;
tcp::acceptor acceptor_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
work_guard_;
};

83
include/Packet.h Normal file
View file

@ -0,0 +1,83 @@
#pragma once
#include <cstdint>
#include <vector>
// 표준 헤더 크기: 2바이트 크기 + 2바이트 ID
#pragma pack(push, 1)
struct PacketHeader {
uint16_t size;
uint16_t id;
};
#pragma pack(pop)
// 처리를 쉽게 하기 위한 단순 패킷 래퍼
struct Packet {
PacketHeader header;
// Payload 데이터 (벡터 또는 문자열)
std::vector<uint8_t> payload;
};
enum class PacketID : uint16_t {
Ping = 1,
// 로그인 요청 (PKT_CS_Login)
Login = 10,
Chat = 20,
// 검 키우기 관련 패킷
// 강화 요청
CS_UpgradeSword = 30,
// 강화 결과 응답
SC_UpgradeResult = 31,
// 판매 요청
CS_SellSword = 35,
// 판매 결과 응답
SC_SellResult = 36,
// 랭킹 조회 요청
CS_RankingRequest = 40,
// 랭킹 리스트 응답
SC_RankingList = 41,
};
// 게임 규칙 관련 상수
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_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)

15
include/PacketHandler.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include "Packet.h"
#include "Session.h"
#include <boost/asio/awaitable.hpp>
#include <memory>
class Session;
class PacketHandler {
public:
// 패킷 종류에 따라 비동기 로직 디스패칭 (코루틴 방식)
static boost::asio::awaitable<void>
HandlePacket(std::shared_ptr<Session> session, const Packet packet);
};

43
include/Session.h Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include "Packet.h"
#include <boost/asio.hpp>
#include <deque>
#include <memory>
#include <span>
using boost::asio::ip::tcp;
class Session : public std::enable_shared_from_this<Session> {
public:
Session(tcp::socket socket);
~Session();
void Start();
void Send(std::span<const uint8_t> data);
void SetNickname(const std::string &nickname);
const std::string &GetNickname() const;
void SetGold(uint64_t gold);
uint64_t GetGold() const;
void SetSwordLevel(uint32_t level);
uint32_t GetSwordLevel() const;
private:
void DoReadHeader();
void DoReadBody();
void DoWrite();
tcp::socket socket_;
PacketHeader packetHeader_;
std::vector<uint8_t> packetBody_;
// 스레드 안전성과 순서 보장을 위한 출력 패킷 큐
std::deque<std::vector<uint8_t>> writeQueue_;
std::string nickname_;
uint64_t gold_ = 0;
uint32_t swordLevel_ = 0;
};

20
include/SessionManager.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include "Session.h"
#include <memory>
#include <mutex>
#include <set>
#include <span>
class SessionManager {
public:
static SessionManager &GetInstance();
void Join(std::shared_ptr<Session> session);
void Leave(std::shared_ptr<Session> session);
void Broadcast(PacketHeader header, std::span<const uint8_t> body);
private:
std::mutex mutex_;
std::set<std::shared_ptr<Session>> sessions_;
};

63
include/SwordLogic.h Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include "Packet.h"
#include <cstdint>
#include <random>
class SwordLogic {
public:
// 강화 비용 계산
static uint64_t GetUpgradeCost(uint32_t currentLevel) {
// 0강은 무료
if (currentLevel == 0)
return 0;
// 레벨이 오를수록 비용 증가
return (currentLevel * 1000) + 500;
}
// 판매 가격 계산
static uint64_t GetSellPrice(uint32_t currentLevel) {
if (currentLevel == 0)
return 100;
// 성공 횟수에 비례하여 상승
return (currentLevel * 2000) + 1000;
}
// 강화 시도 결과 계산 (0: 파괴, 1: 성공, 2: 실패)
static uint8_t TryUpgrade(uint32_t currentLevel) {
// 최대 레벨 도달 시 실패 처리
if (currentLevel >= GameConfig::MAX_SWORD_LEVEL)
return 2;
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(1, 100);
int randVal = dis(gen);
// 확률 설정
int successProb = 0;
int destroyProb = 0;
if (currentLevel < 10) {
// 0~9강: 성공 확률 높음, 파괴 없음
// 90% ~ 45%
successProb = 90 - (currentLevel * 5);
destroyProb = 0;
} else {
// 10~19강: 성공 확률 낮음, 파괴 존재
// 40% ~ 4%
successProb = 40 - ((currentLevel - 10) * 4);
// 5% ~ 23%
destroyProb = 5 + ((currentLevel - 10) * 2);
}
// 성공
if (randVal <= successProb)
return 1;
// 파괴
if (randVal <= (successProb + destroyProb))
return 0;
// 실패
return 2;
}
};