feat(network): add PlayerPublicState to InitGamePayload

- Introduced `PlayerPublicState` structure to represent player state.
- Updated `InitGamePayload` to include `PlayerPublicState` for all players.
- Enhanced `MessageSerializer` to handle serialization and deserialization of `PlayerPublicState`.
This commit is contained in:
Kieran Kihn
2025-12-04 17:15:49 +08:00
parent a136e309e6
commit 024f99fc01
4 changed files with 75 additions and 5 deletions

View File

@@ -13,6 +13,7 @@
#include <string>
#include <variant>
#include <vector>
namespace UNO::NETWORK {
@@ -29,12 +30,19 @@ namespace UNO::NETWORK {
std::vector<GAME::Card> cards;
};
struct PlayerPublicState {
std::string name;
size_t remainingCardCount;
bool isUno;
};
struct PlayCardPayload {
GAME::Card card;
};
struct InitGamePayload {
size_t playerId;
std::vector<PlayerPublicState> players;
GAME::DiscardPile discardPile;
std::multiset<GAME::Card> handCard;
size_t currentPlayerIndex;

View File

@@ -28,6 +28,20 @@ namespace UNO::NETWORK {
return serializeCards(cards.begin(), cards.end());
}
nlohmann::json MessageSerializer::serializePlayerPublicState(const PlayerPublicState &state)
{
return {{"name", state.name}, {"remaining_cards", state.remainingCardCount}, {"is_uno", state.isUno}};
}
nlohmann::json MessageSerializer::serializePlayerPublicStates(const std::vector<PlayerPublicState> &states)
{
nlohmann::json result = nlohmann::json::array();
for (const auto &state : states) {
result.push_back(serializePlayerPublicState(state));
}
return result;
}
nlohmann::json MessageSerializer::serializePayload(const std::monostate &payload)
{
return nullptr;
@@ -56,6 +70,7 @@ namespace UNO::NETWORK {
nlohmann::json MessageSerializer::serializePayload(const InitGamePayload &payload)
{
return {{"player_id", payload.playerId},
{"players", serializePlayerPublicStates(payload.players)},
{"discard_pile", serializeDiscardPile(payload.discardPile)},
{"hand_card", serializeCards(payload.handCard.begin(), payload.handCard.end())},
{"current_player", payload.currentPlayerIndex}};
@@ -215,6 +230,41 @@ namespace UNO::NETWORK {
return res;
}
PlayerPublicState MessageSerializer::deserializePlayerPublicState(const nlohmann::json &payload)
{
try {
if (payload.is_object() == false) {
throw std::invalid_argument("Invalid player public state: expected JSON object");
}
if (payload.at("name").is_string() == false) {
throw std::invalid_argument("Invalid 'name' field in player public state: expected string");
}
if (payload.at("remaining_cards").is_number_unsigned() == false) {
throw std::invalid_argument("Invalid 'remaining_cards' field in player public state: expected unsigned integer");
}
if (payload.at("is_uno").is_boolean() == false) {
throw std::invalid_argument("Invalid 'is_uno' field in player public state: expected boolean");
}
return {payload.at("name"), payload.at("remaining_cards"), payload.at("is_uno")};
}
catch (const nlohmann::json::out_of_range &) {
throw std::invalid_argument("Missing required field in player public state: expected 'name', 'remaining_cards', 'is_uno'");
}
}
std::vector<PlayerPublicState> MessageSerializer::deserializePlayerPublicStates(const nlohmann::json &payload)
{
if (payload.is_array() == false) {
throw std::invalid_argument("Invalid players field in INIT_GAME payload: expected JSON array");
}
std::vector<PlayerPublicState> players;
for (const auto &entry : payload) {
players.push_back(deserializePlayerPublicState(entry));
}
return players;
}
std::monostate MessageSerializer::deserializeEmptyPayload(const nlohmann::json &payload)
{
if (payload.is_null() == false) {
@@ -299,13 +349,14 @@ namespace UNO::NETWORK {
throw std::invalid_argument("Invalid 'current_player' field in INIT_GAME payload: expected unsigned integer");
}
return {payload.at("player_id"),
deserializePlayerPublicStates(payload.at("players")),
deserializeDiscardPile(payload.at("discard_pile")),
deserializeHandCard(payload.at("hand_card")),
payload.at("current_player")};
}
catch (const nlohmann::json::out_of_range &) {
throw std::invalid_argument(
"Missing required field in INIT_GAME payload: expected 'discard_pile', 'hand_card', and 'current_player'");
"Missing required field in INIT_GAME payload: expected 'players', 'discard_pile', 'hand_card', and 'current_player'");
}
}
@@ -415,4 +466,4 @@ namespace UNO::NETWORK {
}
}
} // namespace UNO::NETWORK
} // namespace UNO::NETWORK

View File

@@ -24,6 +24,8 @@ namespace UNO::NETWORK {
static nlohmann::json serializeCards(Iterator begin, Iterator end);
static nlohmann::json serializeDiscardPile(const GAME::DiscardPile &discardPile);
static nlohmann::json serializePlayerPublicState(const PlayerPublicState &state);
static nlohmann::json serializePlayerPublicStates(const std::vector<PlayerPublicState> &states);
static nlohmann::json serializePayload(const std::monostate &payload);
static nlohmann::json serializePayload(const JoinGamePayload &payload);
@@ -43,6 +45,8 @@ namespace UNO::NETWORK {
static GAME::Card deserializeCard(const nlohmann::json &card);
static GAME::DiscardPile deserializeDiscardPile(const nlohmann::json &discardPile);
static std::multiset<GAME::Card> deserializeHandCard(const nlohmann::json &handCard);
static PlayerPublicState deserializePlayerPublicState(const nlohmann::json &payload);
static std::vector<PlayerPublicState> deserializePlayerPublicStates(const nlohmann::json &payload);
static std::monostate deserializeEmptyPayload(const nlohmann::json &payload);
static JoinGamePayload deserializeJoinGamePayload(const nlohmann::json &payload);
@@ -58,4 +62,4 @@ namespace UNO::NETWORK {
static Message deserializeMessage(const nlohmann::json &message);
};
} // namespace UNO::NETWORK
} // namespace UNO::NETWORK

View File

@@ -61,8 +61,15 @@ namespace UNO::SERVER {
void UnoServer::handleStartGame()
{
serverGameState_.init();
std::vector<NETWORK::PlayerPublicState> players;
players.reserve(serverGameState_.getPlayers().size());
for (const auto &player : serverGameState_.getPlayers()) {
players.push_back({player.getName(), player.getRemainingCardCount(), player.getIsUno()});
}
size_t currentPlayerIndex = static_cast<size_t>(serverGameState_.getCurrentPlayer() - serverGameState_.getPlayers().begin());
for (size_t i = 0; i < playerCount; i++) {
NETWORK::InitGamePayload payload = {i, serverGameState_.getDiscardPile(), serverGameState_.getPlayers()[i].getCards(), 0};
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}));
@@ -132,4 +139,4 @@ namespace UNO::SERVER {
this->networkServer_.run();
}
} // namespace UNO::SERVER
} // namespace UNO::SERVER