diff --git a/src/timer/timer.h b/src/timer/timer.h new file mode 100644 --- /dev/null +++ b/src/timer/timer.h @@ -0,0 +1,187 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file timer.h Definition of Interval and OneShot timers */ + +#ifndef TIMER_H +#define TIMER_H + +#include "timer_manager.h" + +#include + +/** + * The base where every other type of timer is derived from. + * + * Never use this class directly yourself. + */ +template +class BaseTimer { +public: + using TPeriod = typename TTimerType::TPeriod; + using TElapsed = typename TTimerType::TElapsed; + using TStorage = typename TTimerType::TStorage; + + /** + * Create a new timer. + * + * @param period The period of the timer. + */ + NODISCARD BaseTimer(const TPeriod period) : + period(period) + { + TimerManager::RegisterTimer(*this); + } + + /** + * Delete the timer. + */ + virtual ~BaseTimer() + { + TimerManager::UnregisterTimer(*this); + } + + /* Although these variables are public, they are only public to make saveload easier; not for common use. */ + + TPeriod period; ///< The period of the timer. + TStorage storage = {}; ///< The storage of the timer. + +protected: + /** + * Called by the timer manager to notify the timer that the given amount of time has elapsed. + * + * @param delta Depending on the time type, this is either in milliseconds or in ticks. + */ + virtual void Elapsed(TElapsed delta) = 0; + + /* To ensure only TimerManager can access Elapsed. */ + friend class TimerManager; +}; + +/** + * An interval timer will fire every interval, and will continue to fire until it is deleted. + * + * The callback receives how many times the timer has fired since the last time it fired. + * It will always try to fire every interval, but in times of severe stress it might be late. + * + * Each Timer-type needs to implement the Elapsed() method, and call the callback if needed. + * + * Setting the period to zero disables the interval. It can be reenabled at any time by + * calling SetInterval() with a non-zero period. + */ +template +class IntervalTimer : public BaseTimer { +public: + using TPeriod = typename TTimerType::TPeriod; + using TElapsed = typename TTimerType::TElapsed; + + /** + * Create a new interval timer. + * + * @param interval The interval between each callback. + * @param callback The callback to call when the interval has passed. + */ + NODISCARD IntervalTimer(const TPeriod interval, std::function callback) : + BaseTimer(interval), + callback(callback) + { + } + + /** + * Set a new interval for the timer. + * + * @param interval The interval between each callback. + * @param reset Whether to reset the timer to zero. + */ + void SetInterval(const TPeriod interval, bool reset = true) + { + this->period = interval; + if (reset) this->storage = {}; + } + +private: + std::function callback; + + void Elapsed(TElapsed count) override; +}; + +/** + * A timeout timer will fire once after the interval. You can reset it to fire again. + * The timer will never fire before the interval has passed, but in times of severe stress it might be late. + */ +template +class TimeoutTimer : public BaseTimer { +public: + using TPeriod = typename TTimerType::TPeriod; + using TElapsed = typename TTimerType::TElapsed; + + /** + * Create a new timeout timer. + * + * By default the timeout starts aborted; you will have to call Reset() before it starts. + * + * @param timeout The timeout after which the timer will fire. + * @param callback The callback to call when the timeout has passed. + * @param start Whether to start the timer immediately. If false, you can call Reset() to start it. + */ + NODISCARD TimeoutTimer(const TPeriod timeout, std::function callback, bool start = false) : + BaseTimer(timeout), + fired(!start), + callback(callback) + { + } + + /** + * Reset the timer, so it will fire again after the timeout. + */ + void Reset() + { + this->fired = false; + this->storage = {}; + } + + /** + * Reset the timer, so it will fire again after the timeout. + * + * @param timeout Set a new timeout for the next trigger. + */ + void Reset(const TPeriod timeout) + { + this->period = timeout; + this->fired = false; + this->storage = {}; + } + + /** + * Abort the timer so it doesn't fire if it hasn't yet. + */ + void Abort() + { + this->fired = true; + } + + /** + * Check whether the timeout occurred. + * + * @return True iff the timeout occurred. + */ + bool HasFired() const + { + return this->fired; + } + + /* Although these variables are public, they are only public to make saveload easier; not for common use. */ + + bool fired; ///< Whether the timeout has occurred. + +private: + std::function callback; + + void Elapsed(TElapsed count) override; +}; + +#endif /* TIMER_H */