mirror of
https://github.com/kierankihn/uno-game.git
synced 2025-12-27 02:13:18 +08:00
feat(server): implement Uno server functionality
- Added `UnoServer` class to handle player actions, game state, and networking. - Supported message handling for joining, starting, drawing, and playing cards. - Integrated `ServerGameState` with `NetworkServer` for multiplayer game logic. - Created `main.cpp` to initialize and run the Uno server.
This commit is contained in:
105
src/server/UnoServer.cpp
Normal file
105
src/server/UnoServer.cpp
Normal file
@@ -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<NETWORK::JoinGamePayload>(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<NETWORK::PlayCardPayload>(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
|
||||||
58
src/server/UnoServer.h
Normal file
58
src/server/UnoServer.h
Normal file
@@ -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<size_t, size_t> gameIdToNetworkId;
|
||||||
|
std::map<size_t, size_t> networkIdToGameId;
|
||||||
|
std::map<size_t, bool> 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
|
||||||
14
src/server/main.cpp
Normal file
14
src/server/main.cpp
Normal file
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user