diff --git a/src/server/UnoServer.cpp b/src/server/UnoServer.cpp new file mode 100644 index 0000000..812f464 --- /dev/null +++ b/src/server/UnoServer.cpp @@ -0,0 +1,105 @@ +/** + * @file UnoServer.cpp + * + * @author Yuzhe Guo + * @date 2025.12.01 + */ +#include "UnoServer.h" + +#include "../network/MessageSerializer.h" + +namespace UNO::SERVER { + UnoServer::UnoServer(uint16_t port) : + networkServer_(port, [this](int playerId, const std::string &message) { this->handlePlayerMessage(playerId, message); }), + playerCount(0) + { + } + + void UnoServer::handlePlayerMessage(size_t playerId, const std::string &message) + { + auto playerMessage = NETWORK::MessageSerializer::deserialize(message); + + if (playerMessage.getMessageStatus() == NETWORK::MessageStatus::OK) { + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::JOIN_GAME) { + auto playerName = std::get(playerMessage.getMessagePayload()).playerName; + + this->networkIdToGameId[playerId] = this->playerCount; + this->gameIdToNetworkId[this->playerCount] = playerId; + this->playerCount++; + this->serverGameState_.addPlayer(GAME::ServerPlayerState{playerName, 0, false}); + } + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::START_GAME) { + this->isReadyToStart[networkIdToGameId[playerId]] = true; + + for (size_t i = 0; i <= this->playerCount; i++) { + if (i == this->playerCount) { + this->handleStartGame(); + break; + } + if (isReadyToStart[i] == false) { + break; + } + } + } + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::INIT_GAME + || playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::END_GAME) { + throw std::invalid_argument("Invalid message payload type from client"); + } + if (this->networkIdToGameId.at(playerId) + != this->serverGameState_.getCurrentPlayer() - this->serverGameState_.getPlayers().begin()) { + throw std::invalid_argument("Invalid player message: not this player's turn"); + } + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::DRAW_CARD) { + this->handleDrawCard(playerId); + } + if (playerMessage.getMessagePayloadType() == NETWORK::MessagePayloadType::PLAY_CARD) { + this->handlePlayCard(playerId, std::get(playerMessage.getMessagePayload()).card); + } + } + } + + void UnoServer::handleStartGame() + { + serverGameState_.init(); + for (size_t i = 0; i < playerCount; i++) { + NETWORK::InitGamePayload payload = {serverGameState_.getDiscardPile(), serverGameState_.getPlayers()[i].getCards(), 0}; + this->networkServer_.send( + gameIdToNetworkId.at(i), + NETWORK::MessageSerializer::serialize({NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::INIT_GAME, payload})); + } + } + + void UnoServer::handleDrawCard(size_t playerId) + { + auto cards = this->serverGameState_.updateStateByDraw(); + for (size_t i = 0; i < playerCount; i++) { + auto networkId = gameIdToNetworkId.at(i); + NETWORK::DrawCardPayload payload; + if (networkId != playerId) { + payload = {cards.size(), {}}; + } + else { + payload = {cards.size(), cards}; + } + this->networkServer_.send( + networkId, + NETWORK::MessageSerializer::serialize({NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::DRAW_CARD, payload})); + } + } + + void UnoServer::handlePlayCard(size_t playerId, GAME::Card card) + { + this->serverGameState_.updateStateByCard(card); + NETWORK::PlayCardPayload payload = {card}; + auto message = NETWORK::MessageSerializer::serialize({NETWORK::MessageStatus::OK, NETWORK::MessagePayloadType::PLAY_CARD, payload}); + for (size_t i = 0; i < playerCount; i++) { + this->networkServer_.send(gameIdToNetworkId.at(i), message); + } + } + + void UnoServer::run() + { + this->networkServer_.run(); + } + +} // namespace UNO::SERVER \ No newline at end of file diff --git a/src/server/UnoServer.h b/src/server/UnoServer.h new file mode 100644 index 0000000..53af658 --- /dev/null +++ b/src/server/UnoServer.h @@ -0,0 +1,58 @@ +/** + * @file UnoServer.h + * + * @author Yuzhe Guo + * @date 2025.12.01 + */ +#pragma once +#include "../game/GameState.h" +#include "../network/NetworkServer.h" + +namespace UNO::SERVER { + + class UnoServer { + private: + GAME::ServerGameState serverGameState_; + NETWORK::NetworkServer networkServer_; + + size_t playerCount; + std::map gameIdToNetworkId; + std::map networkIdToGameId; + std::map isReadyToStart; + + private: + /** + * 处理玩家消息 + * @param playerId 玩家 ID + * @param message 玩家消息 + */ + void handlePlayerMessage(size_t playerId, const std::string &message); + + /** + * 开始游戏 + */ + void handleStartGame(); + + /** + * 处理玩家摸牌事件 + * @param playerId 摸牌的玩家的网络 ID + */ + void handleDrawCard(size_t playerId); + + /** + * 处理玩家出牌事件 + * @param playerId 出牌的玩家的网络 ID + * @param card 玩家出的牌 + */ + void handlePlayCard(size_t playerId, GAME::Card card); + + public: + explicit UnoServer(uint16_t port = 10001); + + /** + * 启动服务器 + */ + void run(); + }; + +} // namespace UNO::SERVER \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp new file mode 100644 index 0000000..795ea04 --- /dev/null +++ b/src/server/main.cpp @@ -0,0 +1,14 @@ +/** + * @file main.cpp + * + * @author Yuzhe Guo + * @date 2025.12.01 + */ + +#include "UnoServer.h" +int main() +{ + UNO::SERVER::UnoServer uno_server; + uno_server.run(); + return 0; +} \ No newline at end of file