diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f47651..245ed9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(nlohmann_json REQUIRED) find_package(asio REQUIRED) find_package(argparse REQUIRED) find_package(Slint REQUIRED) +find_package(spdlog REQUIRED) # Uno Game Library add_library(uno-game-lib @@ -15,6 +16,7 @@ add_library(uno-game-lib src/game/CardTile.cpp src/game/Player.cpp src/game/GameState.cpp + src/common/Logger.cpp src/common/Utils.cpp src/network/Message.cpp src/network/MessageSerializer.cpp @@ -25,7 +27,8 @@ add_library(uno-game-lib target_link_libraries(uno-game-lib PUBLIC nlohmann_json::nlohmann_json) target_link_libraries(uno-game-lib PUBLIC asio::asio) target_link_libraries(uno-game-lib PUBLIC Slint::Slint) -target_link_libraries(uno-game-lib PRIVATE argparse::argparse) +target_link_libraries(uno-game-lib PUBLIC argparse::argparse) +target_link_libraries(uno-game-lib PUBLIC spdlog::spdlog) slint_target_sources(uno-game-lib ui/MainWindow.slint) diff --git a/src/client/UnoClient.cpp b/src/client/UnoClient.cpp index 2e17a43..b4d3dea 100644 --- a/src/client/UnoClient.cpp +++ b/src/client/UnoClient.cpp @@ -10,11 +10,13 @@ #include "../network/MessageSerializer.h" #include +#include #include namespace UNO::CLIENT { void UnoClient::handleNetworkConnected() { + SPDLOG_INFO("Connected to server, sending JOIN_GAME message"); NETWORK::JoinGamePayload messagePayload = {this->clientGameState_->getPlayerName()}; NETWORK::Message message = {NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::JOIN_GAME, messagePayload}; networkClient_->send(NETWORK::MessageSerializer::serialize(message)); @@ -25,11 +27,14 @@ namespace UNO::CLIENT { void UnoClient::handleNetworkInitGame(const NETWORK::InitGamePayload &payload) { + SPDLOG_INFO("Initializing game: player ID={}, {} players total", payload.playerId, payload.players.size()); this->clientGameState_->init(payload.players, payload.discardPile, payload.handCard, payload.currentPlayerIndex, payload.playerId); } void UnoClient::handleNetworkPlayCard(const NETWORK::PlayCardPayload &payload) { + SPDLOG_DEBUG( + "Received PLAY_CARD: color={}, type={}", static_cast(payload.card.getColor()), static_cast(payload.card.getType())); if (clientGameState_->getClientGameStage() == GAME::ClientGameStage::ACTIVE) { clientGameState_->play(payload.card); } @@ -38,6 +43,7 @@ namespace UNO::CLIENT { void UnoClient::handleNetworkDrawCard(const NETWORK::DrawCardPayload &payload) { + SPDLOG_DEBUG("Received DRAW_CARD: {} card(s)", payload.drawCount); if (clientGameState_->getClientGameStage() == GAME::ClientGameStage::ACTIVE) { clientGameState_->draw(payload.cards); } @@ -46,12 +52,15 @@ namespace UNO::CLIENT { void UnoClient::handleNetworkEndGame(const NETWORK::EndGamePayload &payload) { + SPDLOG_INFO("Game ended"); this->clientGameState_->endGame(); } void UnoClient::handleNetworkMessage(const std::string &message) { auto networkMessage = NETWORK::MessageSerializer::deserialize(message); + SPDLOG_DEBUG("Received network message, type: {}", static_cast(networkMessage.getMessagePayloadType())); + if (networkMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::INIT_GAME) { this->handleNetworkInitGame(std::get(networkMessage.getMessagePayload())); } @@ -68,6 +77,7 @@ namespace UNO::CLIENT { if (networkMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::EMPTY || networkMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::JOIN_GAME || networkMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::START_GAME) { + SPDLOG_ERROR("Invalid message type from server: {}", static_cast(networkMessage.getMessagePayloadType())); throw std::invalid_argument("Invalid message type from server"); } @@ -76,6 +86,7 @@ namespace UNO::CLIENT { void UnoClient::handlePlayerAction(PlayerAction action) { + SPDLOG_DEBUG("Handling player action, type: {}", static_cast(action.playerActionType)); if (action.playerActionType == PlayerActionType::CONNECT) { this->handlePlayerConnect(std::get(action.payload)); } @@ -93,6 +104,7 @@ namespace UNO::CLIENT { void UnoClient::handlePlayerConnect(const PlayerConnectPayload &payload) { + SPDLOG_INFO("Player '{}' connecting to {}:{}", payload.playerName, payload.host, payload.port); clientGameState_->setPlayerName(payload.playerName); networkClient_->connect(payload.host, payload.port); } @@ -100,6 +112,7 @@ namespace UNO::CLIENT { void UnoClient::handlePlayerStartGame(PlayerStartGamePayload payload) { + SPDLOG_INFO("Player requests to start game"); NETWORK::StartGamePayload messagePayload = {}; NETWORK::Message message = {NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::START_GAME, messagePayload}; networkClient_->send(NETWORK::MessageSerializer::serialize(message)); @@ -115,9 +128,12 @@ namespace UNO::CLIENT { if ((card->getType() == GAME::CardType::WILD || card->getType() == GAME::CardType::WILDDRAWFOUR) && card->getColor() != GAME::CardColor::RED) { + SPDLOG_ERROR("Invalid wild card played by player: card doesn't have color set"); throw std::invalid_argument("Invalid card played by player"); } + SPDLOG_INFO("Player plays card: color={}, type={}", card->colorToString(), card->typeToString()); + NETWORK::PlayCardPayload messagePayload = { {(card->getType() != GAME::CardType::WILD && card->getType() != GAME::CardType::WILDDRAWFOUR) ? card->getColor() : payload.color, @@ -128,6 +144,7 @@ namespace UNO::CLIENT { void UnoClient::handlePlayerDrawCard(PlayerDrawCardPayload payload) { + SPDLOG_INFO("Player requests to draw card"); NETWORK::DrawCardPayload messagePayload = {}; NETWORK::Message message = {NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::DRAW_CARD, messagePayload}; networkClient_->send(NETWORK::MessageSerializer::serialize(message)); @@ -135,6 +152,7 @@ namespace UNO::CLIENT { UnoClient::UnoClient() { + SPDLOG_DEBUG("UnoClient initialized"); clientGameState_ = std::make_shared(); networkClient_ = std::make_shared( [this]() { this->handleNetworkConnected(); }, [this](const std::string &message) { this->handleNetworkMessage(message); }); @@ -143,6 +161,7 @@ namespace UNO::CLIENT { UnoClient::~UnoClient() { + SPDLOG_DEBUG("UnoClient shutting down"); networkClient_->stop(); if (networkThread_.joinable()) { networkThread_.join(); @@ -151,7 +170,8 @@ namespace UNO::CLIENT { void UnoClient::run() { + SPDLOG_INFO("UnoClient starting"); networkThread_ = std::thread([this]() { this->networkClient_->run(); }); gameUI_->run(); } -} // namespace UNO::CLIENT \ No newline at end of file +} // namespace UNO::CLIENT diff --git a/src/client/main.cpp b/src/client/main.cpp index 9eb931d..87cbe3c 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -1,11 +1,18 @@ #include "UnoClient.h" +#include "../common/Logger.h" +#include #include int main() { + UNO::COMMON::Logger::init("uno-client"); + SPDLOG_INFO("Starting uno-client application"); + UNO::CLIENT::UnoClient client; client.run(); + + SPDLOG_INFO("uno-client exited"); return 0; } diff --git a/src/common/Logger.cpp b/src/common/Logger.cpp new file mode 100644 index 0000000..325e09f --- /dev/null +++ b/src/common/Logger.cpp @@ -0,0 +1,42 @@ +/** + * @file Logger.cpp + */ +#include "Logger.h" + +#include +#include + +#include +#include +#include + +namespace fs = std::filesystem; + +namespace UNO::COMMON { + void Logger::init(const std::string &app_name, const std::string &log_dir) + { + fs::path dir{log_dir}; + if (!fs::exists(dir)) { + fs::create_directories(dir); + } + + auto logfile = (dir / (app_name + ".log")).string(); + + constexpr std::size_t max_file_size = 5 * 1024 * 1024; + constexpr std::size_t max_files = 3; + + auto rotating_sink = std::make_shared(logfile, max_file_size, max_files); + auto console_sink = std::make_shared(); + + rotating_sink->set_level(spdlog::level::debug); + console_sink->set_level(spdlog::level::info); + + std::vector sinks{rotating_sink, console_sink}; + auto logger = std::make_shared(app_name, sinks.begin(), sinks.end()); + + logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%s:%#] [%!] %v"); + + spdlog::register_logger(logger); + spdlog::set_default_logger(logger); + } +} // namespace UNO::COMMON diff --git a/src/common/Logger.h b/src/common/Logger.h new file mode 100644 index 0000000..f9b72cb --- /dev/null +++ b/src/common/Logger.h @@ -0,0 +1,14 @@ +/** + * @file Logger.h + */ +#pragma once + +#include + +namespace UNO::COMMON { + struct Logger { + // Initialize global logging. Safe to call multiple times. + // Default log directory aligns with CMake runtime output: /bin/log + static void init(const std::string &app_name, const std::string &log_dir = "log"); + }; +} // namespace UNO::COMMON diff --git a/src/game/GameState.cpp b/src/game/GameState.cpp index ad5998f..93f90de 100644 --- a/src/game/GameState.cpp +++ b/src/game/GameState.cpp @@ -7,6 +7,7 @@ #include "GameState.h" #include +#include #include #include @@ -141,9 +142,11 @@ namespace UNO::GAME { this->self_ = this->players_.begin() + static_cast(selfIndex); if (this->self_ == this->currentPlayer_) { this->clientGameStage_ = ClientGameStage::ACTIVE; + SPDLOG_INFO("Client game initialized: {} players, self index: {}, it's our turn", players.size(), selfIndex); } else { this->clientGameStage_ = ClientGameStage::IDLE; + SPDLOG_INFO("Client game initialized: {} players, self index: {}, waiting for turn", players.size(), selfIndex); } } @@ -179,6 +182,7 @@ namespace UNO::GAME { void ClientGameState::endGame() { + SPDLOG_INFO("Client game ended"); this->clientGameStage_ = ClientGameStage::AFTER_GAME; this->player_.clear(); } @@ -190,19 +194,25 @@ namespace UNO::GAME { long long currentPlayerIndex = this->currentPlayer_ - this->players_.begin(); this->players_.push_back(std::move(playerState)); this->currentPlayer_ = this->players_.begin() + currentPlayerIndex; + SPDLOG_DEBUG("Player '{}' added to game state, total players: {}", playerState.getName(), this->players_.size()); } void ServerGameState::init() { + SPDLOG_INFO("Initializing server game state"); while (discardPile_.isEmpty() || discardPile_.getFront().getType() > CardType::NUM9) { discardPile_.add(deck_.draw()); } + SPDLOG_DEBUG("Initial discard pile card: color={}, type={}", + discardPile_.getFront().colorToString(), + discardPile_.getFront().typeToString()); for (size_t i = 0; i < 7; i++) { for (auto &player : this->players_) { player.draw(1, this->deck_.draw(1)); } } + SPDLOG_INFO("Dealt 7 cards to each of {} players", this->players_.size()); this->serverGameStage_ = ServerGameStage::IN_GAME; } @@ -212,6 +222,7 @@ namespace UNO::GAME { if (this->drawCount_ == 0) { this->drawCount_ = 1; } + SPDLOG_DEBUG("Player '{}' drawing {} card(s)", this->currentPlayer_->getName(), this->drawCount_); auto cards = deck_.draw(this->drawCount_); this->currentPlayer_->draw(this->drawCount_, cards); this->drawCount_ = 0; @@ -226,6 +237,7 @@ namespace UNO::GAME { void ServerGameState::endGame() { + SPDLOG_INFO("Ending server game, returning to pre-game state"); this->serverGameStage_ = ServerGameStage::PRE_GAME; deck_.clear(); diff --git a/src/network/NetworkClient.cpp b/src/network/NetworkClient.cpp index 6d205fd..690f767 100644 --- a/src/network/NetworkClient.cpp +++ b/src/network/NetworkClient.cpp @@ -9,47 +9,63 @@ #include #include #include +#include #include namespace UNO::NETWORK { NetworkClient::NetworkClient(std::function onConnect, std::function callback) : onConnected_(std::move(onConnect)), callback_(std::move(callback)), workGuard_(asio::make_work_guard(io_context_)) { + SPDLOG_DEBUG("NetworkClient initialized"); } void NetworkClient::connect(const std::string &host, uint16_t port) { + SPDLOG_INFO("Connecting to {}:{}", host, port); auto socket = std::make_shared(io_context_); auto resolver = std::make_shared(io_context_); - resolver->async_resolve(host, - std::to_string(port), - [this, resolver, socket](const asio::error_code &ec, const asio::ip::tcp::resolver::results_type &results) { - if (!ec) { - asio::async_connect( - *socket, results, [this, socket](const asio::error_code &ec, const asio::ip::tcp::endpoint &) { - if (!ec) { - this->session_ = std::make_shared(std::move(*socket)); - this->session_->start(callback_); - this->onConnected_(); - } - }); - } - }); + resolver->async_resolve( + host, + std::to_string(port), + [this, resolver, socket, host, port](const asio::error_code &ec, const asio::ip::tcp::resolver::results_type &results) { + if (!ec) { + SPDLOG_DEBUG("Resolved host {}:{}", host, port); + asio::async_connect( + *socket, results, [this, socket, host, port](const asio::error_code &ec, const asio::ip::tcp::endpoint &) { + if (!ec) { + SPDLOG_INFO("Successfully connected to {}:{}", host, port); + this->session_ = std::make_shared(std::move(*socket)); + this->session_->start(callback_); + this->onConnected_(); + } + else { + SPDLOG_ERROR("Failed to connect to {}:{}: {}", host, port, ec.message()); + } + }); + } + else { + SPDLOG_ERROR("Failed to resolve host {}:{}: {}", host, port, ec.message()); + } + }); } void NetworkClient::send(const std::string &message) { + SPDLOG_DEBUG("Queueing message to send, size: {} bytes", message.size()); asio::post(io_context_, [session = this->session_, message]() { session->send(message); }); } void NetworkClient::run() { + SPDLOG_INFO("NetworkClient starting io_context loop"); this->io_context_.run(); + SPDLOG_INFO("NetworkClient io_context loop stopped"); } void NetworkClient::stop() { + SPDLOG_INFO("NetworkClient stopping"); this->workGuard_.reset(); this->io_context_.stop(); } diff --git a/src/network/NetworkServer.cpp b/src/network/NetworkServer.cpp index e8eb714..956de68 100644 --- a/src/network/NetworkServer.cpp +++ b/src/network/NetworkServer.cpp @@ -6,6 +6,7 @@ */ #include "NetworkServer.h" +#include #include namespace UNO::NETWORK { @@ -13,15 +14,20 @@ namespace UNO::NETWORK { { this->acceptor_.async_accept([this](const asio::error_code &ec, asio::ip::tcp::socket socket) { if (!ec) { + SPDLOG_INFO("Accepted new connection from {}", socket.remote_endpoint().address().to_string()); this->addPlayer(std::move(socket)); accept(); } + else { + SPDLOG_ERROR("Accept error: {}", ec.message()); + } }); } NetworkServer::NetworkServer(uint16_t port, std::function callback) : acceptor_(io_context_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)), playerCount(0), callback_(std::move(callback)) { + SPDLOG_INFO("NetworkServer initialized on port {}", port); accept(); } @@ -32,25 +38,31 @@ namespace UNO::NETWORK { this->sessions_[playerId] = std::make_shared(std::move(socket)); this->sessions_[playerId]->start([this, playerId](std::string message) { this->callback_(playerId, std::move(message)); }); this->playerCount++; + SPDLOG_INFO("Player {} added, total players: {}", playerId, this->playerCount); } void NetworkServer::send(size_t id, const std::string &message) { std::lock_guard lock(this->mutex_); if (this->sessions_.contains(id) == false) { + SPDLOG_ERROR("Failed to send message: Player session {} not found", id); throw std::invalid_argument("Player session not found"); } + SPDLOG_DEBUG("Sending message to player {}, size: {} bytes", id, message.size()); this->sessions_[id]->send(message); } void NetworkServer::run() { + SPDLOG_INFO("NetworkServer starting io_context loop"); this->io_context_.run(); + SPDLOG_INFO("NetworkServer io_context loop stopped"); } void NetworkServer::stop() { + SPDLOG_INFO("NetworkServer stopping"); this->acceptor_.close(); this->io_context_.stop(); } -} // namespace UNO::NETWORK \ No newline at end of file +} // namespace UNO::NETWORK diff --git a/src/network/Session.cpp b/src/network/Session.cpp index 1c68998..e1342af 100644 --- a/src/network/Session.cpp +++ b/src/network/Session.cpp @@ -6,11 +6,17 @@ */ #include "Session.h" +#include + namespace UNO::NETWORK { - Session::Session(asio::ip::tcp::socket socket) : socket_(std::move(socket)) {} + Session::Session(asio::ip::tcp::socket socket) : socket_(std::move(socket)) + { + SPDLOG_DEBUG("Session created"); + } void Session::start(std::function callback) { + SPDLOG_DEBUG("Session started"); this->callback_ = std::move(callback); this->doRead(); } @@ -19,6 +25,7 @@ namespace UNO::NETWORK { { bool writeInProgress = !this->messages_.empty(); messages_.push(message); + SPDLOG_DEBUG("Message queued for sending, size: {} bytes, queue size: {}", message.size(), messages_.size()); if (writeInProgress == false) { this->doWrite(); } @@ -31,13 +38,18 @@ namespace UNO::NETWORK { asio::buffer(messageLength.get(), sizeof(size_t)), [this, self = shared_from_this(), messageLength](const asio::error_code &ec, size_t length) { if (!ec) { + SPDLOG_TRACE("Read message header, length: {} bytes", *messageLength); if (*messageLength <= 10 * 1024 * 1024) { this->doReadBody(*messageLength); } else { + SPDLOG_WARN("Message length {} exceeds limit, skipping", *messageLength); doRead(); } } + else { + SPDLOG_DEBUG("Session read error: {}", ec.message()); + } }); } @@ -45,12 +57,16 @@ namespace UNO::NETWORK { { auto buffer = std::make_shared>(length); asio::async_read( - socket_, asio::buffer(*buffer), [this, self = shared_from_this(), buffer](const asio::error_code &ec, size_t length) { + socket_, asio::buffer(*buffer), [this, self = shared_from_this(), buffer, length](const asio::error_code &ec, size_t) { if (!ec) { std::string message = {buffer->begin(), buffer->end()}; + SPDLOG_TRACE("Received message body, size: {} bytes", length); this->callback_(message); doRead(); } + else { + SPDLOG_DEBUG("Session read body error: {}", ec.message()); + } }); } @@ -62,10 +78,17 @@ namespace UNO::NETWORK { auto length = std::make_shared(message.size()); auto msg = std::make_shared(message); + SPDLOG_TRACE("Sending message, size: {} bytes", *length); std::array buffers = {asio::buffer(length.get(), sizeof(size_t)), asio::buffer(*msg)}; asio::async_write(socket_, buffers, [this, self = shared_from_this(), length, msg](const asio::error_code &ec, size_t) { - if (!ec && this->messages_.empty() == false) { - this->doWrite(); + if (!ec) { + SPDLOG_TRACE("Message sent successfully, size: {} bytes", *length); + if (this->messages_.empty() == false) { + this->doWrite(); + } + } + else { + SPDLOG_ERROR("Session write error: {}", ec.message()); } }); } diff --git a/src/server/UnoServer.cpp b/src/server/UnoServer.cpp index e92293c..eedba30 100644 --- a/src/server/UnoServer.cpp +++ b/src/server/UnoServer.cpp @@ -7,12 +7,14 @@ #include "UnoServer.h" #include "../network/MessageSerializer.h" +#include namespace UNO::SERVER { UnoServer::UnoServer(uint16_t port) : networkServer_(port, [this](size_t playerId, const std::string &message) { this->handlePlayerMessage(playerId, message); }), playerCount(0) { + SPDLOG_INFO("UnoServer initialized on port {}", port); } void UnoServer::handlePlayerMessage(size_t playerId, const std::string &message) @@ -20,6 +22,8 @@ namespace UNO::SERVER { auto playerMessage = NETWORK::MessageSerializer::deserialize(message); if (playerMessage.getMessageStatus() == NETWORK::MessageStatus::OK) { + SPDLOG_DEBUG("Processing message from player {}, type: {}", playerId, static_cast(playerMessage.getMessagePayloadType())); + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::JOIN_GAME) { auto playerName = std::get(playerMessage.getMessagePayload()).playerName; @@ -27,12 +31,15 @@ namespace UNO::SERVER { this->gameIdToNetworkId[this->playerCount] = playerId; this->playerCount++; this->serverGameState_.addPlayer(GAME::ServerPlayerState{playerName, 0, false}); + SPDLOG_INFO("Player {} joined with name '{}', game ID: {}", playerId, playerName, this->playerCount - 1); } - if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::START_GAME) { + else if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::START_GAME) { this->isReadyToStart[networkIdToGameId[playerId]] = true; + SPDLOG_INFO("Player {} (game ID: {}) is ready to start", playerId, networkIdToGameId[playerId]); for (size_t i = 0; i <= this->playerCount; i++) { if (i == this->playerCount) { + SPDLOG_INFO("All {} players ready, starting game", this->playerCount); this->handleStartGame(); break; } @@ -41,25 +48,41 @@ namespace UNO::SERVER { } } } - if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::INIT_GAME - || playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::END_GAME) { + else if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::INIT_GAME + || playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::END_GAME) { + SPDLOG_ERROR( + "Invalid message payload type from client {}: {}", playerId, static_cast(playerMessage.getMessagePayloadType())); throw std::invalid_argument("Invalid message payload type from client"); } - if (this->serverGameState_.getServerGameStage() == GAME::ServerGameStage::IN_GAME - && this->networkIdToGameId.at(playerId) != this->serverGameState_.getCurrentPlayerId()) { + else if (this->serverGameState_.getServerGameStage() == GAME::ServerGameStage::IN_GAME + && this->networkIdToGameId.at(playerId) != this->serverGameState_.getCurrentPlayerId()) { + SPDLOG_WARN("Player {} sent message but it's not their turn (current: {})", + this->networkIdToGameId.at(playerId), + this->serverGameState_.getCurrentPlayerId()); throw std::invalid_argument("Invalid player message: not this player's turn"); } - if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::DRAW_CARD) { + else if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::DRAW_CARD) { + SPDLOG_INFO("Player {} (game ID: {}) draws card", playerId, this->networkIdToGameId.at(playerId)); this->handleDrawCard(playerId); } - if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::PLAY_CARD) { - this->handlePlayCard(playerId, std::get(playerMessage.getMessagePayload()).card); + else if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::PLAY_CARD) { + auto card = std::get(playerMessage.getMessagePayload()).card; + SPDLOG_INFO("Player {} (game ID: {}) plays card: color={}, type={}", + playerId, + this->networkIdToGameId.at(playerId), + card.colorToString(), + card.typeToString()); + this->handlePlayCard(playerId, card); } } + else { + SPDLOG_ERROR("Received message with error status from player {}", playerId); + } } void UnoServer::handleStartGame() { + SPDLOG_INFO("Initializing game state"); serverGameState_.init(); std::vector players; players.reserve(serverGameState_.getPlayers().size()); @@ -67,18 +90,23 @@ namespace UNO::SERVER { players.emplace_back(player.getName(), player.getRemainingCardCount(), player.getIsUno()); } size_t currentPlayerIndex = serverGameState_.getCurrentPlayerId(); + SPDLOG_INFO("Game started, current player index: {}", currentPlayerIndex); + for (size_t i = 0; i < playerCount; i++) { NETWORK::InitGamePayload payload = { i, players, serverGameState_.getDiscardPile(), serverGameState_.getPlayers()[i].getCards(), currentPlayerIndex}; this->networkServer_.send( gameIdToNetworkId.at(i), NETWORK::MessageSerializer::serialize({NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::INIT_GAME, payload})); + SPDLOG_DEBUG("Sent INIT_GAME to player {}", i); } } void UnoServer::handleDrawCard(size_t playerId) { auto cards = this->serverGameState_.updateStateByDraw(); + SPDLOG_INFO("Player {} drew {} card(s)", this->networkIdToGameId.at(playerId), cards.size()); + for (size_t i = 0; i < playerCount; i++) { auto networkId = gameIdToNetworkId.at(i); NETWORK::DrawCardPayload payload; @@ -103,6 +131,7 @@ namespace UNO::SERVER { for (const auto &player : this->serverGameState_.getPlayers()) { if (player.isEmpty()) { gameEnded = true; + SPDLOG_INFO("Player '{}' wins the game!", player.getName()); break; } } @@ -120,6 +149,7 @@ namespace UNO::SERVER { void UnoServer::handleEndGame() { + SPDLOG_INFO("Game ended, resetting to pre-game state"); this->serverGameState_.endGame(); NETWORK::EndGamePayload payload{}; @@ -136,6 +166,7 @@ namespace UNO::SERVER { void UnoServer::run() { + SPDLOG_INFO("UnoServer starting"); this->networkServer_.run(); } diff --git a/src/server/main.cpp b/src/server/main.cpp index dc10f46..99f64a3 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -4,11 +4,16 @@ * @author Yuzhe Guo * @date 2025.12.01 */ +#include "../common/Logger.h" #include +#include #include "UnoServer.h" int main(int argc, char *argv[]) { + UNO::COMMON::Logger::init("uno-server"); + SPDLOG_INFO("Starting uno-server"); + argparse::ArgumentParser parser("Uno Server", "0.1.0"); parser.add_argument("-p", "--port").help("server port").default_value(static_cast(10001)).scan<'i', uint16_t>(); @@ -17,17 +22,18 @@ int main(int argc, char *argv[]) parser.parse_args(argc, argv); } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; - std::cerr << parser; + SPDLOG_ERROR("Argument parsing failed: {}", e.what()); return 1; } try { - UNO::SERVER::UnoServer uno_server(parser.get("--port")); + auto port = parser.get("--port"); + SPDLOG_INFO("Launching server on port {}", port); + UNO::SERVER::UnoServer uno_server(port); uno_server.run(); } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; + SPDLOG_ERROR("Server crashed with exception: {}", e.what()); return 1; } diff --git a/src/ui/GameUI.cpp b/src/ui/GameUI.cpp index 920e09a..650ec69 100644 --- a/src/ui/GameUI.cpp +++ b/src/ui/GameUI.cpp @@ -49,7 +49,7 @@ namespace UNO::UI { for (auto color : GAME::AllColors) { for (auto type : GAME::AllTypes) { this->images_[{color, type}] = - slint::Image::load_from_path(std::format("../assets/cards/{}.svg", GAME::Card{color, type}.toString()).data()); + slint::Image::load_from_path(std::format("assets/cards/{}.svg", GAME::Card{color, type}.toString()).data()); } } }