Add a priority field to TimerGameTick::TPeriod

Use this as the primary sort key for TimerGameTick::TPeriod,
to avoid container sort order changes on timer period saveload.
This commit is contained in:
Jonathan G Rennison
2024-04-20 21:29:21 +01:00
parent d5b8f51bf9
commit 674642f9cc
8 changed files with 57 additions and 21 deletions

View File

@@ -19,13 +19,13 @@
template<>
void IntervalTimer<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
{
if (this->period == 0) return;
if (this->period.value == 0) return;
this->storage.elapsed += delta;
uint count = 0;
while (this->storage.elapsed >= this->period) {
this->storage.elapsed -= this->period;
while (this->storage.elapsed >= this->period.value) {
this->storage.elapsed -= this->period.value;
count++;
}
@@ -38,11 +38,11 @@ template<>
void TimeoutTimer<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
{
if (this->fired) return;
if (this->period == 0) return;
if (this->period.value == 0) return;
this->storage.elapsed += delta;
if (this->storage.elapsed >= this->period) {
if (this->storage.elapsed >= this->period.value) {
this->callback();
this->fired = true;
}
@@ -58,7 +58,16 @@ void TimerManager<TimerGameTick>::Elapsed(TimerGameTick::TElapsed delta)
#ifdef WITH_ASSERT
template<>
void TimerManager<TimerGameTick>::Validate(TimerGameTick::TPeriod)
void TimerManager<TimerGameTick>::Validate(TimerGameTick::TPeriod period)
{
if (period.priority == TimerGameTick::Priority::NONE) return;
/* Validate we didn't make a developer error and scheduled more than one
* entry on the same priority. There can only be one timer on
* a specific priority, to ensure we are deterministic, and to avoid
* container sort order invariant issues with timer period saveload. */
for (const auto &timer : TimerManager<TimerGameTick>::GetTimers()) {
assert(timer->period.priority != period.priority);
}
}
#endif /* WITH_ASSERT */

View File

@@ -21,7 +21,34 @@
*/
class TimerGameTick {
public:
using TPeriod = uint;
enum Priority {
NONE, ///< These timers can be executed in any order; the order is not relevant.
/* For all other priorities, the order is important.
* For safety, you can only setup a single timer on a single priority. */
COMPETITOR_TIMEOUT,
};
struct TPeriod {
Priority priority;
uint value;
TPeriod(Priority priority, uint value) : priority(priority), value(value)
{}
bool operator < (const TPeriod &other) const
{
/* Sort by priority before value, such that changes in value for priorities other than NONE do not change the container order */
if (this->priority != other.priority) return this->priority < other.priority;
return this->value < other.value;
}
bool operator == (const TPeriod &other) const
{
return this->priority == other.priority && this->value == other.value;
}
};
using TElapsed = uint;
struct TStorage {
uint elapsed;