mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-24 10:23:02 -07:00
[Game] Prevent spectator duplication when replaying joined events (#7248)
* [Game] Prevent spectator duplication when replaying joined events The spectator branch of eventJoin emitted spectatorJoined unconditionally even when the spectator was already present (e.g. replayed during a rewind). Guard it like the player branch and eventGameStateChanged, and make PlayerListWidget::addPlayer idempotent as defense in depth. * In resetChatAndPhase() (the rewound() handler), also clear all spectators from both PlayerManager and PlayerListWidget before the replay rebuilds from event 0. The forward replay then re-adds exactly the spectators whose join events fall within the new time range via eventGameStateChanged/eventJoin. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
fb482f037e
commit
8d30ac54f0
6 changed files with 35 additions and 2 deletions
|
|
@ -430,12 +430,13 @@ void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/,
|
||||||
QString playerName = QString::fromStdString(playerInfo.user_info().name());
|
QString playerName = QString::fromStdString(playerInfo.user_info().name());
|
||||||
emit addPlayerToAutoCompleteList(playerName);
|
emit addPlayerToAutoCompleteList(playerName);
|
||||||
|
|
||||||
if (game->getPlayerManager()->getPlayers().contains(playerId)) {
|
PlayerManager *playerManager = game->getPlayerManager();
|
||||||
|
if (playerManager->getPlayers().contains(playerId) || playerManager->getSpectators().contains(playerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerInfo.spectator()) {
|
if (playerInfo.spectator()) {
|
||||||
game->getPlayerManager()->addSpectator(playerId, playerInfo);
|
playerManager->addSpectator(playerId, playerInfo);
|
||||||
emit logJoinSpectator(playerName);
|
emit logJoinSpectator(playerName);
|
||||||
emit spectatorJoined(playerInfo);
|
emit spectatorJoined(playerInfo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,14 @@ PlayerLogic *PlayerManager::getPlayer(int playerId) const
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerManager::clearSpectators()
|
||||||
|
{
|
||||||
|
const QList<int> spectatorIds = spectators.keys();
|
||||||
|
for (int spectatorId : spectatorIds) {
|
||||||
|
removeSpectator(spectatorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerManager::onPlayerConceded(int playerId, bool conceded)
|
void PlayerManager::onPlayerConceded(int playerId, bool conceded)
|
||||||
{
|
{
|
||||||
// Everything else cares about this
|
// Everything else cares about this
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ public:
|
||||||
emit spectatorRemoved(spectatorId, spectatorInfo);
|
emit spectatorRemoved(spectatorId, spectatorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Remove all spectators, emitting the removal signal for each. */
|
||||||
|
void clearSpectators();
|
||||||
|
|
||||||
[[nodiscard]] AbstractGame *getGame() const
|
[[nodiscard]] AbstractGame *getGame() const
|
||||||
{
|
{
|
||||||
return game;
|
return game;
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ void PlayerListWidget::retranslateUi()
|
||||||
|
|
||||||
void PlayerListWidget::addPlayer(const ServerInfo_PlayerProperties &player)
|
void PlayerListWidget::addPlayer(const ServerInfo_PlayerProperties &player)
|
||||||
{
|
{
|
||||||
|
if (players.contains(player.player_id())) {
|
||||||
|
updatePlayerProperties(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeWidgetItem *newPlayer = new PlayerListTWI;
|
QTreeWidgetItem *newPlayer = new PlayerListTWI;
|
||||||
players.insert(player.player_id(), newPlayer);
|
players.insert(player.player_id(), newPlayer);
|
||||||
updatePlayerProperties(player);
|
updatePlayerProperties(player);
|
||||||
|
|
@ -176,6 +181,17 @@ void PlayerListWidget::removePlayer(int playerId)
|
||||||
delete takeTopLevelItem(indexOfTopLevelItem(player));
|
delete takeTopLevelItem(indexOfTopLevelItem(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerListWidget::clearSpectators()
|
||||||
|
{
|
||||||
|
const QList<int> playerIds = players.keys();
|
||||||
|
for (int playerId : playerIds) {
|
||||||
|
QTreeWidgetItem *player = players.value(playerId, 0);
|
||||||
|
if (player && !player->data(1, Qt::UserRole).toBool()) {
|
||||||
|
removePlayer(playerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerListWidget::setActivePlayer(int playerId)
|
void PlayerListWidget::setActivePlayer(int playerId)
|
||||||
{
|
{
|
||||||
QMapIterator<int, QTreeWidgetItem *> i(players);
|
QMapIterator<int, QTreeWidgetItem *> i(players);
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ public slots:
|
||||||
void addPlayer(const ServerInfo_PlayerProperties &player);
|
void addPlayer(const ServerInfo_PlayerProperties &player);
|
||||||
void removePlayer(int playerId);
|
void removePlayer(int playerId);
|
||||||
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
|
void updatePlayerProperties(const ServerInfo_PlayerProperties &prop, int playerId = -1);
|
||||||
|
void clearSpectators();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,10 @@ void TabGame::resetChatAndPhase()
|
||||||
|
|
||||||
// reset phase markers
|
// reset phase markers
|
||||||
game->getGameState()->setCurrentPhase(-1);
|
game->getGameState()->setCurrentPhase(-1);
|
||||||
|
|
||||||
|
// reset spectator state so the replay can rebuild it from the start
|
||||||
|
game->getPlayerManager()->clearSpectators();
|
||||||
|
playerListWidget->clearSpectators();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabGame::emitUserEvent()
|
void TabGame::emitUserEvent()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue