#include "DatabaseManager.h" #include "Logger.h" #include #include #include DatabaseManager &DatabaseManager::GetInstance() { static DatabaseManager instance; return instance; } bool DatabaseManager::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) { try { strand_ = std::make_unique< boost::asio::strand>( io_context.get_executor()); boost::asio::ip::tcp::resolver resolver(io_context.get_executor()); auto endpoints = resolver.resolve(host, std::to_string(port)); boost::mysql::handshake_params params(user, password, db); static boost::asio::ssl::context ssl_ctx( boost::asio::ssl::context::tlsv12_client); conn_ = std::make_unique( io_context.get_executor(), ssl_ctx); conn_->connect(*endpoints.begin(), params); Logger::Log("Database 연결 성공 (AsyncLock 적용): ", host, ":", port); return true; } catch (const std::exception &e) { Logger::Log("Database 연결 실패: ", e.what()); return false; } } // 비동기 락 획득 (이미 락이 걸려있으면 대기열에 들어감) boost::asio::awaitable DatabaseManager::Lock() { auto executor = co_await boost::asio::this_coro::executor; co_await boost::asio::post( boost::asio::bind_executor(*strand_, boost::asio::use_awaitable)); if (!is_locked_) { is_locked_ = true; co_return; } // 이미 잠겨있으면 타이머를 만들어서 대기열에 추가 (비동기 대기) auto timer = std::make_shared( executor, std::chrono::steady_clock::time_point::max()); waiting_queue_.push(timer); try { co_await timer->async_wait(boost::asio::use_awaitable); } catch (...) { // 타이머 취소(Unlock에서 호출함)되면 성공적으로 락을 획득한 것으로 간주 } co_return; } // 비동기 락 해제 (대기열에 다음 사람이 있으면 깨워줌) void DatabaseManager::Unlock() { boost::asio::dispatch(*strand_, [this]() { if (waiting_queue_.empty()) { is_locked_ = false; } else { auto next = waiting_queue_.front(); waiting_queue_.pop(); next->cancel(); } }); } boost::asio::awaitable DatabaseManager::LoadUser(std::string nickname) { co_await Lock(); try { if (!conn_) { Unlock(); co_return UserData{nickname, 10000, 0}; } boost::mysql::results result; boost::mysql::statement stmt = co_await conn_->async_prepare_statement( "SELECT username, gold, sword_level FROM users WHERE username = ?", boost::asio::use_awaitable); co_await conn_->async_execute(stmt.bind(nickname), result, boost::asio::use_awaitable); if (result.rows().empty()) { boost::mysql::statement ins_stmt = co_await conn_->async_prepare_statement( "INSERT INTO users (username, gold, sword_level) VALUES (?, " "10000, 0)", boost::asio::use_awaitable); co_await conn_->async_execute(ins_stmt.bind(nickname), result, boost::asio::use_awaitable); Logger::Log("새로운 유저 탄생 (DB): ", nickname); Unlock(); co_return UserData{nickname, 10000, 0}; } else { auto row = result.rows().at(0); UserData data; data.nickname = row.at(0).as_string(); data.gold = row.at(1).as_uint64(); data.swordLevel = (uint32_t)row.at(2).as_int64(); Unlock(); co_return data; } } catch (const std::exception &e) { Logger::Log("LoadUser 에러: ", e.what()); Unlock(); co_return UserData{nickname, 10000, 0}; } } boost::asio::awaitable DatabaseManager::SaveUser(std::string nickname, uint64_t gold, uint32_t swordLevel) { co_await Lock(); try { if (!conn_) { Unlock(); co_return; } boost::mysql::results result; boost::mysql::statement stmt = co_await conn_->async_prepare_statement( "UPDATE users SET gold = ?, sword_level = ? WHERE username = ?", boost::asio::use_awaitable); co_await conn_->async_execute( stmt.bind(gold, (int64_t)swordLevel, nickname), result, boost::asio::use_awaitable); } catch (const std::exception &e) { Logger::Log("SaveUser 에러: ", e.what()); } Unlock(); co_return; }