OpenTTD
ai_core.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../core/backup_type.hpp"
14 #include "../core/bitmath_func.hpp"
15 #include "../company_base.h"
16 #include "../company_func.h"
17 #include "../network/network.h"
18 #include "../window_func.h"
19 #include "../framerate_type.h"
20 #include "ai_scanner.hpp"
21 #include "ai_instance.hpp"
22 #include "ai_config.hpp"
23 #include "ai_info.hpp"
24 #include "ai.hpp"
25 
26 #include "../safeguards.h"
27 
28 /* static */ uint AI::frame_counter = 0;
29 /* static */ AIScannerInfo *AI::scanner_info = nullptr;
30 /* static */ AIScannerLibrary *AI::scanner_library = nullptr;
31 
32 /* static */ bool AI::CanStartNew()
33 {
34  /* Only allow new AIs on the server and only when that is allowed in multiplayer */
36 }
37 
38 /* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai)
39 {
40  assert(Company::IsValidID(company));
41 
42  /* Clients shouldn't start AIs */
43  if (_networking && !_network_server) return;
44 
46  AIInfo *info = config->GetInfo();
47  if (info == nullptr || (rerandomise_ai && config->IsRandom())) {
48  info = AI::scanner_info->SelectRandomAI();
49  assert(info != nullptr);
50  /* Load default data and store the name in the settings */
51  config->Change(info->GetName(), -1, false, true);
52  }
54 
55  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
56  Company *c = Company::Get(company);
57 
58  c->ai_info = info;
59  assert(c->ai_instance == nullptr);
60  c->ai_instance = new AIInstance();
61  c->ai_instance->Initialize(info);
62 
63  cur_company.Restore();
64 
66  return;
67 }
68 
69 /* static */ void AI::GameLoop()
70 {
71  /* If we are in networking, only servers run this function, and that only if it is allowed */
73 
74  /* The speed with which AIs go, is limited by the 'competitor_speed' */
77  if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
78 
79  Backup<CompanyID> cur_company(_current_company, FILE_LINE);
80  const Company *c;
81  FOR_ALL_COMPANIES(c) {
82  if (c->is_ai) {
84  cur_company.Change(c->index);
85  c->ai_instance->GameLoop();
86  } else {
88  }
89  }
90  cur_company.Restore();
91 
92  /* Occasionally collect garbage; every 255 ticks do one company.
93  * Effectively collecting garbage once every two months per AI. */
94  if ((AI::frame_counter & 255) == 0) {
96  if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
97  }
98 }
99 
100 /* static */ uint AI::GetTick()
101 {
102  return AI::frame_counter;
103 }
104 
105 /* static */ void AI::Stop(CompanyID company)
106 {
107  if (_networking && !_network_server) return;
109 
110  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
111  Company *c = Company::Get(company);
112 
113  delete c->ai_instance;
114  c->ai_instance = nullptr;
115  c->ai_info = nullptr;
116 
117  cur_company.Restore();
118 
121 }
122 
123 /* static */ void AI::Pause(CompanyID company)
124 {
125  /* The reason why dedicated servers are forbidden to execute this
126  * command is not because it is unsafe, but because there is no way
127  * for the server owner to unpause the script again. */
128  if (_network_dedicated) return;
129 
130  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
131  Company::Get(company)->ai_instance->Pause();
132 
133  cur_company.Restore();
134 }
135 
136 /* static */ void AI::Unpause(CompanyID company)
137 {
138  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
139  Company::Get(company)->ai_instance->Unpause();
140 
141  cur_company.Restore();
142 }
143 
144 /* static */ bool AI::IsPaused(CompanyID company)
145 {
146  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
147  bool paused = Company::Get(company)->ai_instance->IsPaused();
148 
149  cur_company.Restore();
150 
151  return paused;
152 }
153 
154 /* static */ void AI::KillAll()
155 {
156  /* It might happen there are no companies .. than we have nothing to loop */
157  if (Company::GetPoolSize() == 0) return;
158 
159  const Company *c;
160  FOR_ALL_COMPANIES(c) {
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) {
172  AI::scanner_info = new AIScannerInfo();
173  AI::scanner_info->Initialize();
174  AI::scanner_library = new AIScannerLibrary();
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 '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
215  _settings_game.ai_config[c]->Change(nullptr);
216  if (Company::IsValidAiID(c)) {
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, false);
223  }
224  } else if (Company::IsValidAiID(c)) {
225  /* Update the reference in the Company struct. */
226  Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
227  }
228  }
229  if (_settings_newgame.ai_config[c] != nullptr && _settings_newgame.ai_config[c]->HasScript()) {
230  if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
231  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());
232  _settings_newgame.ai_config[c]->Change(nullptr);
233  }
234  }
235  }
236 }
237 
238 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
239 {
240  /* AddRef() and Release() need to be called at least once, so do it here */
241  event->AddRef();
242 
243  /* Clients should ignore events */
244  if (_networking && !_network_server) {
245  event->Release();
246  return;
247  }
248 
249  /* Only AIs can have an event-queue */
250  if (!Company::IsValidAiID(company)) {
251  event->Release();
252  return;
253  }
254 
255  /* Queue the event */
256  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
257  Company::Get(_current_company)->ai_instance->InsertEvent(event);
258  cur_company.Restore();
259 
260  event->Release();
261 }
262 
263 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
264 {
265  /* AddRef() and Release() need to be called at least once, so do it here */
266  event->AddRef();
267 
268  /* Clients should ignore events */
269  if (_networking && !_network_server) {
270  event->Release();
271  return;
272  }
273 
274  /* Try to send the event to all AIs */
275  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
276  if (c != skip_company) AI::NewEvent(c, event);
277  }
278 
279  event->Release();
280 }
281 
282 /* static */ void AI::Save(CompanyID company)
283 {
284  if (!_networking || _network_server) {
285  Company *c = Company::GetIfValid(company);
286  assert(c != nullptr && c->ai_instance != nullptr);
287 
288  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
289  c->ai_instance->Save();
290  cur_company.Restore();
291  } else {
293  }
294 }
295 
296 /* static */ void AI::Load(CompanyID company, int version)
297 {
298  if (!_networking || _network_server) {
299  Company *c = Company::GetIfValid(company);
300  assert(c != nullptr && c->ai_instance != nullptr);
301 
302  Backup<CompanyID> cur_company(_current_company, company, FILE_LINE);
303  c->ai_instance->Load(version);
304  cur_company.Restore();
305  } else {
306  /* Read, but ignore, the load data */
308  }
309 }
310 
311 /* static */ int AI::GetStartNextTime()
312 {
313  /* Find the first company which doesn't exist yet */
314  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
316  }
317 
318  /* Currently no AI can be started, check again in a year. */
319  return DAYS_IN_YEAR;
320 }
321 
322 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
323 {
324  return AI::scanner_info->GetConsoleList(p, last, newest_only);
325 }
326 
327 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
328 {
329  return AI::scanner_library->GetConsoleList(p, last, true);
330 }
331 
332 /* static */ const ScriptInfoList *AI::GetInfoList()
333 {
334  return AI::scanner_info->GetInfoList();
335 }
336 
338 {
339  return AI::scanner_info->GetUniqueInfoList();
340 }
341 
342 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
343 {
344  return AI::scanner_info->FindInfo(name, version, force_exact_match);
345 }
346 
347 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
348 {
349  return AI::scanner_library->FindLibrary(library, version);
350 }
351 
352 /* static */ void AI::Rescan()
353 {
355 
356  AI::scanner_info->RescanDir();
357  AI::scanner_library->RescanDir();
358  ResetConfig();
359 
363 }
364 
371 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
372 {
373  return AI::scanner_info->HasScript(ci, md5sum);
374 }
375 
376 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
377 {
378  return AI::scanner_library->HasScript(ci, md5sum);
379 }
380 
382 {
383  return AI::scanner_info;
384 }
385 
387 {
388  return AI::scanner_library;
389 }
390 
AISettings ai
what may the AI do?
Owner
Enum for all companies/owners.
Definition: company_type.h:20
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:81
static void StartNew(CompanyID company, bool rerandomise_ai=true)
Start a new AI company.
Definition: ai_core.cpp:38
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:257
bool _networking
are we in networking mode?
Definition: network.cpp:54
static const int DAYS_IN_YEAR
days per year
Definition: date_type.h:31
static const ScriptInfoList * GetInfoList()
Wrapper function for AIScanner::GetAIInfoList.
Definition: ai_core.cpp:332
int version
Version of the script.
static void Unpause(CompanyID company)
Resume execution of the AI.
Definition: ai_core.cpp:136
Runtime information about an AI like a pointer to the squirrel vm and the current state...
Definition: ai_instance.hpp:18
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:246
const char * GetName() const
Get the Name of the script.
Definition: script_info.hpp:59
static char * GetConsoleList(char *p, const char *last, bool newest_only=false)
Wrapper function for AIScanner::GetAIConsoleList.
Definition: ai_core.cpp:322
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:21
static bool IsPaused(CompanyID company)
Checks if the AI is paused.
Definition: ai_core.cpp:144
static void Pause(CompanyID company)
Suspend the AI and then pause execution of the script.
Definition: ai_core.cpp:123
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:386
static void Initialize()
Initialize the AI system.
Definition: ai_core.cpp:165
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:86
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:263
Tindex index
Index of this pool item.
Definition: pool_type.hpp:147
static int GetStartNextTime()
Get the number of days before the next AI should start.
Definition: ai_core.cpp:311
The AIInstance tracks an AI.
AI debug window; Window numbers:
Definition: window_type.h:658
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:342
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:57
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:23
bool ResetInfo(bool force_exact_match)
When ever the AI Scanner is reloaded, all infos become invalid.
Definition: ai_config.cpp:69
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:3318
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:282
static uint GetTick()
Get the current AI tick.
Definition: ai_core.cpp:100
static void ResetConfig()
Reset all AIConfigs, and make them reload their AIInfo.
Definition: ai_core.cpp:206
static void GameLoop()
Called every game-tick to let AIs do something.
Definition: ai_core.cpp:69
static size_t GetPoolSize()
Returns first unused index.
Definition: pool_type.hpp:267
static bool IsValidAiID(size_t index)
Is this company a valid company, controlled by the computer (a NoAI program)?
Definition: company_base.h:140
static void Uninitialize(bool keepConfig)
Uninitialize the AI system.
Definition: ai_core.cpp:179
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:82
All static information from an AI library like name, version, etc.
Definition: ai_info.hpp:60
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=SSS_DEFAULT)
Get the config of a company.
Definition: ai_config.cpp:47
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:97
void Initialize(class AIInfo *info)
Initialize the AI and prepare it for its first run.
Definition: ai_instance.cpp:92
bool is_ai
If true, the company is (also) controlled by the computer (a NoAI program).
Definition: company_base.h:95
static void Load(CompanyID company, int version)
Load data for an AI from a savegame.
Definition: ai_core.cpp:296
int GetSetting(const char *name) const override
Get the value of a setting for this config.
Definition: ai_config.cpp:91
static void Stop(CompanyID company)
Stop a company to be controlled by an AI.
Definition: ai_core.cpp:105
void RescanDir()
Rescan the script dir.
static bool HasAI(const struct ContentInfo *ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition: ai_core.cpp:371
AI execution for player slot 1.
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:610
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition: ai_core.cpp:238
const char * name
Full name of the script.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:37
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
Delete a window by its class and window number (if it is open).
Definition: window.cpp:1146
static void SaveEmpty()
Don&#39;t save any data in the savegame.
All static information from an AI like name, version, etc.
Definition: ai_info.hpp:18
class AILibrary * FindLibrary(const char *library, int version)
Find a library in the pool.
Definition: ai_scanner.cpp:161
static class AIScannerLibrary * scanner_library
ScriptScanner instance that is used to find AI Libraries.
Definition: ai.hpp:173
First company, same as owner.
Definition: company_type.h:24
static uint frame_counter
Tick counter for the AI code.
Definition: ai.hpp:171
static void KillAll()
Kill any and all AIs we manage.
Definition: ai_core.cpp:154
static void Rescan()
Rescans all searchpaths for available AIs.
Definition: ai_core.cpp:352
static class AILibrary * FindLibrary(const char *library, int version)
Wrapper function for AIScanner::FindLibrary.
Definition: ai_core.cpp:347
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:337
Maximum number of companies.
Definition: company_type.h:25
Scan for AIs and its libraries.
Definition: fileio_func.h:102
bool _network_server
network-server is active
Definition: network.cpp:55
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:47
AI list; Window numbers:
Definition: window_type.h:279
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:235
static char * GetConsoleLibraryList(char *p, const char *last)
Wrapper function for AIScanner::GetAIConsoleLibraryList.
Definition: ai_core.cpp:327
bool ai_in_multiplayer
so we allow AIs in multiplayer
AI settings; Window numbers:
Definition: window_type.h:170
byte competitor_speed
the speed at which the AI builds
Definition: settings_type.h:62
static bool CanStartNew()
Is it possible to start a new AI company?
Definition: ai_core.cpp:32
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:63
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition: window.cpp:3227
static class AIScannerInfo * scanner_info
ScriptScanner instance that is used to find AIs.
Definition: ai.hpp:172
static AIScannerInfo * GetScannerInfo()
Gets the ScriptScanner instance that is used to find AIs.
Definition: ai_core.cpp:381
Get the Script config from the current game.
Container for all important information about a piece of content.
Definition: tcp_content.h:56
static void SetInactive(PerformanceElement elem)
Mark a performance element as not currently in use.
void GameLoop()
Run the GameLoop of a script.
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:3300