Move game state and event handling out of tab_game and into separate classes (#6090)

* Move game state and event handling out of tab_game and into separate classes.

Took 6 hours 38 minutes

Took 23 seconds

* Meta Info

Took 14 hours 36 minutes

* Properly respond to game started again.

Took 49 minutes

* Hook up the message log widgets to game events again.

Took 33 minutes

Took 7 seconds

* Lint.

Took 4 minutes

* Hook up playerListWidget.

Took 1 hour 2 minutes

Took 10 seconds

* Hook up playerListWidget properly.

Took 1 hour 17 minutes

* Fix regressions.

Took 17 minutes

Took 9 seconds

* Log the local player joining too.

Took 2 minutes

* Connect some player signals unrelated to this refactor again.

Took 5 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-09-11 00:40:29 +02:00 committed by GitHub
parent 5c16f0d027
commit b8e545bfa4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1482 additions and 788 deletions

View file

@ -207,8 +207,11 @@ set(cockatrice_SOURCES
src/game/filters/filter_tree.cpp
src/game/filters/filter_tree_model.cpp
src/game/filters/syntax_help.cpp
src/game/game_event_handler.cpp
src/game/game_meta_info.cpp
src/game/game_scene.cpp
src/game/game_selector.cpp
src/game/game_state.cpp
src/game/game_view.cpp
src/game/games_model.cpp
src/game/hand_counter.cpp

View file

@ -95,7 +95,8 @@ ReplayManager::ReplayManager(TabGame *parent, GameReplay *_replay)
void ReplayManager::replayNextEvent(Player::EventProcessingOptions options)
{
game->processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), nullptr, options);
game->getGameEventHandler()->processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()),
nullptr, options);
}
void ReplayManager::replayFinished()

File diff suppressed because it is too large Load diff

View file

@ -2,17 +2,20 @@
#define TAB_GAME_H
#include "../../client/tearoff_menu.h"
#include "../../game/game_event_handler.h"
#include "../../game/game_meta_info.h"
#include "../../game/game_state.h"
#include "../../game/player/player.h"
#include "../replay_manager.h"
#include "../ui/widgets/visual_deck_storage/visual_deck_storage_widget.h"
#include "pb/event_leave.pb.h"
#include "pb/serverinfo_game.pb.h"
#include "tab.h"
#include <QCompleter>
#include <QLoggingCategory>
#include <QMap>
class ServerInfo_PlayerProperties;
class TabbedDeckViewContainer;
inline Q_LOGGING_CATEGORY(TabGameLog, "tab_game");
@ -24,7 +27,6 @@ class GameView;
class GameScene;
class ReplayManager;
class CardInfoFrameWidget;
class MessageLogWidget;
class QTimer;
class QSplitter;
class QLabel;
@ -35,25 +37,6 @@ class ZoneViewWidget;
class PhasesToolbar;
class PlayerListWidget;
class ReplayTimelineWidget;
class Response;
class GameEventContainer;
class GameEventContext;
class GameCommand;
class CommandContainer;
class Event_GameJoined;
class Event_GameStateChanged;
class Event_PlayerPropertiesChanged;
class Event_Join;
class Event_Leave;
class Event_GameHostChanged;
class Event_GameClosed;
class Event_GameStart;
class Event_SetActivePlayer;
class Event_SetActivePhase;
class Event_Ping;
class Event_GameSay;
class Event_Kicked;
class Event_ReverseTurn;
class CardZone;
class AbstractCardItem;
class CardItem;
@ -61,8 +44,6 @@ class DeckLoader;
class QVBoxLayout;
class QHBoxLayout;
class GameReplay;
class ServerInfo_User;
class PendingCommand;
class LineEditCompleter;
class QDockWidget;
class QStackedWidget;
@ -71,26 +52,11 @@ class TabGame : public Tab
{
Q_OBJECT
private:
QTimer *gameTimer;
int secondsElapsed;
GameMetaInfo *gameMetaInfo;
GameState *gameState;
GameEventHandler *gameEventHandler;
const UserListProxy *userListProxy;
QList<AbstractClient *> clients;
ServerInfo_Game gameInfo;
QMap<int, QString> roomGameTypes;
int hostId;
int localPlayerId;
const bool isLocalGame;
bool spectator;
bool judge;
QMap<int, Player *> players;
QMap<int, ServerInfo_User> spectators;
bool gameStateKnown;
bool resuming;
QStringList phasesList;
int currentPhase;
int activePlayer;
CardItem *activeCard;
bool gameClosed;
ReplayManager *replayManager;
QStringList gameTypes;
QCompleter *completer;
@ -121,34 +87,22 @@ private:
QList<QAction *> phaseActions;
QAction *aCardMenu;
Player *addPlayer(int playerId, const ServerInfo_User &info);
bool isMainPlayerConceded() const;
Player *addPlayer(Player *newPlayer);
void addLocalPlayer(Player *newPlayer, int playerId);
void processRemotePlayerDeckSelect(QString deckList, int playerId, QString playerName);
void processMultipleRemotePlayerDeckSelect(QVector<QPair<int, QPair<QString, QString>>> playerIdDeckMap);
void processLocalPlayerDeckSelect(Player *localPlayer, int playerId, ServerInfo_Player playerInfo);
void loadDeckForLocalPlayer(Player *localPlayer, int playerId, ServerInfo_Player playerInfo);
void processLocalPlayerReady(int playerId, ServerInfo_Player playerInfo);
void createZoneForPlayer(Player *newPlayer, int playerId);
void startGame(bool resuming);
void stopGame();
void closeGame();
bool leaveGame();
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
void eventGameStateChanged(const Event_GameStateChanged &event, int eventPlayerId, const GameEventContext &context);
void eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context);
void eventJoin(const Event_Join &event, int eventPlayerId, const GameEventContext &context);
void eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
void eventKicked(const Event_Kicked &event, int eventPlayerId, const GameEventContext &context);
void eventGameHostChanged(const Event_GameHostChanged &event, int eventPlayerId, const GameEventContext &context);
void eventGameClosed(const Event_GameClosed &event, int eventPlayerId, const GameEventContext &context);
Player *setActivePlayer(int id);
void eventSetActivePlayer(const Event_SetActivePlayer &event, int eventPlayerId, const GameEventContext &context);
void setActivePhase(int phase);
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext & /*context*/);
void emitUserEvent();
void createMenuItems();
void createReplayMenuItems();
void createViewMenuItems();
@ -158,7 +112,6 @@ private:
void createPlayAreaWidget(bool bReplay = false);
void createDeckViewContainerWidget(bool bReplay = false);
void createReplayDock(GameReplay *replay);
QString getLeaveReason(Event_Leave::LeaveReason reason);
signals:
void gameClosing(TabGame *tab);
void playerAdded(Player *player);
@ -169,8 +122,15 @@ signals:
void openDeckEditor(const DeckLoader *deck);
void notIdle();
void playerConceded();
void playerUnconceded();
void phaseChanged(int phase);
void gameLeft();
void chatMessageSent(QString chatMessage);
void turnAdvanced();
void arrowDeletionRequested(int arrowId);
private slots:
void incrementGameTime();
void adminLockChanged(bool lock);
void newCardAdded(AbstractCardItem *card);
void setCardMenu(QMenu *menu);
@ -184,17 +144,17 @@ private slots:
void actPhaseAction();
void actNextPhase();
void actNextPhaseAction();
void actNextTurn();
void actReverseTurn();
void addMentionTag(const QString &value);
void linkCardToChat(const QString &cardName);
void commandFinished(const Response &response);
void refreshShortcuts();
void loadLayout();
void actCompleterChanged();
void notifyPlayerJoin(QString playerName);
void notifyPlayerKicked();
void processPlayerLeave(Player *leavingPlayer);
void actResetLayout();
void freeDocksSize();
@ -212,39 +172,36 @@ public:
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes);
void connectToGameState();
void connectToGameEventHandler();
void connectMessageLogToGameEventHandler();
void connectPlayerListToGameEventHandler();
void loadReplay(GameReplay *replay);
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
~TabGame() override;
void retranslateUi() override;
void updatePlayerListDockTitle();
bool closeRequest() override;
const QMap<int, Player *> &getPlayers() const
GameMetaInfo *getGameMetaInfo()
{
return players;
return gameMetaInfo;
}
GameState *getGameState() const
{
return gameState;
}
GameEventHandler *getGameEventHandler() const
{
return gameEventHandler;
}
CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
bool isHost() const
{
return hostId == localPlayerId;
}
bool getIsLocalGame() const
{
return isLocalGame;
}
int getGameId() const
{
return gameInfo.game_id();
}
QString getTabText() const override;
bool isSpectator() const
{
return spectator;
}
bool isSpectatorsOmniscient() const
{
return gameInfo.spectators_omniscient();
}
Player *getActiveLocalPlayer() const;
AbstractClient *getClientForPlayer(int playerId) const;
void setActiveCard(CardItem *card);
@ -253,16 +210,16 @@ public:
return activeCard;
}
void processGameEventContainer(const GameEventContainer &cont,
AbstractClient *client,
Player::EventProcessingOptions options);
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
public slots:
void sendGameCommand(PendingCommand *pend, int playerId = -1);
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
void viewCardInfo(const CardRef &cardRef = {}) const;
void resetChatAndPhase();
void updateTimeElapsedLabel(QString newTime);
void addPlayerToAutoCompleteList(QString playerName);
void removePlayerFromAutoCompleteList(QString playerName);
void removeSpectator(int spectatorId, ServerInfo_User spectator);
void processLocalPlayerSideboardLocked(int playerId, bool sideboardLocked);
void processLocalPlayerReadyStateChanged(int playerId, bool ready);
void emitUserEvent();
};
#endif

View file

@ -707,7 +707,7 @@ void TabSupervisor::gameLeft(TabGame *tab)
if (tab == currentWidget())
emit setMenu();
gameTabs.remove(tab->getGameId());
gameTabs.remove(tab->getGameMetaInfo()->gameId());
removeTab(indexOf(tab));
if (!localClients.isEmpty())
@ -916,7 +916,7 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont)
{
TabGame *tab = gameTabs.value(cont.game_id());
if (tab)
tab->processGameEventContainer(cont, qobject_cast<AbstractClient *>(sender()), {});
tab->getGameEventHandler()->processGameEventContainer(cont, qobject_cast<AbstractClient *>(sender()), {});
else
qCInfo(TabSupervisorLog) << "gameEvent: invalid gameId" << cont.game_id();
}

View file

@ -259,10 +259,10 @@ void CardItem::deleteDragItem()
void CardItem::drawArrow(const QColor &arrowColor)
{
if (static_cast<TabGame *>(owner->parent())->isSpectator())
if (static_cast<TabGame *>(owner->parent())->getGameState()->isSpectator())
return;
Player *arrowOwner = static_cast<TabGame *>(owner->parent())->getActiveLocalPlayer();
Player *arrowOwner = static_cast<TabGame *>(owner->parent())->getGameState()->getActiveLocalPlayer();
ArrowDragItem *arrow = new ArrowDragItem(arrowOwner, this, arrowColor);
scene()->addItem(arrow);
arrow->grabMouse();
@ -282,7 +282,7 @@ void CardItem::drawArrow(const QColor &arrowColor)
void CardItem::drawAttachArrow()
{
if (static_cast<TabGame *>(owner->parent())->isSpectator())
if (static_cast<TabGame *>(owner->parent())->getGameState()->isSpectator())
return;
auto *arrow = new ArrowAttachItem(this);

View file

@ -157,7 +157,7 @@ void DeckViewContainer::switchToDeckSelectView()
deckViewLayout->update();
setVisibility(loadLocalButton, true);
setVisibility(loadRemoteButton, !parentGame->getIsLocalGame());
setVisibility(loadRemoteButton, !parentGame->getGameState()->getIsLocalGame());
setVisibility(loadFromClipboardButton, true);
setVisibility(loadFromWebsiteButton, true);
setVisibility(unloadDeckButton, false);
@ -190,7 +190,7 @@ void DeckViewContainer::switchToDeckLoadedView()
setVisibility(readyStartButton, true);
setVisibility(sideboardLockButton, true);
if (parentGame->isHost()) {
if (parentGame->getGameState()->isHost()) {
setVisibility(forceStartGameButton, true);
}
}
@ -287,9 +287,9 @@ void DeckViewContainer::loadDeckFromDeckLoader(const DeckLoader *deck)
Command_DeckSelect cmd;
cmd.set_deck(deckString.toStdString());
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
PendingCommand *pend = parentGame->getGameEventHandler()->prepareGameCommand(cmd);
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
parentGame->sendGameCommand(pend, playerId);
parentGame->getGameEventHandler()->sendGameCommand(pend, playerId);
}
void DeckViewContainer::loadRemoteDeck()
@ -298,9 +298,9 @@ void DeckViewContainer::loadRemoteDeck()
if (dlg.exec()) {
Command_DeckSelect cmd;
cmd.set_deck_id(dlg.getDeckId());
PendingCommand *pend = parentGame->prepareGameCommand(cmd);
PendingCommand *pend = parentGame->getGameEventHandler()->prepareGameCommand(cmd);
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
parentGame->sendGameCommand(pend, playerId);
parentGame->getGameEventHandler()->sendGameCommand(pend, playerId);
}
}
@ -354,7 +354,7 @@ void DeckViewContainer::forceStart()
Command_ReadyStart cmd;
cmd.set_force_start(true);
cmd.set_ready(true);
parentGame->sendGameCommand(cmd, playerId);
parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardLockButtonClicked()
@ -362,7 +362,7 @@ void DeckViewContainer::sideboardLockButtonClicked()
Command_SetSideboardLock cmd;
cmd.set_locked(sideboardLockButton->getState());
parentGame->sendGameCommand(cmd, playerId);
parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId);
}
void DeckViewContainer::sideboardPlanChanged()
@ -371,7 +371,7 @@ void DeckViewContainer::sideboardPlanChanged()
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
for (const auto &i : newPlan)
cmd.add_move_list()->CopyFrom(i);
parentGame->sendGameCommand(cmd, playerId);
parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId);
}
/**
@ -381,7 +381,7 @@ void DeckViewContainer::sendReadyStartCommand(bool ready)
{
Command_ReadyStart cmd;
cmd.set_ready(ready);
parentGame->sendGameCommand(cmd, playerId);
parentGame->getGameEventHandler()->sendGameCommand(cmd, playerId);
}
/**

View file

@ -0,0 +1,517 @@
#include "game_event_handler.h"
#include "../client/tabs/tab_game.h"
#include "../server/abstract_client.h"
#include "../server/message_log_widget.h"
#include "../server/pending_command.h"
#include "get_pb_extension.h"
#include "pb/command_concede.pb.h"
#include "pb/command_delete_arrow.pb.h"
#include "pb/command_game_say.pb.h"
#include "pb/command_leave_game.pb.h"
#include "pb/command_next_turn.pb.h"
#include "pb/command_reverse_turn.pb.h"
#include "pb/command_set_active_phase.pb.h"
#include "pb/context_connection_state_changed.pb.h"
#include "pb/context_deck_select.pb.h"
#include "pb/context_ping_changed.pb.h"
#include "pb/event_game_closed.pb.h"
#include "pb/event_game_host_changed.pb.h"
#include "pb/event_game_say.pb.h"
#include "pb/event_game_state_changed.pb.h"
#include "pb/event_join.pb.h"
#include "pb/event_kicked.pb.h"
#include "pb/event_leave.pb.h"
#include "pb/event_player_properties_changed.pb.h"
#include "pb/event_reverse_turn.pb.h"
#include "pb/event_set_active_phase.pb.h"
#include "pb/event_set_active_player.pb.h"
#include "pb/game_event_container.pb.h"
GameEventHandler::GameEventHandler(TabGame *_game) : game(_game), gameState(_game->getGameState())
{
}
void GameEventHandler::sendGameCommand(PendingCommand *pend, int playerId)
{
AbstractClient *client = game->getClientForPlayer(playerId);
if (!client)
return;
connect(pend, &PendingCommand::finished, this, &GameEventHandler::commandFinished);
client->sendCommand(pend);
}
void GameEventHandler::sendGameCommand(const google::protobuf::Message &command, int playerId)
{
AbstractClient *client = game->getClientForPlayer(playerId);
if (!client)
return;
PendingCommand *pend = prepareGameCommand(command);
connect(pend, &PendingCommand::finished, this, &GameEventHandler::commandFinished);
client->sendCommand(pend);
}
void GameEventHandler::commandFinished(const Response &response)
{
if (response.response_code() == Response::RespChatFlood)
emit gameFlooded();
}
PendingCommand *GameEventHandler::prepareGameCommand(const ::google::protobuf::Message &cmd)
{
CommandContainer cont;
cont.set_game_id(static_cast<google::protobuf::uint32>(game->getGameMetaInfo()->gameId()));
GameCommand *c = cont.add_game_command();
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
return new PendingCommand(cont);
}
PendingCommand *GameEventHandler::prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList)
{
CommandContainer cont;
cont.set_game_id(static_cast<google::protobuf::uint32>(game->getGameMetaInfo()->gameId()));
for (auto i : cmdList) {
GameCommand *c = cont.add_game_command();
c->GetReflection()->MutableMessage(c, i->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*i);
delete i;
}
return new PendingCommand(cont);
}
void GameEventHandler::processGameEventContainer(const GameEventContainer &cont,
AbstractClient *client,
Player::EventProcessingOptions options)
{
const GameEventContext &context = cont.context();
emit containerProcessingStarted(context);
const int eventListSize = cont.event_list_size();
for (int i = 0; i < eventListSize; ++i) {
const GameEvent &event = cont.event_list(i);
const int playerId = event.player_id();
const auto eventType = static_cast<GameEvent::GameEventType>(getPbExtension(event));
if (cont.has_forced_by_judge()) {
auto id = cont.forced_by_judge();
Player *judgep = gameState->getPlayers().value(id, nullptr);
if (judgep) {
emit setContextJudgeName(judgep->getName());
} else if (gameState->getSpectators().contains(id)) {
emit setContextJudgeName(QString::fromStdString(gameState->getSpectators().value(id).name()));
}
}
if (gameState->getSpectators().contains(playerId)) {
switch (eventType) {
case GameEvent::GAME_SAY:
eventSpectatorSay(event.GetExtension(Event_GameSay::ext), playerId, context);
break;
case GameEvent::LEAVE:
eventSpectatorLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
default:
break;
}
} else {
if ((gameState->getClients().size() > 1) && (playerId != -1))
if (gameState->getClients().at(playerId) != client)
continue;
switch (eventType) {
case GameEvent::GAME_STATE_CHANGED:
qInfo() << "Game state changed event";
eventGameStateChanged(event.GetExtension(Event_GameStateChanged::ext), playerId, context);
break;
case GameEvent::PLAYER_PROPERTIES_CHANGED:
qInfo() << "player prop event";
eventPlayerPropertiesChanged(event.GetExtension(Event_PlayerPropertiesChanged::ext), playerId,
context);
break;
case GameEvent::JOIN:
qInfo() << "join event";
eventJoin(event.GetExtension(Event_Join::ext), playerId, context);
break;
case GameEvent::LEAVE:
qInfo() << "leave event";
eventLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
case GameEvent::KICKED:
qInfo() << "kicked event";
eventKicked(event.GetExtension(Event_Kicked::ext), playerId, context);
break;
case GameEvent::GAME_HOST_CHANGED:
qInfo() << "host changed event";
eventGameHostChanged(event.GetExtension(Event_GameHostChanged::ext), playerId, context);
break;
case GameEvent::GAME_CLOSED:
qInfo() << "game closed event";
eventGameClosed(event.GetExtension(Event_GameClosed::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PLAYER:
qInfo() << "set active player event";
eventSetActivePlayer(event.GetExtension(Event_SetActivePlayer::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PHASE:
qInfo() << "set active phase";
eventSetActivePhase(event.GetExtension(Event_SetActivePhase::ext), playerId, context);
break;
case GameEvent::REVERSE_TURN:
qInfo() << "reverse turn event";
eventReverseTurn(event.GetExtension(Event_ReverseTurn::ext), playerId, context);
break;
default: {
Player *player = gameState->getPlayers().value(playerId, 0);
if (!player) {
// qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id";
break;
}
player->processGameEvent(eventType, event, context, options);
game->emitUserEvent();
}
}
}
}
emit containerProcessingDone();
}
void GameEventHandler::handleNextTurn()
{
sendGameCommand(Command_NextTurn());
}
void GameEventHandler::handleReverseTurn()
{
sendGameCommand(Command_ReverseTurn());
}
void GameEventHandler::handlePlayerConceded()
{
sendGameCommand(Command_Concede());
}
void GameEventHandler::handlePlayerUnconceded()
{
sendGameCommand(Command_Unconcede());
}
void GameEventHandler::handleActivePhaseChanged(int phase)
{
Command_SetActivePhase cmd;
cmd.set_phase(static_cast<google::protobuf::uint32>(phase));
sendGameCommand(cmd);
}
void GameEventHandler::handleGameLeft()
{
sendGameCommand(Command_LeaveGame());
}
void GameEventHandler::handleChatMessageSent(const QString &chatMessage)
{
Command_GameSay cmd;
cmd.set_message(chatMessage.toStdString());
sendGameCommand(cmd);
}
void GameEventHandler::handleArrowDeletion(int arrowId)
{
Command_DeleteArrow cmd;
cmd.set_arrow_id(arrowId);
sendGameCommand(cmd);
}
void GameEventHandler::eventSpectatorSay(const Event_GameSay &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
const ServerInfo_User &userInfo = gameState->getSpectators().value(eventPlayerId);
emit logSpectatorSay(userInfo, QString::fromStdString(event.message()));
}
void GameEventHandler::eventSpectatorLeave(const Event_Leave &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
emit logSpectatorLeave(gameState->getSpectatorName(eventPlayerId), getLeaveReason(event.reason()));
emit spectatorLeft(eventPlayerId);
gameState->removeSpectator(eventPlayerId);
game->emitUserEvent();
}
void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int playerListSize = event.player_list_size();
QVector<QPair<int, QPair<QString, QString>>> opponentDecksToDisplay;
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
const int playerId = prop.player_id();
QString playerName = "@" + QString::fromStdString(prop.user_info().name());
game->addPlayerToAutoCompleteList(playerName);
if (prop.spectator()) {
gameState->addSpectator(playerId, prop);
} else {
Player *player = gameState->getPlayers().value(playerId, 0);
if (!player) {
player = gameState->addPlayer(playerId, prop.user_info(), game);
emit playerJoined(prop);
emit logJoinPlayer(player);
}
player->processPlayerInfo(playerInfo);
if (player->getLocal()) {
emit localPlayerDeckSelected(player, playerId, playerInfo);
} else {
if (!game->getGameMetaInfo()->proto().share_decklists_on_load()) {
continue;
}
opponentDecksToDisplay.append(
qMakePair(playerId, qMakePair(playerName, QString::fromStdString(playerInfo.deck_list()))));
}
}
}
processCardAttachmentsForPlayers(event);
emit remotePlayersDecksSelected(opponentDecksToDisplay);
gameState->setGameTime(event.seconds_elapsed());
if (event.game_started() && !game->getGameMetaInfo()->started()) {
gameState->setResuming(!gameState->isGameStateKnown());
game->getGameMetaInfo()->setStarted(event.game_started());
if (gameState->isGameStateKnown())
emit logGameStart();
gameState->setActivePlayer(event.active_player_id());
gameState->setCurrentPhase(event.active_phase());
} else if (!event.game_started() && game->getGameMetaInfo()->started()) {
gameState->setCurrentPhase(-1);
gameState->setActivePlayer(-1);
game->getGameMetaInfo()->setStarted(false);
emit gameStopped();
}
gameState->setGameStateKnown(true);
game->emitUserEvent();
}
void GameEventHandler::processCardAttachmentsForPlayers(const Event_GameStateChanged &event)
{
for (int i = 0; i < event.player_list_size(); ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
if (!prop.spectator()) {
Player *player = gameState->getPlayers().value(prop.player_id(), 0);
if (!player)
continue;
player->processCardAttachment(playerInfo);
}
}
}
void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context)
{
Player *player = gameState->getPlayers().value(eventPlayerId, 0);
if (!player)
return;
const ServerInfo_PlayerProperties &prop = event.player_properties();
emit playerPropertiesChanged(prop, eventPlayerId);
const auto contextType = static_cast<GameEventContext::ContextType>(getPbExtension(context));
switch (contextType) {
case GameEventContext::READY_START: {
bool ready = prop.ready_start();
if (player->getLocal())
emit localPlayerReadyStateChanged(player->getId(), ready);
if (ready) {
emit logReadyStart(player);
} else {
emit logNotReadyStart(player);
}
break;
}
case GameEventContext::CONCEDE: {
emit playerConceded(player);
player->setConceded(true);
QMapIterator<int, Player *> playerIterator(gameState->getPlayers());
while (playerIterator.hasNext())
playerIterator.next().value()->updateZones();
break;
}
case GameEventContext::UNCONCEDE: {
emit playerUnconceded(player);
player->setConceded(false);
QMapIterator<int, Player *> playerIterator(gameState->getPlayers());
while (playerIterator.hasNext())
playerIterator.next().value()->updateZones();
break;
}
case GameEventContext::DECK_SELECT: {
Context_DeckSelect deckSelect = context.GetExtension(Context_DeckSelect::ext);
emit logDeckSelect(player, QString::fromStdString(deckSelect.deck_hash()), deckSelect.sideboard_size());
if (game->getGameMetaInfo()->proto().share_decklists_on_load() && deckSelect.has_deck_list() &&
eventPlayerId != gameState->getLocalPlayerId()) {
emit remotePlayerDeckSelected(QString::fromStdString(deckSelect.deck_list()), eventPlayerId,
player->getName());
}
break;
}
case GameEventContext::SET_SIDEBOARD_LOCK: {
if (player->getLocal()) {
emit localPlayerSideboardLocked(player->getId(), prop.sideboard_locked());
}
emit logSideboardLockSet(player, prop.sideboard_locked());
break;
}
case GameEventContext::CONNECTION_STATE_CHANGED: {
emit logConnectionStateChanged(player, prop.ping_seconds() != -1);
break;
}
default:;
}
}
void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const GameEventContext & /*context*/)
{
const ServerInfo_PlayerProperties &playerInfo = event.player_properties();
const int playerId = playerInfo.player_id();
QString playerName = QString::fromStdString(playerInfo.user_info().name());
game->addPlayerToAutoCompleteList(playerName);
if (gameState->getPlayers().contains(playerId))
return;
if (playerInfo.spectator()) {
gameState->addSpectator(playerId, playerInfo);
emit logJoinSpectator(playerName);
emit spectatorJoined(playerInfo);
} else {
Player *newPlayer = gameState->addPlayer(playerId, playerInfo.user_info(), game);
emit logJoinPlayer(newPlayer);
emit playerJoined(playerInfo);
}
game->emitUserEvent();
}
QString GameEventHandler::getLeaveReason(Event_Leave::LeaveReason reason)
{
switch (reason) {
case Event_Leave::USER_KICKED:
return tr("kicked by game host or moderator");
break;
case Event_Leave::USER_LEFT:
return tr("player left the game");
break;
case Event_Leave::USER_DISCONNECTED:
return tr("player disconnected from server");
break;
case Event_Leave::OTHER:
default:
return tr("reason unknown");
break;
}
}
void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/)
{
Player *player = gameState->getPlayers().value(eventPlayerId, 0);
if (!player)
return;
emit playerLeft(eventPlayerId);
emit logLeave(player, getLeaveReason(event.reason()));
gameState->removePlayer(eventPlayerId);
player->clear();
player->deleteLater();
// Rearrange all remaining zones so that attachment relationship updates take place
QMapIterator<int, Player *> playerIterator(gameState->getPlayers());
while (playerIterator.hasNext())
playerIterator.next().value()->updateZones();
game->emitUserEvent();
}
void GameEventHandler::eventKicked(const Event_Kicked & /*event*/,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
emit gameClosed();
emit logKicked();
emit playerKicked();
game->emitUserEvent();
}
void GameEventHandler::eventReverseTurn(const Event_ReverseTurn &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
Player *player = gameState->getPlayers().value(eventPlayerId, 0);
if (!player)
return;
emit logTurnReversed(player, event.reversed());
}
void GameEventHandler::eventGameHostChanged(const Event_GameHostChanged & /*event*/,
int eventPlayerId,
const GameEventContext & /*context*/)
{
gameState->setHostId(eventPlayerId);
}
void GameEventHandler::eventGameClosed(const Event_GameClosed & /*event*/,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
game->getGameMetaInfo()->setStarted(false);
gameState->setGameClosed(true);
emit gameClosed();
emit logGameClosed();
game->emitUserEvent();
}
void GameEventHandler::eventSetActivePlayer(const Event_SetActivePlayer &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
gameState->setActivePlayer(event.active_player_id());
Player *player = gameState->getPlayer(event.active_player_id());
if (!player)
return;
emit logActivePlayer(player);
game->emitUserEvent();
}
void GameEventHandler::eventSetActivePhase(const Event_SetActivePhase &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int phase = event.phase();
if (gameState->getCurrentPhase() != phase) {
emit logActivePhaseChanged(phase);
}
gameState->setCurrentPhase(phase);
game->emitUserEvent();
}

View file

@ -0,0 +1,125 @@
#ifndef COCKATRICE_GAME_EVENT_HANDLER_H
#define COCKATRICE_GAME_EVENT_HANDLER_H
#include "pb/event_leave.pb.h"
#include "pb/serverinfo_player.pb.h"
#include "player/player.h"
#include <QObject>
class AbstractClient;
class TabGame;
class Response;
class GameEventContainer;
class GameEventContext;
class GameCommand;
class GameState;
class MessageLogWidget;
class CommandContainer;
class Event_GameJoined;
class Event_GameStateChanged;
class Event_PlayerPropertiesChanged;
class Event_Join;
class Event_Leave;
class Event_GameHostChanged;
class Event_GameClosed;
class Event_GameStart;
class Event_SetActivePlayer;
class Event_SetActivePhase;
class Event_Ping;
class Event_GameSay;
class Event_Kicked;
class Event_ReverseTurn;
class PendingCommand;
class GameEventHandler : public QObject
{
Q_OBJECT
private:
TabGame *game;
GameState *gameState;
public:
GameEventHandler(TabGame *game);
void handleNextTurn();
void handleReverseTurn();
void handlePlayerConceded();
void handlePlayerUnconceded();
void handleActivePhaseChanged(int phase);
void handleGameLeft();
void handleChatMessageSent(const QString &chatMessage);
void handleArrowDeletion(int arrowId);
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
void eventGameStateChanged(const Event_GameStateChanged &event, int eventPlayerId, const GameEventContext &context);
void processCardAttachmentsForPlayers(const Event_GameStateChanged &event);
void eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context);
void eventJoin(const Event_Join &event, int eventPlayerId, const GameEventContext &context);
void eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
QString getLeaveReason(Event_Leave::LeaveReason reason);
void eventKicked(const Event_Kicked &event, int eventPlayerId, const GameEventContext &context);
void eventGameHostChanged(const Event_GameHostChanged &event, int eventPlayerId, const GameEventContext &context);
void eventGameClosed(const Event_GameClosed &event, int eventPlayerId, const GameEventContext &context);
void eventSetActivePlayer(const Event_SetActivePlayer &event, int eventPlayerId, const GameEventContext &context);
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext & /*context*/);
void commandFinished(const Response &response);
void processGameEventContainer(const GameEventContainer &cont,
AbstractClient *client,
Player::EventProcessingOptions options);
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
public slots:
void sendGameCommand(PendingCommand *pend, int playerId = -1);
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
signals:
void localPlayerDeckSelected(Player *localPlayer, int playerId, ServerInfo_Player playerInfo);
void remotePlayerDeckSelected(QString deckList, int playerId, QString playerName);
void remotePlayersDecksSelected(QVector<QPair<int, QPair<QString, QString>>> opponentDecks);
void localPlayerSideboardLocked(int playerId, bool sideboardLocked);
void localPlayerReadyStateChanged(int playerId, bool ready);
void gameStopped();
void gameClosed();
void playerPropertiesChanged(const ServerInfo_PlayerProperties &prop, int playerId);
void playerJoined(const ServerInfo_PlayerProperties &playerInfo);
void playerLeft(int leavingPlayerId);
void playerKicked();
void spectatorJoined(const ServerInfo_PlayerProperties &spectatorInfo);
void spectatorLeft(int leavingSpectatorId);
void gameFlooded();
void containerProcessingStarted(GameEventContext context);
void setContextJudgeName(QString judgeName);
void containerProcessingDone();
void logSpectatorSay(ServerInfo_User userInfo, QString message);
void logSpectatorLeave(QString name, QString reason);
void logGameStart();
void logReadyStart(Player *player);
void logNotReadyStart(Player *player);
void playerConceded(Player *player);
void playerUnconceded(Player *player);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
void logSideboardLockSet(Player *player, bool sideboardLocked);
void logConnectionStateChanged(Player *player, bool connected);
void logJoinSpectator(QString spectatorName);
void logJoinPlayer(Player *player);
void logLeave(Player *player, QString reason);
void logKicked();
void logTurnReversed(Player *player, bool reversed);
void logGameClosed();
void logActivePlayer(Player *activePlayer);
void logActivePhaseChanged(int activePhase);
};
#endif // COCKATRICE_GAME_EVENT_HANDLER_H

View file

@ -0,0 +1 @@
#include "game_meta_info.h"

View file

@ -0,0 +1,107 @@
#ifndef GAME_META_INFO_H
#define GAME_META_INFO_H
#include "pb/serverinfo_game.pb.h"
#include <QMap>
#include <QObject>
// Translation layer class to expose protobuf safely and hook it up to Qt Signals.
// This class de-couples the domain object (i.e. the GameMetaInfo) from the network object.
// If the network object changes, only this class needs to be adjusted.
class GameMetaInfo : public QObject
{
Q_OBJECT
public:
explicit GameMetaInfo(QObject *parent = nullptr) : QObject(parent)
{
}
QMap<int, QString> roomGameTypes;
// Populate from protobuf (e.g., after network message)
void setFromProto(const ServerInfo_Game &gi)
{
gameInfo_.CopyFrom(gi);
}
const ServerInfo_Game &proto() const
{
return gameInfo_;
}
// High-level getters that avoid exposing protobuf directly
int gameId() const
{
return gameInfo_.game_id();
}
int maxPlayers() const
{
return gameInfo_.max_players();
}
QString description() const
{
return QString::fromStdString(gameInfo_.description());
}
bool started() const
{
return gameInfo_.started();
}
bool spectatorsOmniscient() const
{
return gameInfo_.spectators_omniscient();
}
bool spectatorsCanChat() const
{
return gameInfo_.spectators_can_chat();
}
int gameTypesSize() const
{
return gameInfo_.game_types_size();
}
int gameTypeIdAt(int index) const
{
return gameInfo_.game_types(index);
}
QMap<int, QString> getRoomGameTypes() const
{
return roomGameTypes;
}
void setRoomGameTypes(QMap<int, QString> _roomGameTypes)
{
roomGameTypes = _roomGameTypes;
}
QString findRoomGameType(int index)
{
return roomGameTypes.find(gameInfo_.game_types(index)).value();
}
public slots:
void setStarted(bool s)
{
if (gameInfo_.started() == s)
return;
gameInfo_.set_started(s);
emit startedChanged(s);
}
void setSpectatorsOmniscient(bool v)
{
if (gameInfo_.spectators_omniscient() == v)
return;
gameInfo_.set_spectators_omniscient(v);
emit spectatorsOmniscienceChanged(v);
}
signals:
void startedChanged(bool started);
void spectatorsOmniscienceChanged(bool omniscient);
private:
ServerInfo_Game gameInfo_;
};
#endif // GAME_META_INFO_H

View file

@ -0,0 +1,44 @@
#include "game_state.h"
GameState::GameState(int _secondsElapsed,
int _hostId,
int _localPlayerId,
bool _isLocalGame,
const QList<AbstractClient *> _clients,
bool _spectator,
bool _judge,
bool _gameStateKnown,
bool _resuming,
int _currentPhase,
bool _gameClosed)
: secondsElapsed(_secondsElapsed), hostId(_hostId), localPlayerId(_localPlayerId), isLocalGame(_isLocalGame),
clients(_clients), spectator(_spectator), judge(_judge), gameStateKnown(_gameStateKnown), resuming(_resuming),
currentPhase(_currentPhase), gameClosed(_gameClosed)
{
}
void GameState::incrementGameTime()
{
setGameTime(++secondsElapsed);
}
void GameState::setGameTime(int _secondsElapsed)
{
int seconds = _secondsElapsed;
int minutes = seconds / 60;
seconds -= minutes * 60;
int hours = minutes / 60;
minutes -= hours * 60;
emit updateTimeElapsedLabel(QString::number(hours).rightJustified(2, '0') + ":" +
QString::number(minutes).rightJustified(2, '0') + ":" +
QString::number(seconds).rightJustified(2, '0'));
}
void GameState::startGameTimer()
{
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
connect(gameTimer, &QTimer::timeout, this, &GameState::incrementGameTime);
gameTimer->start();
}

View file

@ -0,0 +1,255 @@
#ifndef COCKATRICE_GAME_STATE_H
#define COCKATRICE_GAME_STATE_H
#include "../client/tabs/tab_game.h"
#include "../server/abstract_client.h"
#include "pb/serverinfo_game.pb.h"
#include "pb/serverinfo_playerproperties.pb.h"
#include <QObject>
class ServerInfo_PlayerProperties;
class ServerInfo_User;
class GameState : public QObject
{
Q_OBJECT
public:
explicit GameState(int secondsElapsed,
int hostId,
int localPlayerId,
bool isLocalGame,
QList<AbstractClient *> clients,
bool spectator,
bool judge,
bool gameStateKnown,
bool resuming,
int currentPhase,
bool gameClosed);
const QMap<int, Player *> &getPlayers() const
{
return players;
}
int getPlayerCount() const
{
return players.size();
}
const QMap<int, ServerInfo_User> &getSpectators() const
{
return spectators;
}
ServerInfo_User getSpectator(int playerId) const
{
return spectators.value(playerId);
}
QString getSpectatorName(int spectatorId) const
{
return QString::fromStdString(spectators.value(spectatorId).name());
}
void addSpectator(int spectatorId, const ServerInfo_PlayerProperties &prop)
{
if (!spectators.contains(spectatorId)) {
spectators.insert(spectatorId, prop.user_info());
emit spectatorAdded(prop);
}
}
void removeSpectator(int spectatorId)
{
ServerInfo_User spectatorInfo = spectators.value(spectatorId);
spectators.remove(spectatorId);
emit spectatorRemoved(spectatorId, spectatorInfo);
}
bool isHost() const
{
return hostId == localPlayerId;
}
void setHostId(int _hostId)
{
hostId = _hostId;
}
bool isJudge() const
{
return judge;
}
int getLocalPlayerId() const
{
return localPlayerId;
}
QList<AbstractClient *> getClients() const
{
return clients;
}
bool isLocalPlayer(int playerId) const
{
return clients.size() > 1 || playerId == getLocalPlayerId();
}
Player *addPlayer(int playerId, const ServerInfo_User &info, TabGame *game)
{
auto *newPlayer = new Player(info, playerId, isLocalPlayer(playerId), isJudge(), game);
// TODO
// connect(newPlayer, &Player::openDeckEditor, game, &TabGame::openDeckEditor);
players.insert(playerId, newPlayer);
emit playerAdded(newPlayer);
return newPlayer;
}
void removePlayer(int playerId)
{
Player *player = getPlayer(playerId);
if (!player) {
return;
}
players.remove(playerId);
emit playerRemoved(player);
}
Player *getPlayer(int playerId)
{
Player *player = players.value(playerId, 0);
if (!player)
return nullptr;
return player;
}
Player *getActiveLocalPlayer() const
{
Player *active = players.value(activePlayer, 0);
if (active)
if (active->getLocal())
return active;
QMapIterator<int, Player *> playerIterator(players);
while (playerIterator.hasNext()) {
Player *temp = playerIterator.next().value();
if (temp->getLocal())
return temp;
}
return nullptr;
}
void setActivePlayer(int activePlayerId)
{
activePlayer = activePlayerId;
emit activePlayerChanged(activePlayer);
}
bool getIsLocalGame() const
{
return isLocalGame;
}
bool isSpectator() const
{
return spectator;
}
bool isResuming() const
{
return resuming;
}
void setResuming(bool _resuming)
{
resuming = _resuming;
}
bool isGameStateKnown()
{
return gameStateKnown;
}
int getCurrentPhase() const
{
return currentPhase;
}
void setCurrentPhase(int phase)
{
currentPhase = phase;
emit activePhaseChanged(phase);
}
bool isMainPlayerConceded() const
{
Player *player = players.value(localPlayerId, nullptr);
return player && player->getConceded();
}
void setGameClosed(bool closed)
{
gameClosed = closed;
}
bool isGameClosed() const
{
return gameClosed;
}
void onStartedChanged(bool _started)
{
if (_started) {
startGameTimer();
emit gameStarted(_started);
} else {
emit gameStopped();
}
}
void startGameTimer();
void setGameStateKnown(bool known)
{
gameStateKnown = known;
}
signals:
void updateTimeElapsedLabel(QString newTime);
void playerAdded(Player *player);
void playerRemoved(Player *player);
void spectatorAdded(ServerInfo_PlayerProperties spectator);
void spectatorRemoved(int spectatorId, ServerInfo_User spectator);
void gameStarted(bool resuming);
void gameStopped();
void activePhaseChanged(int activePhase);
void activePlayerChanged(int playerId);
public slots:
void incrementGameTime();
void setGameTime(int _secondsElapsed);
private:
QTimer *gameTimer;
int secondsElapsed;
int hostId;
int localPlayerId;
const bool isLocalGame;
QMap<int, Player *> players;
QMap<int, ServerInfo_User> spectators;
QList<AbstractClient *> clients;
bool spectator;
bool judge;
bool gameStateKnown;
bool resuming;
QStringList phasesList;
int currentPhase;
int activePlayer;
bool gameClosed;
};
#endif // COCKATRICE_GAME_STATE_H

View file

@ -150,16 +150,19 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
stack = addZone(new StackZone(this, (int)table->boundingRect().height(), this));
hand = addZone(new HandZone(this, _local || _judge || (_parent->isSpectator() && _parent->isSpectatorsOmniscient()),
(int)table->boundingRect().height(), this));
hand = addZone(
new HandZone(this,
_local || _judge ||
(_parent->getGameState()->isSpectator() && _parent->getGameMetaInfo()->spectatorsOmniscient()),
(int)table->boundingRect().height(), this));
connect(hand, &HandZone::cardCountChanged, handCounter, &HandCounter::updateNumber);
connect(handCounter, &HandCounter::showContextMenu, hand, &HandZone::showContextMenu);
updateBoundingRect();
if (local || judge) {
connect(_parent, &TabGame::playerAdded, this, &Player::addPlayer);
connect(_parent, &TabGame::playerRemoved, this, &Player::removePlayer);
connect(_parent->getGameState(), &GameState::playerAdded, this, &Player::addPlayer);
connect(_parent->getGameState(), &GameState::playerRemoved, this, &Player::removePlayer);
}
if (local || judge) {
@ -558,7 +561,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
connect(tempSetCounter, &QAction::triggered, this, &Player::actCardCounterTrigger);
}
const QList<Player *> &players = game->getPlayers().values();
const QList<Player *> &players = game->getGameState()->getPlayers().values();
for (const auto player : players) {
addPlayer(player);
}
@ -1015,7 +1018,7 @@ void Player::setShortcutsActive()
// Don't enable always-active shortcuts in local games, since it causes keyboard shortcuts to work inconsistently
// when there are more than 1 player.
if (!game->getIsLocalGame()) {
if (!game->getGameState()->getIsLocalGame()) {
// unattach action is only active in card menu if the active card is attached.
// make unattach shortcut always active so that it consistently works when multiple cards are selected.
game->addAction(aUnattach);
@ -2341,7 +2344,7 @@ void Player::eventDelCounter(const Event_DelCounter &event)
void Player::eventDumpZone(const Event_DumpZone &event)
{
Player *zoneOwner = game->getPlayers().value(event.zone_owner_id(), 0);
Player *zoneOwner = game->getGameState()->getPlayers().value(event.zone_owner_id(), 0);
if (!zoneOwner) {
return;
}
@ -2354,13 +2357,13 @@ void Player::eventDumpZone(const Event_DumpZone &event)
void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context)
{
Player *startPlayer = game->getPlayers().value(event.start_player_id());
Player *startPlayer = game->getGameState()->getPlayers().value(event.start_player_id());
if (!startPlayer) {
return;
}
QString startZoneString = QString::fromStdString(event.start_zone());
CardZone *startZone = startPlayer->getZones().value(startZoneString, 0);
Player *targetPlayer = game->getPlayers().value(event.target_player_id());
Player *targetPlayer = game->getGameState()->getPlayers().value(event.target_player_id());
if (!targetPlayer) {
return;
}
@ -2433,7 +2436,7 @@ void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext &
// Look at all arrows from and to the card.
// If the card was moved to another zone, delete the arrows, otherwise update them.
QMapIterator<int, Player *> playerIterator(game->getPlayers());
QMapIterator<int, Player *> playerIterator(game->getGameState()->getPlayers());
while (playerIterator.hasNext()) {
Player *p = playerIterator.next().value();
@ -2507,7 +2510,7 @@ void Player::eventDestroyCard(const Event_DestroyCard &event)
void Player::eventAttachCard(const Event_AttachCard &event)
{
const QMap<int, Player *> &playerList = game->getPlayers();
const QMap<int, Player *> &playerList = game->getGameState()->getPlayers();
Player *targetPlayer = nullptr;
CardZone *targetZone = nullptr;
CardItem *targetCard = nullptr;
@ -2586,7 +2589,7 @@ void Player::eventRevealCards(const Event_RevealCards &event, EventProcessingOpt
}
Player *otherPlayer = nullptr;
if (event.has_other_player_id()) {
otherPlayer = game->getPlayers().value(event.other_player_id());
otherPlayer = game->getGameState()->getPlayers().value(event.other_player_id());
if (!otherPlayer) {
return;
}
@ -2790,7 +2793,9 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
switch (zoneInfo.type()) {
case ServerInfo_Zone::PrivateZone:
contentsKnown = local || judge || (game->isSpectator() && game->isSpectatorsOmniscient());
contentsKnown =
local || judge ||
(game->getGameState()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
break;
case ServerInfo_Zone::PublicZone:
@ -3084,7 +3089,7 @@ void Player::incrementAllCardCounters()
ArrowItem *Player::addArrow(const ServerInfo_Arrow &arrow)
{
const QMap<int, Player *> &playerList = game->getPlayers();
const QMap<int, Player *> &playerList = game->getGameState()->getPlayers();
Player *startPlayer = playerList.value(arrow.start_player_id(), 0);
Player *targetPlayer = playerList.value(arrow.target_player_id(), 0);
if (!startPlayer || !targetPlayer) {
@ -3178,9 +3183,9 @@ PendingCommand *Player::prepareGameCommand(const google::protobuf::Message &cmd)
GameCommand *c = base.add_game_command();
base.set_target_id(id);
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
return game->prepareGameCommand(base);
return game->getGameEventHandler()->prepareGameCommand(base);
} else {
return game->prepareGameCommand(cmd);
return game->getGameEventHandler()->prepareGameCommand(cmd);
}
}
@ -3196,9 +3201,9 @@ PendingCommand *Player::prepareGameCommand(const QList<const ::google::protobuf:
->CopyFrom(*cmdList[i]);
delete cmdList[i];
}
return game->prepareGameCommand(base);
return game->getGameEventHandler()->prepareGameCommand(base);
} else {
return game->prepareGameCommand(cmdList);
return game->getGameEventHandler()->prepareGameCommand(cmdList);
}
}
@ -3209,15 +3214,15 @@ void Player::sendGameCommand(const google::protobuf::Message &command)
GameCommand *c = base.add_game_command();
base.set_target_id(id);
c->GetReflection()->MutableMessage(c, command.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(command);
game->sendGameCommand(base, id);
game->getGameEventHandler()->sendGameCommand(base, id);
} else {
game->sendGameCommand(command, id);
game->getGameEventHandler()->sendGameCommand(command, id);
}
}
void Player::sendGameCommand(PendingCommand *pend)
{
game->sendGameCommand(pend, id);
game->getGameEventHandler()->sendGameCommand(pend, id);
}
bool Player::clearCardsToDelete()
@ -3284,7 +3289,7 @@ void Player::actMoveCardXCardsFromTop()
if (local) {
sendGameCommand(prepareGameCommand(commandList));
} else {
game->sendGameCommand(prepareGameCommand(commandList));
game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList));
}
}
@ -3470,7 +3475,7 @@ void Player::cardMenuAction()
if (local) {
sendGameCommand(prepareGameCommand(commandList));
} else {
game->sendGameCommand(prepareGameCommand(commandList));
game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList));
}
}
@ -3505,7 +3510,7 @@ void Player::actIncPT(int deltaP, int deltaT)
}
}
game->sendGameCommand(prepareGameCommand(commandList), playerid);
game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid);
}
void Player::actResetPT()
@ -3538,7 +3543,7 @@ void Player::actResetPT()
}
if (!commandList.empty()) {
game->sendGameCommand(prepareGameCommand(commandList), playerid);
game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid);
}
}
@ -3632,7 +3637,7 @@ void Player::actSetPT()
}
}
game->sendGameCommand(prepareGameCommand(commandList), playerid);
game->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid);
}
void Player::actDrawArrow()
@ -4045,6 +4050,7 @@ QMenu *Player::createCardMenu(const CardItem *card)
QMenu *revealMenu = cardMenu->addMenu(tr("Re&veal to..."));
initContextualPlayersMenu(revealMenu);
connect(revealMenu, &QMenu::triggered, this, &Player::actReveal);
cardMenu->addSeparator();
@ -4243,7 +4249,7 @@ QMenu *Player::updateCardMenu(const CardItem *card)
// If is spectator (as spectators don't need card menus), return
// only update the menu if the card is actually selected
if ((game->isSpectator() && !judge) || game->getActiveCard() != card) {
if ((game->getGameState()->isSpectator() && !judge) || game->getActiveCard() != card) {
return nullptr;
}

View file

@ -1,6 +1,8 @@
#ifndef PLAYERLISTWIDGET_H
#define PLAYERLISTWIDGET_H
#include "player.h"
#include <QIcon>
#include <QMap>
#include <QStyledItemDelegate>
@ -47,12 +49,14 @@ signals:
public:
PlayerListWidget(TabSupervisor *_tabSupervisor, AbstractClient *_client, TabGame *_game, QWidget *parent = nullptr);
void retranslateUi();
void addPlayer(const ServerInfo_PlayerProperties &player);
void removePlayer(int playerId);
void setActivePlayer(int playerId);
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
void setGameStarted(bool _gameStarted, bool resuming);
void showContextMenu(const QPoint &pos, const QModelIndex &index);
public slots:
void addPlayer(const ServerInfo_PlayerProperties &player);
void removePlayer(int playerId);
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
};
#endif

View file

@ -391,6 +391,11 @@ void MessageLogWidget::logGameStart()
appendHtmlServerMessage(tr("The game has started."));
}
void MessageLogWidget::logGameFlooded()
{
appendMessage(tr("You are flooding the game. Please wait a couple of seconds."));
}
void MessageLogWidget::logJoin(Player *player)
{
soundEngine->playSound("player_join");

View file

@ -49,6 +49,7 @@ public slots:
void logFlipCard(Player *player, QString cardName, bool faceDown);
void logGameClosed();
void logGameStart();
void logGameFlooded();
void logJoin(Player *player);
void logJoinSpectator(QString name);
void logKicked();

View file

@ -380,7 +380,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
aRemoveMessages = new QAction(tr("Remove this user's messages"), this);
menu->addAction(aRemoveMessages);
}
if (game && (game->isHost() || !tabSupervisor->getAdminLocked())) {
if (game && (game->getGameState()->isHost() || !tabSupervisor->getAdminLocked())) {
menu->addSeparator();
menu->addAction(aKick);
}
@ -476,7 +476,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
Command_KickFromGame cmd;
cmd.set_player_id(playerId);
game->sendGameCommand(cmd);
game->getGameEventHandler()->sendGameCommand(cmd);
} else if (actionClicked == aBan) {
Command_GetUserInfo cmd;
cmd.set_user_name(userName.toStdString());