OpenTTD Source  20241108-master-g80f628063a
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 <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "../stdafx.h"
11 #include "../core/backup_type.hpp"
12 #include "../core/bitmath_func.hpp"
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 */ AIScannerInfo *AI::scanner_info = nullptr;
28 /* static */ 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 */
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  Backup<CompanyID> 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::SSS_FORCE_GAME));
49  config = c->ai_config.get();
50  }
51 
52  AIInfo *info = config->GetInfo();
53  if (info == nullptr) {
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  cur_company.Restore();
69 
71  return;
72 }
73 
74 /* static */ void AI::GameLoop()
75 {
76  /* If we are in networking, only servers run this function, and that only if it is allowed */
78 
79  /* The speed with which AIs go, is limited by the 'competitor_speed' */
82  if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
83 
85  for (const Company *c : Company::Iterate()) {
86  if (c->is_ai) {
87  PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index));
88  cur_company.Change(c->index);
89  c->ai_instance->GameLoop();
90  /* Occasionally collect garbage; every 255 ticks do one company.
91  * Effectively collecting garbage once every two months per AI. */
92  if ((AI::frame_counter & 255) == 0 && (CompanyID)GB(AI::frame_counter, 8, 4) == c->index) {
93  c->ai_instance->CollectGarbage();
94  }
95  } else {
97  }
98  }
99  cur_company.Restore();
100 }
101 
102 /* static */ uint AI::GetTick()
103 {
104  return AI::frame_counter;
105 }
106 
107 /* static */ void AI::Stop(CompanyID company)
108 {
109  if (_networking && !_network_server) return;
111 
112  Backup<CompanyID> cur_company(_current_company, company);
113  Company *c = Company::Get(company);
114 
115  c->ai_instance.reset();
116  c->ai_info = nullptr;
117  c->ai_config.reset();
118 
119  cur_company.Restore();
120 
122 }
123 
124 /* static */ void AI::Pause(CompanyID company)
125 {
126  /* The reason why dedicated servers are forbidden to execute this
127  * command is not because it is unsafe, but because there is no way
128  * for the server owner to unpause the script again. */
129  if (_network_dedicated) return;
130 
131  Backup<CompanyID> cur_company(_current_company, company);
132  Company::Get(company)->ai_instance->Pause();
133 
134  cur_company.Restore();
135 }
136 
137 /* static */ void AI::Unpause(CompanyID company)
138 {
139  Backup<CompanyID> cur_company(_current_company, company);
140  Company::Get(company)->ai_instance->Unpause();
141 
142  cur_company.Restore();
143 }
144 
145 /* static */ bool AI::IsPaused(CompanyID company)
146 {
147  Backup<CompanyID> cur_company(_current_company, company);
148  bool paused = Company::Get(company)->ai_instance->IsPaused();
149 
150  cur_company.Restore();
151 
152  return paused;
153 }
154 
155 /* static */ void AI::KillAll()
156 {
157  /* It might happen there are no companies .. than we have nothing to loop */
158  if (Company::GetPoolSize() == 0) return;
159 
160  for (const Company *c : Company::Iterate()) {
161  if (c->is_ai) AI::Stop(c->index);
162  }
163 }
164 
165 /* static */ void AI::Initialize()
166 {
167  if (AI::scanner_info != nullptr) AI::Uninitialize(true);
168 
169  AI::frame_counter = 0;
170  if (AI::scanner_info == nullptr) {
173  AI::scanner_info->Initialize();
175  AI::scanner_library->Initialize();
176  }
177 }
178 
179 /* static */ void AI::Uninitialize(bool keepConfig)
180 {
181  AI::KillAll();
182 
183  if (keepConfig) {
184  /* Run a rescan, which indexes all AIInfos again, and check if we can
185  * still load all the AIS, while keeping the configs in place */
186  Rescan();
187  } else {
188  delete AI::scanner_info;
189  delete AI::scanner_library;
190  AI::scanner_info = nullptr;
191  AI::scanner_library = nullptr;
192 
193  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
194  if (_settings_game.ai_config[c] != nullptr) {
195  delete _settings_game.ai_config[c];
196  _settings_game.ai_config[c] = nullptr;
197  }
198  if (_settings_newgame.ai_config[c] != nullptr) {
199  delete _settings_newgame.ai_config[c];
200  _settings_newgame.ai_config[c] = nullptr;
201  }
202  }
203  }
204 }
205 
206 /* static */ void AI::ResetConfig()
207 {
208  /* Check for both newgame as current game if we can reload the AIInfo inside
209  * the AIConfig. If not, remove the AI from the list (which will assign
210  * a random new AI on reload). */
211  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
212  if (_settings_game.ai_config[c] != nullptr && _settings_game.ai_config[c]->HasScript()) {
213  if (!_settings_game.ai_config[c]->ResetInfo(true)) {
214  Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
215  _settings_game.ai_config[c]->Change(std::nullopt);
216  }
217  }
218 
219  if (_settings_newgame.ai_config[c] != nullptr && _settings_newgame.ai_config[c]->HasScript()) {
220  if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
221  Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
222  _settings_newgame.ai_config[c]->Change(std::nullopt);
223  }
224  }
225 
226  if (Company::IsValidAiID(c) && Company::Get(c)->ai_config != nullptr) {
227  AIConfig *config = Company::Get(c)->ai_config.get();
228  if (!config->ResetInfo(true)) {
229  /* The code belonging to an already running AI was deleted. We can only do
230  * one thing here to keep everything sane and that is kill the AI. After
231  * killing the offending AI we start a random other one in it's place, just
232  * like what would happen if the AI was missing during loading. */
233  AI::Stop(c);
234  AI::StartNew(c);
235  } else {
236  /* Update the reference in the Company struct. */
237  Company::Get(c)->ai_info = config->GetInfo();
238  }
239  }
240  }
241 }
242 
243 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
244 {
245  ScriptObjectRef counter(event);
246 
247  /* Clients should ignore events */
248  if (_networking && !_network_server) {
249  return;
250  }
251 
252  /* Only AIs can have an event-queue */
253  if (!Company::IsValidAiID(company)) {
254  return;
255  }
256 
257  /* Queue the event */
258  Backup<CompanyID> cur_company(_current_company, company);
259  Company::Get(_current_company)->ai_instance->InsertEvent(event);
260  cur_company.Restore();
261 }
262 
263 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
264 {
265  ScriptObjectRef counter(event);
266 
267  /* Clients should ignore events */
268  if (_networking && !_network_server) {
269  return;
270  }
271 
272  /* Try to send the event to all AIs */
273  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
274  if (c != skip_company) AI::NewEvent(c, event);
275  }
276 }
277 
278 /* static */ void AI::Save(CompanyID company)
279 {
280  if (!_networking || _network_server) {
281  Company *c = Company::GetIfValid(company);
282  assert(c != nullptr);
283 
284  /* When doing emergency saving, an AI can be not fully initialised. */
285  if (c->ai_instance != nullptr) {
286  Backup<CompanyID> cur_company(_current_company, company);
287  c->ai_instance->Save();
288  cur_company.Restore();
289  return;
290  }
291  }
292 
294 }
295 
296 /* static */ void AI::GetConsoleList(std::back_insert_iterator<std::string> &output_iterator, bool newest_only)
297 {
298  AI::scanner_info->GetConsoleList(output_iterator, newest_only);
299 }
300 
301 /* static */ void AI::GetConsoleLibraryList(std::back_insert_iterator<std::string> &output_iterator)
302 {
303  AI::scanner_library->GetConsoleList(output_iterator, true);
304 }
305 
306 /* static */ const ScriptInfoList *AI::GetInfoList()
307 {
308  return AI::scanner_info->GetInfoList();
309 }
310 
312 {
314 }
315 
316 /* static */ AIInfo *AI::FindInfo(const std::string &name, int version, bool force_exact_match)
317 {
318  return AI::scanner_info->FindInfo(name, version, force_exact_match);
319 }
320 
321 /* static */ AILibrary *AI::FindLibrary(const std::string &library, int version)
322 {
323  return AI::scanner_library->FindLibrary(library, version);
324 }
325 
326 /* static */ void AI::Rescan()
327 {
329 
332  ResetConfig();
333 
337 }
338 
345 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
346 {
347  return AI::scanner_info->HasScript(ci, md5sum);
348 }
349 
350 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
351 {
352  return AI::scanner_library->HasScript(ci, md5sum);
353 }
354 
356 {
357  return AI::scanner_info;
358 }
359 
361 {
362  return AI::scanner_library;
363 }
364 
Base functions for all AIs.
AIConfig stores the configuration settings of every AI.
AIInfo keeps track of all information of an AI, like Author, Description, ...
The AIInstance tracks an AI.
declarations of the class for AI scanner
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=SSS_DEFAULT)
Get the config of a company.
Definition: ai_config.cpp:20
bool ResetInfo(bool force_exact_match)
When ever the AI Scanner is reloaded, all infos become invalid.
Definition: ai_config.cpp:48
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:57
class AIInfo * FindInfo(const std::string &name, int version, bool force_exact_match)
Check if we have an AI by name and version available in our list.
Definition: ai_scanner.cpp:95
class AIInfo * SelectRandomAI() const
Select a random AI.
Definition: ai_scanner.cpp:61
class AILibrary * FindLibrary(const std::string &library, int version)
Find a library in the pool.
Definition: ai_scanner.cpp:148
static uint GetTick()
Get the current AI tick.
Definition: ai_core.cpp:102
static class AIScannerInfo * scanner_info
ScriptScanner instance that is used to find AIs.
Definition: ai.hpp:142
static void Uninitialize(bool keepConfig)
Uninitialize the AI system.
Definition: ai_core.cpp:179
static AIScannerLibrary * GetScannerLibrary()
Gets the ScriptScanner instance that is used to find AI Libraries.
Definition: ai_core.cpp:360
static bool HasAI(const struct ContentInfo *ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition: ai_core.cpp:345
static void Pause(CompanyID company)
Suspend the AI and then pause execution of the script.
Definition: ai_core.cpp:124
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator)
Wrapper function for AIScanner::GetAIConsoleLibraryList.
Definition: ai_core.cpp:301
static void Initialize()
Initialize the AI system.
Definition: ai_core.cpp:165
static class AIScannerLibrary * scanner_library
ScriptScanner instance that is used to find AI Libraries.
Definition: ai.hpp:143
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Wrapper function for AIScanner::GetAIConsoleList.
Definition: ai_core.cpp:296
static void GameLoop()
Called every game-tick to let AIs do something.
Definition: ai_core.cpp:74
static bool CanStartNew()
Is it possible to start a new AI company?
Definition: ai_core.cpp:30
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=MAX_COMPANIES)
Broadcast a new event to all active AIs.
Definition: ai_core.cpp:263
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:107
static class AILibrary * FindLibrary(const std::string &library, int version)
Wrapper function for AIScanner::FindLibrary.
Definition: ai_core.cpp:321
static void ResetConfig()
Reset all AIConfigs, and make them reload their AIInfo.
Definition: ai_core.cpp:206
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition: ai_core.cpp:243
static AIScannerInfo * GetScannerInfo()
Gets the ScriptScanner instance that is used to find AIs.
Definition: ai_core.cpp:355
static const ScriptInfoList * GetUniqueInfoList()
Wrapper function for AIScanner::GetUniqueAIInfoList.
Definition: ai_core.cpp:311
static void Rescan()
Rescans all searchpaths for available AIs.
Definition: ai_core.cpp:326
static uint frame_counter
Tick counter for the AI code.
Definition: ai.hpp:141
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:278
static void Unpause(CompanyID company)
Resume execution of the AI.
Definition: ai_core.cpp:137
static void KillAll()
Kill any and all AIs we manage.
Definition: ai_core.cpp:155
static const ScriptInfoList * GetInfoList()
Wrapper function for AIScanner::GetAIInfoList.
Definition: ai_core.cpp:306
static class AIInfo * FindInfo(const std::string &name, int version, bool force_exact_match)
Wrapper function for AIScanner::FindInfo.
Definition: ai_core.cpp:316
RAII class for measuring simple elements of performance.
static void SetInactive(PerformanceElement elem)
Mark a performance element as not currently in use.
@ SSS_FORCE_GAME
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.
bool HasScript() const
Is this config attached to an Script? In other words, is there a Script that is assigned to this slot...
void Change(std::optional< const std::string > 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.
const std::string & GetName() const
Get the Name of the script.
Definition: script_info.hpp:46
static void SaveEmpty()
Don't save any data in the savegame.
bool HasScript(const struct ContentInfo *ci, bool md5sum)
Check whether we have a script with the exact characteristics as ci.
const ScriptInfoList * GetUniqueInfoList()
Get the list of the latest version of all registered scripts.
const ScriptInfoList * GetInfoList()
Get the list of all registered scripts.
void RescanDir()
Rescan the script dir.
void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only) const
Get the list of registered scripts to print on the console.
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:375
@ AI
Scan for AIs and its libraries.
Definition: fileio_func.h:67
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ COMPANY_FIRST
First company, same as owner.
Definition: company_type.h:22
@ MAX_COMPANIES
Maximum number of companies.
Definition: company_type.h:23
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
PerformanceElement
Elements of game performance that can be measured.
@ PFE_AI0
AI execution for player slot 1.
bool _networking
are we in networking mode?
Definition: network.cpp:65
bool _network_dedicated
are we a dedicated server?
Definition: network.cpp:68
bool _network_server
network-server is active
Definition: network.cpp:66
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:57
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition: settings.cpp:58
bool ai_in_multiplayer
so we allow AIs in multiplayer
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:21
void Change(const U &new_value)
Change the value of the variable.
Definition: backup_type.hpp:82
void Restore()
Restore the variable.
static bool IsValidAiID(size_t index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Definition: company_base.h:159
Container for all important information about a piece of content.
uint8_t competitor_speed
the speed at which the AI builds
class AIConfig * ai_config[MAX_COMPANIES]
settings per company
AISettings ai
what may the AI do?
DifficultySettings difficulty
settings related to the difficulty
static size_t GetPoolSize()
Returns first unused index.
Definition: pool_type.hpp:360
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:350
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition: window.cpp:3119
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:3211
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:3228
@ WC_SCRIPT_SETTINGS
Script settings; Window numbers:
Definition: window_type.h:175
@ WC_SCRIPT_LIST
Scripts list; Window numbers:
Definition: window_type.h:284
@ WC_SCRIPT_DEBUG
Script debug window; Window numbers:
Definition: window_type.h:674