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 = NULL;
30 /* static */ AIScannerLibrary *AI::scanner_library = NULL;
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 == NULL || (rerandomise_ai && config->IsRandom())) {
48  info = AI::scanner_info->SelectRandomAI();
49  assert(info != NULL);
50  /* Load default data and store the name in the settings */
51  config->Change(info->GetName(), -1, false, true);
52  }
54 
55  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
56  Company *c = Company::Get(company);
57 
58  c->ai_info = info;
59  assert(c->ai_instance == NULL);
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<CompanyByte> 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<CompanyByte> cur_company(_current_company, company, FILE_LINE);
111  Company *c = Company::Get(company);
112 
113  delete c->ai_instance;
114  c->ai_instance = NULL;
115  c->ai_info = NULL;
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<CompanyByte> 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<CompanyByte> 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<CompanyByte> 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 != NULL) AI::Uninitialize(true);
168 
169  AI::frame_counter = 0;
170  if (AI::scanner_info == NULL) {
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 = NULL;
191  AI::scanner_library = NULL;
192 
193  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
194  if (_settings_game.ai_config[c] != NULL) {
195  delete _settings_game.ai_config[c];
196  _settings_game.ai_config[c] = NULL;
197  }
198  if (_settings_newgame.ai_config[c] != NULL) {
199  delete _settings_newgame.ai_config[c];
200  _settings_newgame.ai_config[c] = NULL;
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] != NULL && _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(NULL);
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  }
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());
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<CompanyByte> 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 != NULL && c->ai_instance != NULL);
287 
288  Backup<CompanyByte> 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 != NULL && c->ai_instance != NULL);
301 
302  Backup<CompanyByte> 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 
365 #if defined(ENABLE_NETWORK)
366 
373 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
374 {
375  return AI::scanner_info->HasScript(ci, md5sum);
376 }
377 
378 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
379 {
380  return AI::scanner_library->HasScript(ci, md5sum);
381 }
382 
383 #endif /* defined(ENABLE_NETWORK) */
384 
386 {
387  return AI::scanner_info;
388 }
389 
391 {
392  return AI::scanner_library;
393 }
394 
AISettings ai
what may the AI do?
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:77
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:56
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:390
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:59
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:3319
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:137
int GetSetting(const char *name) const
Get the value of a setting for this config.
Definition: ai_config.cpp:91
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:78
First company, same as owner.
Definition: company_type.h:24
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:95
void Initialize(class AIInfo *info)
Initialize the AI and prepare it for its first run.
Definition: ai_instance.cpp:91
bool is_ai
If true, the company is (also) controlled by the computer (a NoAI program).
Definition: company_base.h:92
static void Load(CompanyID company, int version)
Load data for an AI from a savegame.
Definition: ai_core.cpp:296
static void Stop(CompanyID company)
Stop a company to be controlled by an AI.
Definition: ai_core.cpp:105
Maximum number of companies.
Definition: company_type.h:25
void RescanDir()
Rescan the script dir.
static bool HasAI(const struct ContentInfo *ci, bool md5sum)
Wrapper function for AIScanner::HasAI.
Definition: ai_core.cpp:373
AI execution for player slot 1.
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:637
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:36
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
CompanyByte _current_company
Company currently doing an action.
Definition: company_cmd.cpp:47
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:175
static uint frame_counter
Tick counter for the AI code.
Definition: ai.hpp:173
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
Scan for AIs and its libraries.
Definition: fileio_func.h:104
bool _network_server
network-server is active
Definition: network.cpp:57
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-NULL) 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.
Owner
Enum for all companies/owners.
Definition: company_type.h:20
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:3229
static class AIScannerInfo * scanner_info
ScriptScanner instance that is used to find AIs.
Definition: ai.hpp:174
static AIScannerInfo * GetScannerInfo()
Gets the ScriptScanner instance that is used to find AIs.
Definition: ai_core.cpp:385
Get the Script config from the current game.
Container for all important information about a piece of content.
Definition: tcp_content.h:58
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:3301