mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
* [CardDB] Drop the CardInfo self pointer The weak self pointer had to be assigned right after every construction, and a missed assignment silently stopped change notifications. CardInfo is always owned by a QSharedPointer, so it now derives from QEnableSharedFromThis<CardInfo> and asks for sharedFromThis() instead: nothing to initialize, and the CardSet cycle stays broken. CardSet::newInstance() also loses the commented-out self-pointer assignment left over from the same design. * [CardDB] Pin the cardInfoChanged contract without the self pointer Dropping CardInfo's self pointer moved change notifications onto QEnableSharedFromThis, so the signal now rests on the card being owned by a QSharedPointer. Two tests pin that down: - A property change on a card owned by a CardDatabase still emits cardInfoChanged() carrying that card, which is what the card database views and models listen to. - clone() starts out unowned, so a change to the clone is reported as the clone's and must not reach the original's observers. * [CardDB] Treat an unowned CardInfo as a bug, not a no-op Every CardInfo is built inside a QSharedPointer (both factory overloads and clone()), and no constructor notifies anyone, so sharedFromThis() is always available by the time addToSet() or a setter runs. Assert that and warn in a released build, instead of silently skipping the notification or the set append. The copy constructor keeps its explicit QEnableSharedFromThis initializer: GCC's -Wextra warns about the uninitialized base in every translation unit that copies a CardInfo otherwise. A comment there records that the clone deliberately starts unowned. * [CardDB] Break the CardInfo <-> CardSet cycle on reload CardSet is a QList<CardInfoPtr> and CardInfo::addToSet() appends the card to its sets, while a card keeps a CardSetPtr per printing in setsToPrintings. The sets therefore hold strong references back to the cards, and the previous database snapshot stayed alive after a reload, so RSS grew by roughly a second database on every reload. Empty the sets before the cards are dropped: CardDatabase::clear() and swapInDatabaseData() clear the sets before replacing the containers, and removeCard() detaches the card from its sets, so nothing keeps a card alive on behalf of a set once the database lets go of it. Two tests pin the behaviour: a reload and a clear() must drop the last strong reference to a card from the previous snapshot. * [CardDB] Keep the set detach in one place clear() and swapInDatabaseData() both emptied every set before dropping the cards, so share that as detachSetMembership() instead of two copies that have to stay in sync. removeCard() now detaches through the first printing of each set directly, the same way CardInfo::newInstance() appends, instead of an inner loop that always breaks after the first item. Because clear() empties the sets up front, its per-card detach was walking every card's printings to remove it from sets that were already empty. Split removeCard() into the per-card detach and unindexCard(), and let clear() call only the latter. * [CardDB] Test that removeCard detaches the card from its set removeCard() is the one teardown path a caller can trigger for a single card (the token editor uses it), and it now detaches the card from its sets as well. Without that detach the set's strong CardInfoPtr keeps the removed card alive forever, which is exactly what the reload and clear tests pin for the whole-snapshot paths. The test loads the fixture, removes Cat, and requires the card to be gone from the CAT set and to be destroyed once the test drops its own reference. * [Servatrice] Drop the settings cache from the logger #7602 removed #include "settingscache.h" from server_logger.cpp but left isLoggingEnabled() reading settingsCache, so master no longer compiles: the servatrice target fails on the undeclared global and server_logger_test, which compiles the same file without main.cpp, fails to link it. The same commit made reloadConfig() the single place that stores writelog and the log filters, precisely so logging threads stop reading the global QSettings. isLoggingEnabled() now mirrors logMessage() and reads those members instead: the console is a sink of its own, and the logfile needs an open file plus writelog. The mutex is mutable so the const query can take it. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
256 lines
8.3 KiB
C++
256 lines
8.3 KiB
C++
#ifndef CARDDATABASE_H
|
|
#define CARDDATABASE_H
|
|
|
|
#include "../set/card_set_list.h"
|
|
#include "card_database_loader.h"
|
|
|
|
// IWYU pragma: keep
|
|
// CardDatabaseData is passed by value to the swapInDatabaseData() slot, and
|
|
// CardInfoPtr is passed by value to the cardAdded()/cardRemoved() signals, so
|
|
// the moc-generated code needs the complete types.
|
|
#include "card_database_data.h"
|
|
#include "libcockatrice/card/card_info.h"
|
|
|
|
#include <QBasicMutex>
|
|
#include <QLoggingCategory>
|
|
#include <QObject>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
#include <qtmetamacros.h>
|
|
|
|
class CardDatabaseQuerier;
|
|
class ICardDatabasePathProvider;
|
|
class ICardPreferenceProvider;
|
|
class ICardSetPriorityController;
|
|
|
|
inline Q_LOGGING_CATEGORY(CardDatabaseLog, "card_database");
|
|
|
|
/**
|
|
* @class CardDatabase
|
|
* @ingroup CardDatabase
|
|
* @brief Core in-memory container for card and set data.
|
|
*
|
|
* Responsible for maintaining CardInfo objects, CardSet objects, and
|
|
* providing access to CardDatabaseQuerier for query operations.
|
|
* Handles addition, removal, and clearing of cards and sets.
|
|
*/
|
|
class CardDatabase : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
protected:
|
|
/** @brief Controller to determine set priority when choosing preferred printings. */
|
|
ICardSetPriorityController *setPriorityController;
|
|
|
|
/** @brief Cards indexed by exact name. */
|
|
CardNameMap cards;
|
|
|
|
/** @brief Cards indexed by simplified name (normalized). */
|
|
CardNameMap simpleNameCards;
|
|
|
|
/** @brief Sets indexed by short name. */
|
|
SetNameMap sets;
|
|
|
|
FormatRulesNameMap formats;
|
|
|
|
/** @brief Loader responsible for file discovery and parsing. */
|
|
CardDatabaseLoader *loader;
|
|
|
|
/** @brief Current load status of the database. */
|
|
LoadStatus loadStatus;
|
|
|
|
/** @brief Querier for higher-level card lookups. */
|
|
CardDatabaseQuerier *querier;
|
|
|
|
private:
|
|
/**
|
|
* @brief Refreshes the cached reverse-related cards for all cards.
|
|
*/
|
|
void refreshCachedReverseRelatedCards();
|
|
|
|
/**
|
|
* @brief Refreshes the cached reverse-related cards for the given card map.
|
|
* @param cardMap Map of cards to process.
|
|
*/
|
|
void refreshCachedReverseRelatedCards(CardNameMap &cardMap);
|
|
|
|
/**
|
|
* @brief Empties every set, dropping the strong card references they hold.
|
|
*
|
|
* A card keeps a CardSetPtr through its printings while its set keeps a strong CardInfoPtr, so the
|
|
* two form a reference cycle. Dropping a whole snapshot has to break that cycle once, up front,
|
|
* instead of relying on the per-card detach in removeCard().
|
|
*/
|
|
void detachSetMembership();
|
|
|
|
/**
|
|
* @brief Drops a card from the database indexes and notifies observers.
|
|
*
|
|
* Callers are responsible for detaching the card from its sets first.
|
|
* @param card Pointer to the card to unindex.
|
|
*/
|
|
void unindexCard(const CardInfoPtr &card);
|
|
|
|
/** @brief Mutexes for thread safety. */
|
|
QBasicMutex *clearDatabaseMutex = new QBasicMutex(), *addCardMutex = new QBasicMutex(),
|
|
*removeCardMutex = new QBasicMutex();
|
|
|
|
public:
|
|
/**
|
|
* @brief Constructs a new CardDatabase instance.
|
|
* @param parent QObject parent.
|
|
* @param prefs Optional card preference provider.
|
|
* @param pathProvider Optional database path provider.
|
|
* @param setPriorityController Optional controller for set priority.
|
|
*/
|
|
explicit CardDatabase(QObject *parent = nullptr,
|
|
ICardPreferenceProvider *prefs = nullptr,
|
|
ICardDatabasePathProvider *pathProvider = nullptr,
|
|
ICardSetPriorityController *setPriorityController = nullptr);
|
|
|
|
/** @brief Destructor clears all internal data. */
|
|
~CardDatabase() override;
|
|
|
|
/**
|
|
* @brief Removes a card from the database.
|
|
* @param card Pointer to the card to remove.
|
|
*/
|
|
void removeCard(const CardInfoPtr &card);
|
|
|
|
/** @brief Clears all cards, sets, and internal state. */
|
|
void clear();
|
|
|
|
/** @brief Returns the map of cards by name. */
|
|
[[nodiscard]] const CardNameMap &getCardList() const
|
|
{
|
|
return cards;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves a set by short name, creating a new one if missing.
|
|
* @param setName Short name of the set.
|
|
* @return Pointer to the CardSet.
|
|
*/
|
|
CardSetPtr getSet(const QString &setName);
|
|
|
|
/** @brief Returns a list of all sets in the database. */
|
|
[[nodiscard]] CardSetList getSetList() const;
|
|
|
|
/** @brief Returns the current load status. */
|
|
[[nodiscard]] LoadStatus getLoadStatus() const
|
|
{
|
|
return loadStatus;
|
|
}
|
|
|
|
/** @brief Returns the querier for performing card lookups. */
|
|
[[nodiscard]] CardDatabaseQuerier *query() const
|
|
{
|
|
return querier;
|
|
}
|
|
|
|
/** @brief Enables all unknown sets in the database. */
|
|
void enableAllUnknownSets();
|
|
|
|
/** @brief Marks all sets as known. */
|
|
void markAllSetsAsKnown();
|
|
|
|
/**
|
|
* @brief Notifies listeners that enabled sets changed.
|
|
* @param recomputeCachedSets When true, every card's cached set-name / alt-name
|
|
* data is recomputed (needed only when enabled-set state actually
|
|
* changed). Pass false after a plain reload, where enabled-set state is
|
|
* unchanged and the cached data is still valid.
|
|
*/
|
|
void notifyEnabledSetsChanged(bool recomputeCachedSets = true);
|
|
|
|
/**
|
|
* @brief Check for sets that are unknown and emit signals if needed.
|
|
*
|
|
* Called from MainWindow::startupConfigCheck() after signal connections are
|
|
* live (the front-loaded parse in main() runs before MainWindow exists, so
|
|
* signals emitted there would have no receivers). May also be called by
|
|
* the loader via the friend declaration. Has side effects: enables all
|
|
* sets when none are enabled (first-run), marks all sets as known.
|
|
*/
|
|
void checkUnknownSets();
|
|
|
|
ICardSetPriorityController *getPriorityController()
|
|
{
|
|
return setPriorityController;
|
|
}
|
|
|
|
public slots:
|
|
/**
|
|
* @brief Adds a card to the database.
|
|
* @param card CardInfoPtr to add.
|
|
*/
|
|
void addCard(const CardInfoPtr &card);
|
|
|
|
/**
|
|
* @brief Adds a set to the database.
|
|
* @param set Pointer to CardSet to add.
|
|
*/
|
|
void addSet(const CardSetPtr &set);
|
|
|
|
void addFormat(const FormatRulesPtr &format);
|
|
|
|
/** @brief Loads card databases from configured paths, using the binary cache when valid. */
|
|
void loadCardDatabases();
|
|
/**
|
|
* @brief Reloads card databases from disk, bypassing the binary cache,
|
|
* and notifies observers of the fresh state.
|
|
*/
|
|
void reloadCardDatabasesAndNotify();
|
|
|
|
/** @brief Saves custom tokens to file.
|
|
* @return True if successful.
|
|
*/
|
|
bool saveCustomTokensToFile();
|
|
|
|
/**
|
|
* @brief Replaces the entire contents of the database with the provided
|
|
* snapshot in a single operation. Intended to be called once a
|
|
* background load has finished building a CardDatabaseData, so that
|
|
* observers never see a partially-populated database.
|
|
* @param data The fully-built snapshot to adopt.
|
|
*/
|
|
void swapInDatabaseData(CardDatabaseData data);
|
|
|
|
signals:
|
|
/** @brief Emitted when the card database has finished loading successfully. */
|
|
void cardDatabaseLoadingFinished();
|
|
|
|
/**
|
|
* @brief Emitted once after the live database contents have been replaced
|
|
* by a freshly loaded snapshot. Models should rebuild from the
|
|
* database in a single batch rather than reacting to per-card adds.
|
|
*/
|
|
void cardDatabaseReset();
|
|
|
|
/** @brief Emitted when the card database fails to load. */
|
|
void cardDatabaseLoadingFailed();
|
|
|
|
/**
|
|
* @brief Emitted when new sets are found.
|
|
* @param numUnknownSets Number of unknown sets.
|
|
* @param unknownSetsNames Names of unknown sets.
|
|
*/
|
|
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
|
|
|
|
/** @brief Emitted when all new sets have been enabled. */
|
|
void cardDatabaseAllNewSetsEnabled();
|
|
|
|
/** @brief Emitted when enabled sets have changed. */
|
|
void cardDatabaseEnabledSetsChanged();
|
|
|
|
/** @brief Emitted when a new card is added. */
|
|
void cardAdded(CardInfoPtr card);
|
|
|
|
/** @brief Emitted when a card is removed. */
|
|
void cardRemoved(CardInfoPtr card);
|
|
|
|
friend class CardDatabaseLoader;
|
|
friend class CardDatabaseQuerier;
|
|
};
|
|
|
|
#endif // CARDDATABASE_H
|