OpenTTD Source 20260711-master-g3fb3006dff
ai_core.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "../stdafx.h"
13#include "../company_base.h"
14#include "../company_func.h"
15#include "../network/network.h"
16#include "../window_func.h"
17#include "../framerate_type.h"
18#include "ai_scanner.hpp"
19#include "ai_instance.hpp"
20#include "ai_config.hpp"
21#include "ai_info.hpp"
22#include "ai.hpp"
23
24#include "../safeguards.h"
25
26/* static */ uint AI::frame_counter = 0;
27/* static */ std::unique_ptr<AIScannerInfo> AI::scanner_info = nullptr;
28/* static */ std::unique_ptr<AIScannerLibrary> AI::scanner_library = nullptr;
29
30/* static */ bool AI::CanStartNew()
31{
32 /* Only allow new AIs on the server and only when that is allowed in multiplayer */
33 return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer);
34}
35
36/* static */ void AI::StartNew(CompanyID company)
37{
38 assert(Company::IsValidID(company));
39
40 /* Clients shouldn't start AIs */
41 if (_networking && !_network_server) return;
42
43 AutoRestoreBackup cur_company(_current_company, company);
44 Company *c = Company::Get(company);
45
46 AIConfig *config = c->ai_config.get();
47 if (config == nullptr) {
48 c->ai_config = std::make_unique<AIConfig>(*AIConfig::GetConfig(company, AIConfig::ScriptSettingSource::ForceCurrentGame));
49 config = c->ai_config.get();
50 }
51
52 AIInfo *info = config->GetInfo();
53 if (info == nullptr) {
54 info = AI::scanner_info->SelectRandomAI();
55 assert(info != nullptr);
56 /* Load default data and store the name in the settings */
57 config->Change(info->GetName(), -1, false);
58 }
60
61 c->ai_info = info;
62 assert(c->ai_instance == nullptr);
63 c->ai_instance = std::make_unique<AIInstance>();
64 c->ai_instance->Initialize(info);
65 c->ai_instance->LoadOnStack(config->GetToLoadData());
66 config->SetToLoadData(nullptr);
67
68 InvalidateWindowClassesData(WindowClass::ScriptDebug, -1);
69 return;
70}
71
77static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
78{
79 return static_cast<PerformanceElement>(PerformanceElement::AI0 + company);
80}
81
82/* static */ void AI::GameLoop()
83{
84 /* If we are in networking, only servers run this function, and that only if it is allowed */
85 if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
86
87 /* The speed with which AIs go, is limited by the 'competitor_speed' */
89 assert(_settings_game.difficulty.competitor_speed <= 4);
90 if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
91
92 for (const Company *c : Company::Iterate()) {
93 if (c->is_ai) {
95 AutoRestoreBackup cur_company(_current_company, c->index);
96 c->ai_instance->GameLoop();
97 /* Occasionally collect garbage; every 255 ticks do one company.
98 * Effectively collecting garbage once every two months per AI. */
99 if ((AI::frame_counter & 255) == 0 && (CompanyID)GB(AI::frame_counter, 8, 4) == c->index) {
100 c->ai_instance->CollectGarbage();
101 }
102 } else {
104 }
105 }
106}
107
108/* static */ uint AI::GetTick()
109{
110 return AI::frame_counter;
111}
112
113/* static */ void AI::Stop(CompanyID company)
114{
115 if (_networking && !_network_server) return;
117
118 AutoRestoreBackup cur_company(_current_company, company);
119 Company *c = Company::Get(company);
120
121 c->ai_instance.reset();
122 c->ai_info = nullptr;
123 c->ai_config.reset();
124
125 InvalidateWindowClassesData(WindowClass::ScriptDebug, -1);
126}
127
128/* static */ void AI::Pause(CompanyID company)
129{
130 /* The reason why dedicated servers are forbidden to execute this
131 * command is not because it is unsafe, but because there is no way
132 * for the server owner to unpause the script again. */
133 if (_network_dedicated) return;
134
135 AutoRestoreBackup cur_company(_current_company, company);
136 Company::Get(company)->ai_instance->Pause();
137}
138
139/* static */ void AI::Unpause(CompanyID company)
140{
141 AutoRestoreBackup cur_company(_current_company, company);
142 Company::Get(company)->ai_instance->Unpause();
143}
144
145/* static */ bool AI::IsPaused(CompanyID company)
146{
147 AutoRestoreBackup cur_company(_current_company, company);
148 return Company::Get(company)->ai_instance->IsPaused();
149}
150
151/* static */ void AI::KillAll()
152{
153 /* It might happen there are no companies .. than we have nothing to loop */
154 if (Company::GetPoolSize() == 0) return;
155
156 for (const Company *c : Company::Iterate()) {
157 if (c->is_ai) AI::Stop(c->index);
158 }
159}
160
161/* static */ void AI::Initialize()
162{
163 if (AI::scanner_info != nullptr) AI::Uninitialize(true);
164
166 if (AI::scanner_info == nullptr) {
168 AI::scanner_info = std::make_unique<AIScannerInfo>();
169 AI::scanner_info->Initialize();
170 AI::scanner_library = std::make_unique<AIScannerLibrary>();
171 AI::scanner_library->Initialize();
172 }
173}
174
175/* static */ void AI::Uninitialize(bool keepConfig)
176{
177 AI::KillAll();
178
179 if (keepConfig) {
180 /* Run a rescan, which indexes all AIInfos again, and check if we can
181 * still load all the AIS, while keeping the configs in place */
182 Rescan();
183 } else {
184 AI::scanner_info.reset();
185 AI::scanner_library.reset();
186
187 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
188 _settings_game.script_config.ai[c].reset();
189 _settings_newgame.script_config.ai[c].reset();
190 }
191 }
192}
193
194/* static */ void AI::ResetConfig()
195{
196 /* Check for both newgame as current game if we can reload the AIInfo inside
197 * the AIConfig. If not, remove the AI from the list (which will assign
198 * a random new AI on reload). */
199 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
200 if (_settings_game.script_config.ai[c] != nullptr && _settings_game.script_config.ai[c]->HasScript()) {
201 if (!_settings_game.script_config.ai[c]->ResetInfo(true)) {
202 Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_game.script_config.ai[c]->GetName());
203 _settings_game.script_config.ai[c]->Change(std::nullopt);
204 }
205 }
206
207 if (_settings_newgame.script_config.ai[c] != nullptr && _settings_newgame.script_config.ai[c]->HasScript()) {
208 if (!_settings_newgame.script_config.ai[c]->ResetInfo(false)) {
209 Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_newgame.script_config.ai[c]->GetName());
210 _settings_newgame.script_config.ai[c]->Change(std::nullopt);
211 }
212 }
213
214 if (Company::IsValidAiID(c) && Company::Get(c)->ai_config != nullptr) {
215 AIConfig *config = Company::Get(c)->ai_config.get();
216 if (!config->ResetInfo(true)) {
217 /* The code belonging to an already running AI was deleted. We can only do
218 * one thing here to keep everything sane and that is kill the AI. After
219 * killing the offending AI we start a random other one in it's place, just
220 * like what would happen if the AI was missing during loading. */
221 AI::Stop(c);
222 AI::StartNew(c);
223 } else {
224 /* Update the reference in the Company struct. */
225 Company::Get(c)->ai_info = config->GetInfo();
226 }
227 }
228 }
229}
230
231/* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
232{
233 ScriptObjectRef counter(event);
234
235 /* Clients should ignore events */
237 return;
238 }
239
240 /* Only AIs can have an event-queue */
241 if (!Company::IsValidAiID(company)) {
242 return;
243 }
244
245 /* Queue the event */
246 AutoRestoreBackup cur_company(_current_company, company);
247 Company::Get(_current_company)->ai_instance->InsertEvent(event);
248}
249
250/* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
251{
252 ScriptObjectRef counter(event);
253
254 /* Clients should ignore events */
256 return;
257 }
258
259 /* Try to send the event to all AIs */
260 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
261 if (c != skip_company) AI::NewEvent(c, event);
262 }
263}
264
265/* static */ void AI::Save(CompanyID company)
266{
268 Company *c = Company::GetIfValid(company);
269 assert(c != nullptr);
270
271 /* When doing emergency saving, an AI can be not fully initialised. */
272 if (c->ai_instance != nullptr) {
273 AutoRestoreBackup cur_company(_current_company, company);
274 c->ai_instance->Save();
275 return;
276 }
277 }
278
280}
281
282/* static */ void AI::GetConsoleList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only)
283{
284 AI::scanner_info->GetConsoleList(output_iterator, newest_only);
285}
286
287/* static */ void AI::GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only)
288{
289 AI::scanner_library->GetConsoleList(output_iterator, newest_only);
290}
291
292/* static */ const ScriptInfoList *AI::GetInfoList()
293{
294 return AI::scanner_info->GetInfoList();
295}
296
298{
299 return AI::scanner_info->GetUniqueInfoList();
300}
301
302/* static */ AIInfo *AI::FindInfo(const std::string &name, int version, bool force_exact_match)
303{
304 return AI::scanner_info->FindInfo(name, version, force_exact_match);
305}
306
307/* static */ AILibrary *AI::FindLibrary(const std::string &library, int version)
308{
309 return AI::scanner_library->FindLibrary(library, version);
310}
311
312/* static */ void AI::Rescan()
313{
315
316 AI::scanner_info->RescanDir();
317 AI::scanner_library->RescanDir();
318 ResetConfig();
319
320 InvalidateWindowData(WindowClass::ScriptList, 0, 1);
321 SetWindowClassesDirty(WindowClass::ScriptDebug);
322 InvalidateWindowClassesData(WindowClass::ScriptSettings);
323}
324
331/* static */ bool AI::HasAI(const ContentInfo &ci, bool md5sum)
332{
333 return AI::scanner_info->HasScript(ci, md5sum);
334}
335
342/* static */ bool AI::HasAILibrary(const ContentInfo &ci, bool md5sum)
343{
344 return AI::scanner_library->HasScript(ci, md5sum);
345}
346
352{
353 return AI::scanner_info.get();
354}
355
361{
362 return AI::scanner_library.get();
363}
364
Base functions for all AIs.
AIConfig stores the configuration settings of every AI.
static constexpr PerformanceElement GetAIPerformanceElement(CompanyID company)
Get the PerformanceElement for the AI of the given company.
Definition ai_core.cpp:77
AIInfo keeps track of all information of an AI, like Author, Description, ...
The AIInstance tracks an AI.
Declarations of the class for AI scanner.
Class for backupping variables and making sure they are restored later.
Functions related to bit mathematics.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
AI instantion of script configuration.
Definition ai_config.hpp:17
class AIInfo * GetInfo() const
Get the ScriptInfo linked to this ScriptConfig.
Definition ai_config.cpp:37
bool ResetInfo(bool force_exact_match)
When ever the AI Scanner is reloaded, all infos become invalid.
Definition ai_config.cpp:47
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=ScriptSettingSource::Default)
Get the AI configuration of specific company.
Definition ai_config.cpp:20
All static information from an AI like name, version, etc.
Definition ai_info.hpp:16
All static information from an AI library like name, version, etc.
Definition ai_info.hpp:69
AI instantiation of a ScriptScanner.
AI instantiation of a ScriptScanner for libraries.
static uint GetTick()
Get the current AI tick.
Definition ai_core.cpp:108
static std::unique_ptr< AIScannerLibrary > scanner_library
ScriptScanner instance that is used to find AI Libraries.
Definition ai.hpp:149
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
Definition ai_core.cpp:287
static void Uninitialize(bool keepConfig)
Uninitialize the AI system.
Definition ai_core.cpp:175
static AIScannerLibrary * GetScannerLibrary()
Gets the ScriptScanner instance that is used to find AI Libraries.
Definition ai_core.cpp:360
static void Pause(CompanyID company)
Suspend the AI and then pause execution of the script.
Definition ai_core.cpp:128
static void Initialize()
Initialize the AI system.
Definition ai_core.cpp:161
static bool HasAILibrary(const ContentInfo &ci, bool md5sum)
Check whether we have an AI library with the exact characteristics as ci.
Definition ai_core.cpp:342
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
Definition ai_core.cpp:282
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=CompanyID::Invalid())
Broadcast a new event to all active AIs.
Definition ai_core.cpp:250
static std::unique_ptr< AIScannerInfo > scanner_info
ScriptScanner instance that is used to find AIs.
Definition ai.hpp:148
static void GameLoop()
Called every game-tick to let AIs do something.
Definition ai_core.cpp:82
static bool CanStartNew()
Is it possible to start a new AI company?
Definition ai_core.cpp:30
static void StartNew(CompanyID company)
Start a new AI company.
Definition ai_core.cpp:36
static void Stop(CompanyID company)
Stop a company to be controlled by an AI.
Definition ai_core.cpp:113
static class AILibrary * FindLibrary(const std::string &library, int version)
Find a library.
Definition ai_core.cpp:307
static void ResetConfig()
Reset all AIConfigs, and make them reload their AIInfo.
Definition ai_core.cpp:194
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:231
static AIScannerInfo * GetScannerInfo()
Gets the ScriptScanner instance that is used to find AIs.
Definition ai_core.cpp:351
static const ScriptInfoList * GetUniqueInfoList()
Get the list of the latest version of all registered scripts.
Definition ai_core.cpp:297
static bool HasAI(const ContentInfo &ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition ai_core.cpp:331
static void Rescan()
Rescans all searchpaths for available AIs.
Definition ai_core.cpp:312
static uint frame_counter
Tick counter for the AI code.
Definition ai.hpp:147
static bool IsPaused(CompanyID company)
Checks if the AI is paused.
Definition ai_core.cpp:145
static void Save(CompanyID company)
Save data from an AI to a savegame.
Definition ai_core.cpp:265
static void Unpause(CompanyID company)
Resume execution of the AI.
Definition ai_core.cpp:139
static void KillAll()
Kill any and all AIs we manage.
Definition ai_core.cpp:151
static const ScriptInfoList * GetInfoList()
Get the list of all registered scripts.
Definition ai_core.cpp:292
static class AIInfo * FindInfo(const std::string &name, int version, bool force_exact_match)
Finds the appropriate ScriptInfo for a given script name and version.
Definition ai_core.cpp:302
RAII class for measuring simple elements of performance.
static void SetInactive(PerformanceElement elem)
Mark a performance element as not currently in use.
@ ForceCurrentGame
Get the Script config from the current game.
void AnchorUnchangeableSettings()
As long as the default of a setting has not been changed, the value of the setting is not stored.
void Change(std::optional< std::string_view > name, int version=-1, bool force_exact_match=false)
Set another Script to be loaded in this slot.
const std::string & GetName() const
Get the Name of the script.
static void SaveEmpty()
Don't save any data in the savegame.
@ AI
Scan for AIs and its libraries.
Definition fileio_func.h:66
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition fileio.cpp:383
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Types for recording game performance data.
PerformanceElement
Elements of game performance that can be measured.
@ AI0
AI execution for player slot 1.
bool _networking
are we in networking mode?
Definition network.cpp:67
bool _network_dedicated
are we a dedicated server?
Definition network.cpp:70
bool _network_server
network-server is active
Definition network.cpp:68
Basic functions/variables used all over the place.
A number of safeguards to prevent using unsafe methods.
std::map< std::string, class ScriptInfo *, CaseInsensitiveComparator > ScriptInfoList
Type for the list of scripts.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition settings.cpp:62
Definition of base types and functions in a cross-platform compatible way.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
static bool IsValidAiID(auto index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Container for all important information about a piece of content.
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static Company * Get(auto index)
static Company * GetIfValid(auto index)
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3223
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3315
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3333
Window functions not directly related to making/drawing windows.