Fix segfault when game is closed while card view window is open (#5507)

This commit is contained in:
RickyRister 2025-01-20 19:06:55 -08:00 committed by GitHub
parent b004e91aa4
commit aeb1b9fb4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 26 additions and 40 deletions

View file

@ -189,14 +189,7 @@ void TabGame::emitUserEvent()
TabGame::~TabGame() TabGame::~TabGame()
{ {
scene->clearViews();
delete replay; delete replay;
QMapIterator<int, Player *> i(players);
while (i.hasNext()) {
delete i.next().value();
}
} }
void TabGame::updatePlayerListDockTitle() void TabGame::updatePlayerListDockTitle()
@ -1097,6 +1090,7 @@ void TabGame::eventLeave(const Event_Leave &event, int eventPlayerId, const Game
players.remove(eventPlayerId); players.remove(eventPlayerId);
emit playerRemoved(player); emit playerRemoved(player);
player->clear(); player->clear();
scene->removePlayer(player);
player->deleteLater(); player->deleteLater();
// Rearrange all remaining zones so that attachment relationship updates take place // Rearrange all remaining zones so that attachment relationship updates take place

View file

@ -38,16 +38,9 @@ CardItem::CardItem(Player *_owner,
CardItem::~CardItem() CardItem::~CardItem()
{ {
prepareDelete();
if (scene())
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
delete cardMenu; delete cardMenu;
delete ptMenu; delete ptMenu;
delete moveMenu; delete moveMenu;
deleteDragItem();
} }
void CardItem::prepareDelete() void CardItem::prepareDelete()
@ -74,6 +67,8 @@ void CardItem::prepareDelete()
void CardItem::deleteLater() void CardItem::deleteLater()
{ {
prepareDelete(); prepareDelete();
if (scene())
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
AbstractCardItem::deleteLater(); AbstractCardItem::deleteLater();
} }

View file

@ -29,6 +29,13 @@ GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent)
GameScene::~GameScene() GameScene::~GameScene()
{ {
delete animationTimer; delete animationTimer;
// DO NOT call clearViews() here
// clearViews calls close() on the zoneViews, which sends signals; sending signals in destructors leads to segfaults
// deleteLater() deletes the zoneView without allowing it to send signals
for (const auto &zoneView : zoneViews) {
zoneView->deleteLater();
}
} }
void GameScene::retranslateUi() void GameScene::retranslateUi()
@ -76,7 +83,7 @@ void GameScene::rearrange()
QListIterator<Player *> playersIter(players); QListIterator<Player *> playersIter(players);
while (playersIter.hasNext()) { while (playersIter.hasNext()) {
Player *p = playersIter.next(); Player *p = playersIter.next();
if (!p->getConceded()) { if (p && !p->getConceded()) {
playersPlaying.append(p); playersPlaying.append(p);
if (!firstPlayerFound && (p->getLocal())) { if (!firstPlayerFound && (p->getLocal())) {
firstPlayerIndex = playersPlaying.size() - 1; firstPlayerIndex = playersPlaying.size() - 1;

View file

@ -564,9 +564,6 @@ Player::~Player()
{ {
qCDebug(PlayerLog) << "Player destructor:" << getName(); qCDebug(PlayerLog) << "Player destructor:" << getName();
static_cast<GameScene *>(scene())->removePlayer(this);
clear();
QMapIterator<QString, CardZone *> i(zones); QMapIterator<QString, CardZone *> i(zones);
while (i.hasNext()) while (i.hasNext())
delete i.next().value(); delete i.next().value();
@ -2086,7 +2083,7 @@ void Player::eventShuffle(const Event_Shuffle &event)
int length = view->getCards().length(); int length = view->getCards().length();
// we want to close empty views as well // we want to close empty views as well
if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library
view->deleteLater(); view->close();
break; break;
} }
} else { } else {

View file

@ -40,17 +40,6 @@ CardZone::CardZone(Player *_p,
&CardZone::refreshCardInfos); &CardZone::refreshCardInfos);
} }
CardZone::~CardZone()
{
qCDebug(CardZoneLog) << "CardZone destructor: " << name;
for (auto *view : views) {
if (view != nullptr) {
view->deleteLater();
}
}
clearContents();
}
void CardZone::retranslateUi() void CardZone::retranslateUi()
{ {
for (int i = 0; i < cards.size(); ++i) for (int i = 0; i < cards.size(); ++i)

View file

@ -67,7 +67,6 @@ public:
bool _contentsKnown, bool _contentsKnown,
QGraphicsItem *parent = nullptr, QGraphicsItem *parent = nullptr,
bool _isView = false); bool _isView = false);
~CardZone();
void retranslateUi(); void retranslateUi();
void clearContents(); void clearContents();
bool getHasCardAttr() const bool getHasCardAttr() const

View file

@ -40,13 +40,17 @@ ZoneViewZone::ZoneViewZone(Player *_p,
} }
} }
ZoneViewZone::~ZoneViewZone() /**
* Deletes this ZoneView and removes it from the origZone's views.
* You should normally call this method instead of deleteLater()
*/
void ZoneViewZone::close()
{ {
emit beingDeleted(); emit closed();
qDebug("ZoneViewZone destructor");
if (!(revealZone && !writeableRevealZone)) { if (!(revealZone && !writeableRevealZone)) {
origZone->getViews().removeOne(this); origZone->getViews().removeOne(this);
} }
deleteLater();
} }
QRectF ZoneViewZone::boundingRect() const QRectF ZoneViewZone::boundingRect() const

View file

@ -65,7 +65,6 @@ public:
bool _writeableRevealZone = false, bool _writeableRevealZone = false,
QGraphicsItem *parent = nullptr, QGraphicsItem *parent = nullptr,
bool _isReversed = false); bool _isReversed = false);
~ZoneViewZone();
QRectF boundingRect() const; QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void reorganizeCards(); void reorganizeCards();
@ -94,13 +93,14 @@ public:
return isReversed; return isReversed;
} }
public slots: public slots:
void close();
void setGroupBy(CardList::SortOption _groupBy); void setGroupBy(CardList::SortOption _groupBy);
void setSortBy(CardList::SortOption _sortBy); void setSortBy(CardList::SortOption _sortBy);
void setPileView(int _pileView); void setPileView(int _pileView);
private slots: private slots:
void zoneDumpReceived(const Response &r); void zoneDumpReceived(const Response &r);
signals: signals:
void beingDeleted(); void closed();
void optimumRectChanged(); void optimumRectChanged();
void wheelEventReceived(QGraphicsSceneWheelEvent *event); void wheelEventReceived(QGraphicsSceneWheelEvent *event);

View file

@ -133,7 +133,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
setLayout(vbox); setLayout(vbox);
connect(zone, &ZoneViewZone::optimumRectChanged, this, &ZoneViewWidget::resizeToZoneContents); connect(zone, &ZoneViewZone::optimumRectChanged, this, &ZoneViewWidget::resizeToZoneContents);
connect(zone, &ZoneViewZone::beingDeleted, this, &ZoneViewWidget::zoneDeleted); connect(zone, &ZoneViewZone::closed, this, &ZoneViewWidget::zoneDeleted);
zone->initializeCards(cardList); zone->initializeCards(cardList);
// QLabel sizes aren't taken into account until the widget is rendered. // QLabel sizes aren't taken into account until the widget is rendered.
@ -314,11 +314,12 @@ void ZoneViewWidget::handleScrollBarChange(int value)
void ZoneViewWidget::closeEvent(QCloseEvent *event) void ZoneViewWidget::closeEvent(QCloseEvent *event)
{ {
disconnect(zone, &ZoneViewZone::beingDeleted, this, 0); disconnect(zone, &ZoneViewZone::closed, this, 0);
// manually call zone->close in order to remove it from the origZones views
zone->close();
if (shuffleCheckBox.isChecked()) if (shuffleCheckBox.isChecked())
player->sendGameCommand(Command_Shuffle()); player->sendGameCommand(Command_Shuffle());
emit closePressed(this); zoneDeleted();
deleteLater();
event->accept(); event->accept();
} }