OpenTTD
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, bool rerandomise_ai)
37 {
38  assert(Company::IsValidID(company));
39 
40  /* Clients shouldn't start AIs */
41  if (_networking && !_network_server) return;
42 
44  AIInfo *info = config->GetInfo();
45  if (info == nullptr || (rerandomise_ai && config->IsRandom())) {
46  info = AI::scanner_info->SelectRandomAI();
47  assert(info != nullptr);
48  /* Load default data and store the name in the settings */
49  config->Change(info->GetName(), -1, false, true);
50  }
52 
53  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
54  Company *c = Company::Get(company);
55 
56  c->ai_info = info;
57  assert(c->ai_instance == nullptr);
58  c->ai_instance = new AIInstance();
59  c->ai_instance->Initialize(info);
60 
61  cur_company.Restore();
62 
64  return;
65 }
66 
67 /* static */ void AI::GameLoop()
68 {
69  /* If we are in networking, only servers run this function, and that only if it is allowed */
71 
72  /* The speed with which AIs go, is limited by the 'competitor_speed' */
75  if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
76 
77  Backup<CompanyID> cur_company(_current_company, FILE_LINE);
78  for (const Company *c : Company::Iterate()) {
79  if (c->is_ai) {
80  PerformanceMeasurer framerate((PerformanceElement)(PFE_AI0 + c->index));
81  cur_company.Change(c->index);
82  c->ai_instance->GameLoop();
83  } else {
85  }
86  }
87  cur_company.Restore();
88 
89  /* Occasionally collect garbage; every 255 ticks do one company.
90  * Effectively collecting garbage once every two months per AI. */
91  if ((AI::frame_counter & 255) == 0) {
93  if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
94  }
95 }
96 
97 /* static */ uint AI::GetTick()
98 {
99  return AI::frame_counter;
100 }
101 
102 /* static */ void AI::Stop(CompanyID company)
103 {
104  if (_networking && !_network_server) return;
106 
107  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
108  Company *c = Company::Get(company);
109 
110  delete c->ai_instance;
111  c->ai_instance = nullptr;
112  c->ai_info = nullptr;
113 
114  cur_company.Restore();
115 
118 }
119 
120 /* static */ void AI::Pause(CompanyID company)
121 {
122  /* The reason why dedicated servers are forbidden to execute this
123  * command is not because it is unsafe, but because there is no way
124  * for the server owner to unpause the script again. */
125  if (_network_dedicated) return;
126 
127  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
128  Company::Get(company)->ai_instance->Pause();
129 
130  cur_company.Restore();
131 }
132 
133 /* static */ void AI::Unpause(CompanyID company)
134 {
135  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
136  Company::Get(company)->ai_instance->Unpause();
137 
138  cur_company.Restore();
139 }
140 
141 /* static */ bool AI::IsPaused(CompanyID company)
142 {
143  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
144  bool paused = Company::Get(company)->ai_instance->IsPaused();
145 
146  cur_company.Restore();
147 
148  return paused;
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 
165  AI::frame_counter = 0;
166  if (AI::scanner_info == nullptr) {
168  AI::scanner_info = new AIScannerInfo();
169  AI::scanner_info->Initialize();
170  AI::scanner_library = new 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  delete AI::scanner_info;
185  delete AI::scanner_library;
186  AI::scanner_info = nullptr;
187  AI::scanner_library = nullptr;
188 
189  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
190  if (_settings_game.ai_config[c] != nullptr) {
191  delete _settings_game.ai_config[c];
192  _settings_game.ai_config[c] = nullptr;
193  }
194  if (_settings_newgame.ai_config[c] != nullptr) {
195  delete _settings_newgame.ai_config[c];
196  _settings_newgame.ai_config[c] = nullptr;
197  }
198  }
199  }
200 }
201 
202 /* static */ void AI::ResetConfig()
203 {
204  /* Check for both newgame as current game if we can reload the AIInfo inside
205  * the AIConfig. If not, remove the AI from the list (which will assign
206  * a random new AI on reload). */
207  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
208  if (_settings_game.ai_config[c] != nullptr && _settings_game.ai_config[c]->HasScript()) {
209  if (!_settings_game.ai_config[c]->ResetInfo(true)) {
210  DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
211  _settings_game.ai_config[c]->Change(nullptr);
212  if (Company::IsValidAiID(c)) {
213  /* The code belonging to an already running AI was deleted. We can only do
214  * one thing here to keep everything sane and that is kill the AI. After
215  * killing the offending AI we start a random other one in it's place, just
216  * like what would happen if the AI was missing during loading. */
217  AI::Stop(c);
218  AI::StartNew(c, false);
219  }
220  } else if (Company::IsValidAiID(c)) {
221  /* Update the reference in the Company struct. */
222  Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
223  }
224  }
225  if (_settings_newgame.ai_config[c] != nullptr && _settings_newgame.ai_config[c]->HasScript()) {
226  if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
227  DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
228  _settings_newgame.ai_config[c]->Change(nullptr);
229  }
230  }
231  }
232 }
233 
234 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
235 {
236  /* AddRef() and Release() need to be called at least once, so do it here */
237  event->AddRef();
238 
239  /* Clients should ignore events */
240  if (_networking && !_network_server) {
241  event->Release();
242  return;
243  }
244 
245  /* Only AIs can have an event-queue */
246  if (!Company::IsValidAiID(company)) {
247  event->Release();
248  return;
249  }
250 
251  /* Queue the event */
252  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
253  Company::Get(_current_company)->ai_instance->InsertEvent(event);
254  cur_company.Restore();
255 
256  event->Release();
257 }
258 
259 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
260 {
261  /* AddRef() and Release() need to be called at least once, so do it here */
262  event->AddRef();
263 
264  /* Clients should ignore events */
265  if (_networking && !_network_server) {
266  event->Release();
267  return;
268  }
269 
270  /* Try to send the event to all AIs */
271  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
272  if (c != skip_company) AI::NewEvent(c, event);
273  }
274 
275  event->Release();
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 && c->ai_instance != nullptr);
283 
284  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
285  c->ai_instance->Save();
286  cur_company.Restore();
287  } else {
289  }
290 }
291 
292 /* static */ void AI::Load(CompanyID company, int version)
293 {
294  if (!_networking || _network_server) {
295  Company *c = Company::GetIfValid(company);
296  assert(c != nullptr && c->ai_instance != nullptr);
297 
298  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
299  c->ai_instance->Load(version);
300  cur_company.Restore();
301  } else {
302  /* Read, but ignore, the load data */
304  }
305 }
306 
307 /* static */ int AI::GetStartNextTime()
308 {
309  /* Find the first company which doesn't exist yet */
310  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
312  }
313 
314  /* Currently no AI can be started, check again in a year. */
315  return DAYS_IN_YEAR;
316 }
317 
318 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
319 {
320  return AI::scanner_info->GetConsoleList(p, last, newest_only);
321 }
322 
323 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
324 {
325  return AI::scanner_library->GetConsoleList(p, last, true);
326 }
327 
328 /* static */ const ScriptInfoList *AI::GetInfoList()
329 {
330  return AI::scanner_info->GetInfoList();
331 }
332 
334 {
335  return AI::scanner_info->GetUniqueInfoList();
336 }
337 
338 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
339 {
340  return AI::scanner_info->FindInfo(name, version, force_exact_match);
341 }
342 
343 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
344 {
345  return AI::scanner_library->FindLibrary(library, version);
346 }
347 
348 /* static */ void AI::Rescan()
349 {
351 
352  AI::scanner_info->RescanDir();
353  AI::scanner_library->RescanDir();
354  ResetConfig();
355 
359 }
360 
367 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
368 {
369  return AI::scanner_info->HasScript(ci, md5sum);
370 }
371 
372 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
373 {
374  return AI::scanner_library->HasScript(ci, md5sum);
375 }
376 
378 {
379  return AI::scanner_info;
380 }
381 
383 {
384  return AI::scanner_library;
385 }
386 
AISettings ai
what may the AI do?
Owner
Enum for all companies/owners.
Definition: company_type.h:18
const ScriptInfoList * GetUniqueInfoList()
Get the list of the latest version of all registered scripts.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:79
static void StartNew(CompanyID company, bool rerandomise_ai=true)
Start a new AI company.
Definition: ai_core.cpp:36
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:302
bool _networking
are we in networking mode?
Definition: network.cpp:52
static const int DAYS_IN_YEAR
days per year
Definition: date_type.h:29
static const ScriptInfoList * GetInfoList()
Wrapper function for AIScanner::GetAIInfoList.
Definition: ai_core.cpp:328
int version
Version of the script.
static void Unpause(CompanyID company)
Resume execution of the AI.
Definition: ai_core.cpp:133
Runtime information about an AI like a pointer to the squirrel vm and the current state...
Definition: ai_instance.hpp:16
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:291
const char * GetName() const
Get the Name of the script.
Definition: script_info.hpp:57
static char * GetConsoleList(char *p, const char *last, bool newest_only=false)
Wrapper function for AIScanner::GetAIConsoleList.
Definition: ai_core.cpp:318
PerformanceElement
Elements of game performance that can be measured.
std::map< const char *, class ScriptInfo *, StringCompare > ScriptInfoList
A list that maps AI names to their AIInfo object.
Definition: ai.hpp:19
static bool IsPaused(CompanyID company)
Checks if the AI is paused.
Definition: ai_core.cpp:141
static void Pause(CompanyID company)
Suspend the AI and then pause execution of the script.
Definition: ai_core.cpp:120
const ScriptInfoList * GetInfoList()
Get the list of all registered scripts.
static AIScannerLibrary * GetScannerLibrary()
Gets the ScriptScanner instance that is used to find AI Libraries.
Definition: ai_core.cpp:382
static void Initialize()
Initialize the AI system.
Definition: ai_core.cpp:161
void Change(const char *name, int version=-1, bool force_exact_match=false, bool is_random=false)
Set another Script to be loaded in this slot.
void Change(const U &new_value)
Change the value of the variable.
Definition: backup_type.hpp:84
DifficultySettings difficulty
settings related to the difficulty
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=MAX_COMPANIES)
Broadcast a new event to all active AIs.
Definition: ai_core.cpp:259
static int GetStartNextTime()
Get the number of days before the next AI should start.
Definition: ai_core.cpp:307
The AIInstance tracks an AI.
AI debug window; Window numbers:
Definition: window_type.h:656
RAII class for measuring simple elements of performance.
static class AIInfo * FindInfo(const char *name, int version, bool force_exact_match)
Wrapper function for AIScanner::FindInfo.
Definition: ai_core.cpp:338
void Save()
Call the script Save function and save all data in the savegame.
declarations of the class for AI scanner
const char * GetName() const
Get the name of the Script.
bool _network_dedicated
are we a dedicated server?
Definition: network.cpp:55
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:21
bool ResetInfo(bool force_exact_match)
When ever the AI Scanner is reloaded, all infos become invalid.
Definition: ai_config.cpp:67
bool IsRandom() const
Is the current Script a randomly chosen Script?
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:3334
bool HasScript(const struct ContentInfo *ci, bool md5sum)
Check whether we have a script with the exact characteristics as ci.
static void Save(CompanyID company)
Save data from an AI to a savegame.
Definition: ai_core.cpp:278
static uint GetTick()
Get the current AI tick.
Definition: ai_core.cpp:97
static void ResetConfig()
Reset all AIConfigs, and make them reload their AIInfo.
Definition: ai_core.cpp:202
static void GameLoop()
Called every game-tick to let AIs do something.
Definition: ai_core.cpp:67
static size_t GetPoolSize()
Returns first unused index.
Definition: pool_type.hpp:312
static bool IsValidAiID(size_t index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Definition: company_base.h:138
static void Uninitialize(bool keepConfig)
Uninitialize the AI system.
Definition: ai_core.cpp:175
bool HasScript() const
Is this config attached to an Script? In other words, is there a Script that is assigned to this slot...
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition: settings.cpp:80
All static information from an AI library like name, version, etc.
Definition: ai_info.hpp:58
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=SSS_DEFAULT)
Get the config of a company.
Definition: ai_config.cpp:45
class AIInfo * FindInfo(const char *nameParam, int versionParam, bool force_exact_match)
Check if we have an AI by name and version available in our list.
Definition: ai_scanner.cpp:95
void Initialize(class AIInfo *info)
Initialize the AI and prepare it for its first run.
Definition: ai_instance.cpp:90
static void Load(CompanyID company, int version)
Load data for an AI from a savegame.
Definition: ai_core.cpp:292
int GetSetting(const char *name) const override
Get the value of a setting for this config.
Definition: ai_config.cpp:89
static void Stop(CompanyID company)
Stop a company to be controlled by an AI.
Definition: ai_core.cpp:102
void RescanDir()
Rescan the script dir.
static bool HasAI(const struct ContentInfo *ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition: ai_core.cpp:367
AI execution for player slot 1.
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:608
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition: ai_core.cpp:234
const char * name
Full name of the script.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
Delete a window by its class and window number (if it is open).
Definition: window.cpp:1162
static void SaveEmpty()
Don&#39;t save any data in the savegame.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:340
All static information from an AI like name, version, etc.
Definition: ai_info.hpp:16
class AILibrary * FindLibrary(const char *library, int version)
Find a library in the pool.
Definition: ai_scanner.cpp:159
static class AIScannerLibrary * scanner_library
ScriptScanner instance that is used to find AI Libraries.
Definition: ai.hpp:171
First company, same as owner.
Definition: company_type.h:22
static uint frame_counter
Tick counter for the AI code.
Definition: ai.hpp:169
static void KillAll()
Kill any and all AIs we manage.
Definition: ai_core.cpp:151
static void Rescan()
Rescans all searchpaths for available AIs.
Definition: ai_core.cpp:348
static class AILibrary * FindLibrary(const char *library, int version)
Wrapper function for AIScanner::FindLibrary.
Definition: ai_core.cpp:343
void AnchorUnchangeableSettings()
As long as the default of a setting has not been changed, the value of the setting is not stored...
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
char * GetConsoleList(char *p, const char *last, bool newest_only) const
Get the list of registered scripts to print on the console.
static const ScriptInfoList * GetUniqueInfoList()
Wrapper function for AIScanner::GetUniqueAIInfoList.
Definition: ai_core.cpp:333
Maximum number of companies.
Definition: company_type.h:23
Scan for AIs and its libraries.
Definition: fileio_func.h:100
bool _network_server
network-server is active
Definition: network.cpp:53
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:45
AI list; Window numbers:
Definition: window_type.h:277
class AIConfig * ai_config[MAX_COMPANIES]
settings per company
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:280
static char * GetConsoleLibraryList(char *p, const char *last)
Wrapper function for AIScanner::GetAIConsoleLibraryList.
Definition: ai_core.cpp:323
bool ai_in_multiplayer
so we allow AIs in multiplayer
AI settings; Window numbers:
Definition: window_type.h:168
byte competitor_speed
the speed at which the AI builds
Definition: settings_type.h:60
static bool CanStartNew()
Is it possible to start a new AI company?
Definition: ai_core.cpp:30
void Restore()
Restore the variable.
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, ...
static void LoadEmpty()
Load and discard data from a savegame.
void Load(int version)
Load data from a savegame and store it on the stack.
class AIInfo * SelectRandomAI() const
Select a random AI.
Definition: ai_scanner.cpp:61
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition: window.cpp:3243
static class AIScannerInfo * scanner_info
ScriptScanner instance that is used to find AIs.
Definition: ai.hpp:170
static AIScannerInfo * GetScannerInfo()
Gets the ScriptScanner instance that is used to find AIs.
Definition: ai_core.cpp:377
Get the Script config from the current game.
Container for all important information about a piece of content.
Definition: tcp_content.h:54
static void SetInactive(PerformanceElement elem)
Mark a performance element as not currently in use.
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:3316