Centralize MAX_COUNTERS_ON_CARD and enforce [0, 999] bounds on server

- Move MAX_COUNTERS_ON_CARD to trice_limits.h
  - Server clamps values in setCounter() and incrementCounter()
  - Client uses clamped comparison to allow recovery from invalid states
  - Add tests for clamping behavior
This commit is contained in:
DawnFire42 2026-05-12 22:40:04 -04:00
parent 5f92bff741
commit eca3d75e12
No known key found for this signature in database
GPG key ID: 24BB855EE2911B33
6 changed files with 66 additions and 18 deletions

View file

@ -26,6 +26,7 @@
#include <libcockatrice/protocol/pb/event_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/utility/trice_limits.h>
#include <limits>
Server_Card::Server_Card(const CardRef &cardRef, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone)
@ -113,6 +114,9 @@ QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue
bool Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event)
{
// Clamp to valid card counter range [0, MAX_COUNTERS_ON_CARD]
value = qBound(0, value, MAX_COUNTERS_ON_CARD);
const int oldValue = counters.value(_id, 0);
if (value == oldValue) {
return false;
@ -134,12 +138,11 @@ bool Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event)
bool Server_Card::incrementCounter(int counterId, int delta, Event_SetCardCounter *event)
{
// TODO: Extract overflow-safe arithmetic into shared helper.
// Duplicated in Server_Counter::incrementCount() - keep in sync if modified.
const int oldValue = counters.value(counterId, 0);
const auto result = static_cast<int64_t>(oldValue) + static_cast<int64_t>(delta);
const int newValue = static_cast<int>(qBound(static_cast<int64_t>(std::numeric_limits<int>::min()), result,
static_cast<int64_t>(std::numeric_limits<int>::max())));
// Clamp to [0, MAX_COUNTERS_ON_CARD] for card counters
const int newValue =
static_cast<int>(qBound(static_cast<int64_t>(0), result, static_cast<int64_t>(MAX_COUNTERS_ON_CARD)));
if (newValue == oldValue) {
return false;

View file

@ -154,9 +154,9 @@ public:
cardRef = _cardRef;
}
/**
* @brief Sets a card counter to an exact value.
* @brief Sets a card counter to an exact value with clamping.
* @param _id The counter ID.
* @param value The new value (0 removes the counter).
* @param value The desired value (clamped to [0, MAX_COUNTERS_ON_CARD]; 0 removes the counter).
* @param event Optional event to populate with counter state.
* @return true if the value changed, false otherwise.
*/
@ -168,7 +168,7 @@ public:
* @param event Optional event to populate with counter state.
* @return true if the value changed, false otherwise.
* @note If counter does not exist, starts from 0. Counter is removed if result is 0.
* @warning For direct assignment without clamping, use setCounter().
* @note Clamps result to [0, MAX_COUNTERS_ON_CARD].
*/
[[nodiscard]] bool incrementCounter(int counterId, int delta, Event_SetCardCounter *event = nullptr);
void setTapped(bool _tapped)