Changeset - r27088:7e2816121458
[Not reviewed]
master
0 5 7
Patric Stout - 14 months ago 2023-04-13 15:16:48
truebrain@openttd.org
Codechange: introduce a framework for all our timers

IntervalTimer and TimeoutTimer use RAII, and can be used to replace
all the time-based timeouts, lag-detection, "execute every N" we
have.
As it uses RAII, you can safely use it as static variable, class
member, temporary variable, etc. As soon as it goes out-of-scope,
it will be safely removed.
This allows for much easier to read code when it comes to intervals.
12 files changed with 591 insertions and 85 deletions:
0 comments (0 inline, 0 general)
src/CMakeLists.txt
Show inline comments
 
@@ -24,6 +24,7 @@ add_subdirectory(sound)
 
add_subdirectory(spriteloader)
 
add_subdirectory(table)
 
add_subdirectory(tests)
 
add_subdirectory(timer)
 
add_subdirectory(video)
 
add_subdirectory(widgets)
 

	
src/date.cpp
Show inline comments
 
@@ -20,6 +20,8 @@
 
#include "saveload/saveload.h"
 
#include "newgrf_profiling.h"
 
#include "widgets/statusbar_widget.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 

	
 
#include "safeguards.h"
 

	
 
@@ -189,7 +191,7 @@ static const Month _autosave_months[] = 
 
/**
 
 * Runs various procedures that have to be done yearly
 
 */
 
static void OnNewYear()
 
static IntervalTimer<TimerGameCalendar> _on_new_year({TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [](auto)
 
{
 
	CompaniesYearlyLoop();
 
	VehiclesYearlyLoop();
 
@@ -222,12 +224,12 @@ static void OnNewYear()
 
	}
 

	
 
	if (_settings_client.gui.auto_euro) CheckSwitchToEuro();
 
}
 
});
 

	
 
/**
 
 * Runs various procedures that have to be done monthly
 
 */
 
static void OnNewMonth()
 
static IntervalTimer<TimerGameCalendar> _on_new_month({TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [](auto)
 
{
 
	if (_settings_client.gui.autosave != 0 && (_cur_month % _autosave_months[_settings_client.gui.autosave]) == 0) {
 
		_do_autosave = true;
 
@@ -242,12 +244,12 @@ static void OnNewMonth()
 
	SubsidyMonthlyLoop();
 
	StationMonthlyLoop();
 
	if (_network_server) NetworkServerMonthlyLoop();
 
}
 
});
 

	
 
/**
 
 * Runs various procedures that have to be done daily
 
 */
 
static void OnNewDay()
 
static IntervalTimer<TimerGameCalendar> _on_new_day({TimerGameCalendar::DAY, TimerGameCalendar::Priority::NONE}, [](auto)
 
{
 
	if (!_newgrf_profilers.empty() && _newgrf_profile_end_date <= _date) {
 
		NewGRFProfiler::FinishAll();
 
@@ -263,45 +265,4 @@ static void OnNewDay()
 

	
 
	/* Refresh after possible snowline change */
 
	SetWindowClassesDirty(WC_TOWN_VIEW);
 
}
 

	
 
/**
 
 * Increases the tick counter, increases date  and possibly calls
 
 * procedures that have to be called daily, monthly or yearly.
 
 */
 
void IncreaseDate()
 
{
 
	/* increase day, and check if a new day is there? */
 
	_tick_counter++;
 

	
 
	if (_game_mode == GM_MENU) return;
 

	
 
	_date_fract++;
 
	if (_date_fract < DAY_TICKS) return;
 
	_date_fract = 0;
 

	
 
	/* increase day counter */
 
	_date++;
 

	
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(_date, &ymd);
 

	
 
	/* check if we entered a new month? */
 
	bool new_month = ymd.month != _cur_month;
 

	
 
	/* check if we entered a new year? */
 
	bool new_year = ymd.year != _cur_year;
 

	
 
	/* update internal variables before calling the daily/monthly/yearly loops */
 
	_cur_month = ymd.month;
 
	_cur_year  = ymd.year;
 

	
 
	/* yes, call various daily loops */
 
	OnNewDay();
 

	
 
	/* yes, call various monthly loops */
 
	if (new_month) OnNewMonth();
 

	
 
	/* yes, call various yearly loops */
 
	if (new_year) OnNewYear();
 
}
 
});
src/openttd.cpp
Show inline comments
 
@@ -68,6 +68,8 @@
 
#include "industry.h"
 
#include "network/network_gui.h"
 
#include "misc_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 

	
 
#include "linkgraph/linkgraphschedule.h"
 

	
 
@@ -82,7 +84,6 @@
 
#endif
 

	
 
void CallLandscapeTick();
 
void IncreaseDate();
 
void DoPaletteAnimations();
 
void MusicLoop();
 
void ResetMusic();
 
@@ -1408,7 +1409,7 @@ void StateGameLoop()
 

	
 
		BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
 
		AnimateAnimatedTiles();
 
		IncreaseDate();
 
		TimerManager<TimerGameCalendar>::Elapsed(1);
 
		RunTileLoop();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
src/stdafx.h
Show inline comments
 
@@ -155,6 +155,13 @@
 
#      define NOACCESS(args)
 
#endif
 

	
 
/* [[nodiscard]] on constructors doesn't work in GCC older than 10.1. */
 
#if __GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)
 
#      define NODISCARD
 
#else
 
#      define NODISCARD [[nodiscard]]
 
#endif
 

	
 
#if defined(__WATCOMC__)
 
#	define NORETURN
 
#	define CDECL
src/timer/CMakeLists.txt
Show inline comments
 
new file 100644
 
add_files(
 
    timer_game_calendar.cpp
 
    timer_game_calendar.h
 
    timer_window.cpp
 
    timer_window.h
 
    timer_manager.h
 
    timer.h
 
)
src/timer/timer.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer.h Definition of Interval and OneShot timers */
 

	
 
#ifndef TIMER_H
 
#define TIMER_H
 

	
 
#include "timer_manager.h"
 

	
 
#include <functional>
 

	
 
/**
 
 * The base where every other type of timer is derived from.
 
 *
 
 * Never use this class directly yourself.
 
 */
 
template <typename TTimerType>
 
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<TTimerType>::RegisterTimer(*this);
 
	}
 

	
 
	/**
 
	 * Delete the timer.
 
	 */
 
	virtual ~BaseTimer()
 
	{
 
		TimerManager<TTimerType>::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<TTimerType>;
 
};
 

	
 
/**
 
 * 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 <typename TTimerType>
 
class IntervalTimer : public BaseTimer<TTimerType> {
 
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<void(uint)> callback) :
 
		BaseTimer<TTimerType>(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<void(uint)> 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 <typename TTimerType>
 
class TimeoutTimer : public BaseTimer<TTimerType> {
 
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<void()> callback, bool start = false) :
 
		BaseTimer<TTimerType>(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<void()> callback;
 

	
 
	void Elapsed(TElapsed count) override;
 
};
 

	
 
#endif /* TIMER_H */
src/timer/timer_game_calendar.cpp
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file timer_game_calendar.cpp
 
 * This file implements the timer logic for the game-calendar-timer.
 
 */
 

	
 
#include "stdafx.h"
 
#include "date_func.h"
 
#include "openttd.h"
 
#include "timer.h"
 
#include "timer_game_calendar.h"
 
#include "vehicle_base.h"
 
#include "linkgraph/linkgraph.h"
 

	
 
#include "safeguards.h"
 

	
 
template<>
 
void IntervalTimer<TimerGameCalendar>::Elapsed(TimerGameCalendar::TElapsed trigger)
 
{
 
	if (trigger == this->period.trigger) {
 
		this->callback(1);
 
	}
 
}
 

	
 
template<>
 
void TimeoutTimer<TimerGameCalendar>::Elapsed(TimerGameCalendar::TElapsed trigger)
 
{
 
	if (this->fired) return;
 

	
 
	if (trigger == this->period.trigger) {
 
		this->callback();
 
		this->fired = true;
 
	}
 
}
 

	
 
template<>
 
void TimerManager<TimerGameCalendar>::Elapsed(TimerGameCalendar::TElapsed delta)
 
{
 
	assert(delta == 1);
 

	
 
	_tick_counter++;
 

	
 
	if (_game_mode == GM_MENU) return;
 

	
 
	_date_fract++;
 
	if (_date_fract < DAY_TICKS) return;
 
	_date_fract = 0;
 

	
 
	/* increase day counter */
 
	_date++;
 

	
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(_date, &ymd);
 

	
 
	/* check if we entered a new month? */
 
	bool new_month = ymd.month != _cur_month;
 

	
 
	/* check if we entered a new year? */
 
	bool new_year = ymd.year != _cur_year;
 

	
 
	/* update internal variables before calling the daily/monthly/yearly loops */
 
	_cur_month = ymd.month;
 
	_cur_year  = ymd.year;
 

	
 
	/* Make a temporary copy of the timers, as a timer's callback might add/remove other timers. */
 
	auto timers = TimerManager<TimerGameCalendar>::GetTimers();
 

	
 
	for (auto timer : timers) {
 
		timer->Elapsed(TimerGameCalendar::DAY);
 
	}
 

	
 
	if (new_month) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameCalendar::MONTH);
 
		}
 
	}
 

	
 
	if (new_year) {
 
		for (auto timer : timers) {
 
			timer->Elapsed(TimerGameCalendar::YEAR);
 
		}
 
	}
 
}
src/timer/timer_game_calendar.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer_game_calendar.h Definition of the game-calendar-timer */
 

	
 
#ifndef TIMER_GAME_CALENDAR_H
 
#define TIMER_GAME_CALENDAR_H
 

	
 
/**
 
 * Timer that is increased every 27ms, and counts towards ticks / days / months / years.
 
 *
 
 * The amount of days in a month depends on the month and year (leap-years).
 
 * There are always 74 ticks in a day (and with 27ms, this makes 1 day 1.998 seconds).
 
 *
 
 * IntervalTimer and TimeoutTimer based on this Timer are a bit unusual, as their count is always one.
 
 * You create those timers based on a transition: a new day, a new month or a new year.
 
 *
 
 * Additionally, you need to set a priority. To ensure deterministic behaviour, events are executed
 
 * in priority. It is important that if you assign NONE, you do not use Random() in your callback.
 
 * Other than that, make sure you only set one callback per priority.
 
 *
 
 * For example:
 
 *   IntervalTimer<TimerGameCalendar>({TimerGameCalendar::DAY, TimerGameCalendar::Priority::NONE}, [](uint count){});
 
 *
 
 */
 
class TimerGameCalendar {
 
public:
 
	enum Trigger {
 
		DAY,
 
		MONTH,
 
		YEAR,
 
	};
 
	enum Priority {
 
		NONE, ///< These timers can be executed in any order; there is no Random() in them, so order is not relevant.
 

	
 
		/* All other may have a Random() call in them, so order is important.
 
		 * For safety, you can only setup a single timer on a single priority. */
 
		AUTOSAVE,
 
		COMPANY,
 
		DISASTER,
 
		ENGINE,
 
		INDUSTRY,
 
		STATION,
 
		SUBSIDY,
 
		TOWN,
 
		VEHICLE,
 
	};
 

	
 
	struct TPeriod {
 
		Trigger trigger;
 
		Priority priority;
 

	
 
		TPeriod(Trigger trigger, Priority priority) : trigger(trigger), priority(priority) {}
 

	
 
		bool operator < (const TPeriod &other) const
 
		{
 
			if (this->trigger != other.trigger) return this->trigger < other.trigger;
 
			return this->priority < other.priority;
 
		}
 

	
 
		bool operator == (const TPeriod &other) const
 
		{
 
			return this->trigger == other.trigger && this->priority == other.priority;
 
		}
 
	};
 

	
 
	using TElapsed = uint;
 
	struct TStorage {
 
	};
 
};
 

	
 
#endif /* TIMER_GAME_CALENDAR_H */
src/timer/timer_manager.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer_manager.h Definition of the TimerManager */
 
/** @note don't include this file; include "timer.h". */
 

	
 
#ifndef TIMER_MANAGER_H
 
#define TIMER_MANAGER_H
 

	
 
#include "stdafx.h"
 

	
 
#include <set>
 

	
 
template <typename TTimerType>
 
class BaseTimer;
 

	
 
/**
 
 * The TimerManager manages a single Timer-type.
 
 *
 
 * It allows for automatic registration and unregistration of timers like Interval and OneShot.
 
 *
 
 * Each Timer-type needs to implement the Elapsed() method, and distribute that to the timers if needed.
 
 */
 
template <typename TTimerType>
 
class TimerManager {
 
public:
 
	using TElapsed = typename TTimerType::TElapsed;
 

	
 
	/* Avoid copying this object; it is a singleton object. */
 
	TimerManager(TimerManager const &) = delete;
 
	TimerManager &operator=(TimerManager const &) = delete;
 

	
 
	/**
 
	 * Register a timer.
 
	 *
 
	 * @param timer The timer to register.
 
	 */
 
	static void RegisterTimer(BaseTimer<TTimerType> &timer) {
 
		GetTimers().insert(&timer);
 
	}
 

	
 
	/**
 
	 * Unregister a timer.
 
	 *
 
	 * @param timer The timer to unregister.
 
	 */
 
	static void UnregisterTimer(BaseTimer<TTimerType> &timer) {
 
		GetTimers().erase(&timer);
 
	}
 

	
 
	/**
 
	 * Called when time for this timer elapsed.
 
	 *
 
	 * The implementation per type is different, but they all share a similar goal:
 
	 *   Call the Elapsed() method of all active timers.
 
	 *
 
	 * @param value The amount of time that has elapsed.
 
	 */
 
	static void Elapsed(TElapsed value);
 

	
 
private:
 
	/**
 
	 * Sorter for timers.
 
	 *
 
	 * It will sort based on the period, smaller first. If the period is the
 
	 * same, it will sort based on the pointer value.
 
	 */
 
	struct base_timer_sorter {
 
		bool operator() (BaseTimer<TTimerType> *a, BaseTimer<TTimerType> *b) const {
 
			if (a->period == b->period) return a < b;
 
			return a->period < b->period;
 
		}
 
	};
 

	
 
	/** Singleton list, to store all the active timers. */
 
	static std::set<BaseTimer<TTimerType> *, base_timer_sorter> &GetTimers() {
 
		static std::set<BaseTimer<TTimerType> *, base_timer_sorter> timers;
 
		return timers;
 
	}
 
};
 

	
 
#endif /* TIMER_MANAGER_H */
src/timer/timer_window.cpp
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file timer_window.cpp
 
 * This file implements the timer logic for the Window system.
 
 */
 

	
 
#include "stdafx.h"
 
#include "timer.h"
 
#include "timer_window.h"
 

	
 
#include "safeguards.h"
 

	
 
template<>
 
void IntervalTimer<TimerWindow>::Elapsed(TimerWindow::TElapsed delta)
 
{
 
	if (this->period == std::chrono::milliseconds::zero()) return;
 

	
 
	this->storage.elapsed += delta;
 

	
 
	uint count = 0;
 
	while (this->storage.elapsed >= this->period) {
 
		this->storage.elapsed -= this->period;
 
		count++;
 
	}
 

	
 
	if (count > 0) {
 
		this->callback(count);
 
	}
 
}
 

	
 
template<>
 
void TimeoutTimer<TimerWindow>::Elapsed(TimerWindow::TElapsed delta)
 
{
 
	if (this->fired) return;
 
	if (this->period == std::chrono::milliseconds::zero()) return;
 

	
 
	this->storage.elapsed += delta;
 

	
 
	if (this->storage.elapsed >= this->period) {
 
		this->callback();
 
		this->fired = true;
 
	}
 
}
 

	
 
template<>
 
void TimerManager<TimerWindow>::Elapsed(TimerWindow::TElapsed delta)
 
{
 
	/* Make a temporary copy of the timers, as a timer's callback might add/remove other timers. */
 
	auto timers = TimerManager<TimerWindow>::GetTimers();
 

	
 
	for (auto timer : timers) {
 
		timer->Elapsed(delta);
 
	}
 
}
src/timer/timer_window.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file timer_window.h Definition of the Window system */
 

	
 
#ifndef TIMER_WINDOW_H
 
#define TIMER_WINDOW_H
 

	
 
#include <chrono>
 

	
 
/**
 
 * Timer that represents the real time, usable for the Window system.
 
 *
 
 * This can be used to create intervals based on milliseconds, seconds, etc.
 
 * Mostly used for animation, scrolling, etc.
 
 *
 
 * Please be mindful that the order in which timers are called is not guaranteed.
 
 *
 
 * @note The lowest possible interval is 1ms.
 
 * @note These timers can only be used in the Window system.
 
 */
 
class TimerWindow {
 
public:
 
	using TPeriod = std::chrono::milliseconds;
 
	using TElapsed = std::chrono::milliseconds;
 
	struct TStorage {
 
		std::chrono::milliseconds elapsed;
 
	};
 
};
 

	
 
#endif /* TIMER_WINDOW_H */
src/window.cpp
Show inline comments
 
@@ -39,6 +39,8 @@
 
#include "network/network_func.h"
 
#include "guitimer_func.h"
 
#include "news_func.h"
 
#include "timer/timer.h"
 
#include "timer/timer_window.h"
 

	
 
#include "safeguards.h"
 

	
 
@@ -3076,6 +3078,34 @@ void CallWindowRealtimeTickEvent(uint de
 
	}
 
}
 

	
 
/** Update various of window-related information on a regular interval. */
 
static IntervalTimer<TimerWindow> window_interval(std::chrono::milliseconds(30), [](auto) {
 
	extern int _caret_timer;
 
	_caret_timer += 3;
 
	CursorTick();
 

	
 
	HandleKeyScrolling();
 
	HandleAutoscroll();
 
	DecreaseWindowCounters();
 
});
 

	
 
/** Blink the window highlight colour constantly. */
 
static IntervalTimer<TimerWindow> highlight_interval(std::chrono::milliseconds(450), [](auto) {
 
	_window_highlight_colour = !_window_highlight_colour;
 
});
 

	
 
/** Blink all windows marked with a white border. */
 
static IntervalTimer<TimerWindow> white_border_interval(std::chrono::milliseconds(30), [](auto) {
 
	if (_network_dedicated) return;
 

	
 
	for (Window *w : Window::Iterate()) {
 
		if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
 
			CLRBITS(w->flags, WF_WHITE_BORDER);
 
			w->SetDirty();
 
		}
 
	}
 
});
 

	
 
/**
 
 * Update the continuously changing contents of the windows, such as the viewports
 
 */
 
@@ -3093,56 +3123,21 @@ void UpdateWindows()
 

	
 
	ProcessPendingPerformanceMeasurements();
 

	
 
	TimerManager<TimerWindow>::Elapsed(std::chrono::milliseconds(delta_ms));
 
	CallWindowRealtimeTickEvent(delta_ms);
 

	
 
	static GUITimer network_message_timer = GUITimer(1);
 
	if (network_message_timer.Elapsed(delta_ms)) {
 
		network_message_timer.SetInterval(1000);
 
		NetworkChatMessageLoop();
 
	}
 

	
 
	/* Process invalidations before anything else. */
 
	for (Window *w : Window::Iterate()) {
 
		w->ProcessScheduledInvalidations();
 
		w->ProcessHighlightedInvalidations();
 
	}
 

	
 
	static GUITimer window_timer = GUITimer(1);
 
	if (window_timer.Elapsed(delta_ms)) {
 
		if (_network_dedicated) window_timer.SetInterval(MILLISECONDS_PER_TICK);
 

	
 
		extern int _caret_timer;
 
		_caret_timer += 3;
 
		CursorTick();
 

	
 
		HandleKeyScrolling();
 
		HandleAutoscroll();
 
		DecreaseWindowCounters();
 
	}
 

	
 
	static GUITimer highlight_timer = GUITimer(1);
 
	if (highlight_timer.Elapsed(delta_ms)) {
 
		highlight_timer.SetInterval(450);
 
		_window_highlight_colour = !_window_highlight_colour;
 
	}
 

	
 
	if (!_pause_mode || _game_mode == GM_EDITOR || _settings_game.construction.command_pause_level > CMDPL_NO_CONSTRUCTION) MoveAllTextEffects(delta_ms);
 

	
 
	/* Skip the actual drawing on dedicated servers without screen.
 
	 * But still empty the invalidation queues above. */
 
	if (_network_dedicated) return;
 

	
 
	if (window_timer.HasElapsed()) {
 
		window_timer.SetInterval(MILLISECONDS_PER_TICK);
 

	
 
		for (Window *w : Window::Iterate()) {
 
			if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
 
				CLRBITS(w->flags, WF_WHITE_BORDER);
 
				w->SetDirty();
 
			}
 
		}
 
	}
 

	
 
	DrawDirtyBlocks();
 

	
 
	for (Window *w : Window::Iterate()) {
0 comments (0 inline, 0 general)