1
0
Fork 0

Fix #13513, ec492cb267: std::numeric_limits<CompanyMask> not working causes no vehicles to exist

std::numeric_limits<T>::max() returns 0 instead of an error when the type is unknown.
Solve it by implementing and using Set() and All() in BaseBitSet in same way as std::bitset.
pull/13517/head
Rubidium 2025-02-09 21:49:15 +01:00 committed by rubidium42
parent 521b860394
commit 37c215f1fd
9 changed files with 55 additions and 30 deletions

View File

@ -718,7 +718,7 @@ static void HandleBankruptcyTakeover(Company *c)
}
/* Did we ask everyone for bankruptcy? If so, bail out. */
if (c->bankrupt_asked == std::numeric_limits<CompanyMask>::max()) return;
if (c->bankrupt_asked.All()) return;
Company *best = nullptr;
int32_t best_performance = -1;
@ -736,7 +736,7 @@ static void HandleBankruptcyTakeover(Company *c)
/* Asked all companies? */
if (best_performance == -1) {
c->bankrupt_asked = std::numeric_limits<CompanyMask>::max();
c->bankrupt_asked.Set();
return;
}

View File

@ -19,17 +19,28 @@
* @tparam Tvalue_type Type of values to wrap.
* @tparam Tstorage Storage type required to hold values.
*/
template <typename Timpl, typename Tvalue_type, typename Tstorage>
template <typename Timpl, typename Tvalue_type, typename Tstorage, Tstorage Tmask = std::numeric_limits<Tstorage>::max()>
class BaseBitSet {
public:
using ValueType = Tvalue_type; ///< Value type of this BaseBitSet.
using BaseType = Tstorage; ///< Storage type of this BaseBitSet, be ConvertibleThroughBase
static constexpr Tstorage MASK = Tmask; ///< Mask of valid values.
constexpr BaseBitSet() : data(0) {}
explicit constexpr BaseBitSet(Tstorage data) : data(data) {}
explicit constexpr BaseBitSet(Tstorage data) : data(data & Tmask) {}
constexpr auto operator <=>(const BaseBitSet &) const noexcept = default;
/**
* Set all bits.
* @returns The bit set
*/
inline constexpr Timpl &Set()
{
this->data = Tmask;
return static_cast<Timpl&>(*this);
}
/**
* Set the value-th bit.
* @param value Bit to set.
@ -97,6 +108,15 @@ public:
return (this->data & other.data) == other.data;
}
/**
* Test if all of the values are set.
* @returns true iff all of the values are set.
*/
inline constexpr bool All() const
{
return this->data == Tmask;
}
/**
* Test if any of the given values are set.
* @param other BitSet of values to test.
@ -144,6 +164,15 @@ public:
return this->data;
}
/**
* Test that the raw value of this bit set is valid.
* @returns true iff the no bits outside the masked value are set.
*/
inline constexpr bool IsValid() const
{
return (this->base() & Tmask) == this->base();
}
private:
Tstorage data; ///< Bitmask of values.
};

View File

@ -110,6 +110,12 @@ debug_inline constexpr void ToggleFlag(T &x, const T y)
}
}
/** Helper template structure to get the mask for an EnumBitSet from the end enum value. */
template <typename Tstorage, typename Tenum, Tenum Tend_value>
struct EnumBitSetMask {
static constexpr Tstorage value = std::numeric_limits<Tstorage>::max() >> (std::numeric_limits<Tstorage>::digits - to_underlying(Tend_value));
};
/**
* Enum-as-bit-set wrapper.
* Allows wrapping enum values as a bit set. Methods are loosely modelled on std::bitset.
@ -119,15 +125,14 @@ debug_inline constexpr void ToggleFlag(T &x, const T y)
* @tparam Tend_value Last valid value + 1.
*/
template <typename Tenum, typename Tstorage, Tenum Tend_value = Tenum{std::numeric_limits<Tstorage>::digits}>
class EnumBitSet : public BaseBitSet<EnumBitSet<Tenum, Tstorage, Tend_value>, Tenum, Tstorage> {
using BaseClass = BaseBitSet<EnumBitSet<Tenum, Tstorage, Tend_value>, Tenum, Tstorage>;
class EnumBitSet : public BaseBitSet<EnumBitSet<Tenum, Tstorage, Tend_value>, Tenum, Tstorage, EnumBitSetMask<Tstorage, Tenum, Tend_value>::value> {
using BaseClass = BaseBitSet<EnumBitSet<Tenum, Tstorage, Tend_value>, Tenum, Tstorage, EnumBitSetMask<Tstorage, Tenum, Tend_value>::value>;
public:
using EnumType = BaseClass::ValueType;
static constexpr Tstorage MASK = std::numeric_limits<Tstorage>::max() >> (std::numeric_limits<Tstorage>::digits - to_underlying(Tend_value)); ///< Mask of valid values.
constexpr EnumBitSet() : BaseClass() {}
constexpr EnumBitSet(Tenum value) : BaseClass() { this->Set(value); }
explicit constexpr EnumBitSet(Tstorage data) : BaseClass(data & MASK) {}
explicit constexpr EnumBitSet(Tstorage data) : BaseClass(data) {}
/**
* Construct an EnumBitSet from a list of enum values.
@ -142,15 +147,6 @@ public:
constexpr auto operator <=>(const EnumBitSet &) const noexcept = default;
/**
* Test that the raw value of this EnumBitSet is valid.
* @returns true iff the no bits outside the masked value are set.
*/
inline constexpr bool IsValid() const
{
return (this->base() & MASK) == this->base();
}
static constexpr size_t DecayValueType(const BaseClass::ValueType &value) { return to_underlying(value); }
};

View File

@ -627,7 +627,7 @@ static void CompanyCheckBankrupt(Company *c)
* is no THE-END, otherwise mark the client as spectator to make sure
* they are no longer in control of this company. However... when you
* join another company (cheat) the "unowned" company can bankrupt. */
c->bankrupt_asked = std::numeric_limits<CompanyMask>::max();
c->bankrupt_asked.Set();
break;
}

View File

@ -744,7 +744,7 @@ void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ym
int intro_months = intro_ymd.year.base() * 12 + intro_ymd.month;
if (intro_ymd.day > 1) intro_months++; // Engines are introduced at the first month start at/after intro date.
e->age = aging_months - intro_months;
e->company_avail = std::numeric_limits<CompanyMask>::max();
e->company_avail.Set();
e->flags.Set(EngineFlag::Available);
}
@ -885,7 +885,7 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_d
Engine *e = Engine::Get(eid);
e->preview_company = INVALID_COMPANY;
e->preview_asked = std::numeric_limits<CompanyMask>::max();
e->preview_asked.Set();
EnableEngineForCompany(eid, company);
@ -980,7 +980,7 @@ static IntervalTimer<TimerGameCalendar> _calendar_engines_daily({TimerGameCalend
e->preview_company = GetPreviewCompany(e);
if (e->preview_company == INVALID_COMPANY) {
e->preview_asked = std::numeric_limits<CompanyMask>::max();
e->preview_asked.Set();
continue;
}
@ -1109,7 +1109,7 @@ static void NewVehicleAvailable(Engine *e)
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
/* Now available for all companies */
e->company_avail = std::numeric_limits<CompanyMask>::max();
e->company_avail.Set();
/* Do not introduce new rail wagons */
if (IsWagon(index)) return;

View File

@ -2051,15 +2051,15 @@ bool AfterLoadGame()
/* More companies ... */
for (Company *c : Company::Iterate()) {
if (c->bankrupt_asked.base() == 0xFF) c->bankrupt_asked = std::numeric_limits<CompanyMask>::max();
if (c->bankrupt_asked.base() == 0xFF) c->bankrupt_asked.Set();
}
for (Engine *e : Engine::Iterate()) {
if (e->company_avail.base() == 0xFF) e->company_avail = std::numeric_limits<CompanyMask>::max();
if (e->company_avail.base() == 0xFF) e->company_avail.Set();
}
for (Town *t : Town::Iterate()) {
if (t->have_ratings.base() == 0xFF) t->have_ratings = std::numeric_limits<CompanyMask>::max();
if (t->have_ratings.base() == 0xFF) t->have_ratings.Set();
for (uint i = 8; i != MAX_COMPANIES; i++) t->ratings[i] = RATING_INITIAL;
}
}

View File

@ -109,7 +109,7 @@ struct ENGNChunkHandler : ChunkHandler {
* Just cancel any previews. */
e->flags.Reset(EngineFlag{4}); // ENGINE_OFFER_WINDOW_OPEN
e->preview_company = INVALID_COMPANY;
e->preview_asked = std::numeric_limits<CompanyMask>::max();
e->preview_asked.Set();
}
}
}

View File

@ -406,7 +406,7 @@ static bool FixTTOEngines()
/* Make sure for example monorail and maglev are available when they should be */
if (TimerGameCalendar::date >= e->intro_date && e->info.climates.Test(LandscapeType::Temperate)) {
e->flags.Set(EngineFlag::Available);
e->company_avail = std::numeric_limits<CompanyMask>::max();
e->company_avail.Set();
e->age = TimerGameCalendar::date > e->intro_date ? (TimerGameCalendar::date - e->intro_date).base() / 30 : 0;
}
} else {
@ -431,7 +431,7 @@ static bool FixTTOEngines()
* if at least one of them was available. */
for (uint j = 0; j < lengthof(tto_to_ttd); j++) {
if (tto_to_ttd[j] == i && _old_engines[j].company_avail.Any()) {
e->company_avail = std::numeric_limits<CompanyMask>::max();
e->company_avail.Set();
e->flags.Set(EngineFlag::Available);
break;
}
@ -441,7 +441,7 @@ static bool FixTTOEngines()
}
e->preview_company = INVALID_COMPANY;
e->preview_asked = std::numeric_limits<CompanyMask>::max();
e->preview_asked.Set();
e->preview_wait = 0;
e->name = std::string{};
}

View File

@ -724,7 +724,7 @@ protected:
*/
inline CompanyMask GetOverlayCompanyMask() const
{
return Company::IsValidID(_local_company) ? CompanyMask{}.Set(_local_company) : std::numeric_limits<CompanyMask>::max();
return Company::IsValidID(_local_company) ? CompanyMask{}.Set(_local_company) : CompanyMask{}.Set();
}
/** Blink the industries (if selected) on a regular interval. */