OpenTTD
ai_core.cpp
Go to the documentation of this file.
1 /* $Id: ai_core.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
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 "ai_scanner.hpp"
20 #include "ai_instance.hpp"
21 #include "ai_config.hpp"
22 #include "ai_info.hpp"
23 #include "ai.hpp"
24 
25 #include "../safeguards.h"
26 
27 /* static */ uint AI::frame_counter = 0;
28 /* static */ AIScannerInfo *AI::scanner_info = NULL;
29 /* static */ AIScannerLibrary *AI::scanner_library = NULL;
30 
31 /* static */ bool AI::CanStartNew()
32 {
33  /* Only allow new AIs on the server and only when that is allowed in multiplayer */
35 }
36 
37 /* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai)
38 {
39  assert(Company::IsValidID(company));
40 
41  /* Clients shouldn't start AIs */
42  if (_networking && !_network_server) return;
43 
45  AIInfo *info = config->GetInfo();
46  if (info == NULL || (rerandomise_ai && config->IsRandom())) {
47  info = AI::scanner_info->SelectRandomAI();
48  assert(info != NULL);
49  /* Load default data and store the name in the settings */
50  config->Change(info->GetName(), -1, false, true);
51  }
53 
54  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
55  Company *c = Company::Get(company);
56 
57  c->ai_info = info;
58  assert(c->ai_instance == NULL);
59  c->ai_instance = new AIInstance();
60  c->ai_instance->Initialize(info);
61 
62  cur_company.Restore();
63 
65  return;
66 }
67 
68 /* static */ void AI::GameLoop()
69 {
70  /* If we are in networking, only servers run this function, and that only if it is allowed */
72 
73  /* The speed with which AIs go, is limited by the 'competitor_speed' */
76  if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
77 
78  Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
79  const Company *c;
80  FOR_ALL_COMPANIES(c) {
81  if (c->is_ai) {
82  cur_company.Change(c->index);
83  c->ai_instance->GameLoop();
84  }
85  }
86  cur_company.Restore();
87 
88  /* Occasionally collect garbage; every 255 ticks do one company.
89  * Effectively collecting garbage once every two months per AI. */
90  if ((AI::frame_counter & 255) == 0) {
92  if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
93  }
94 }
95 
96 /* static */ uint AI::GetTick()
97 {
98  return AI::frame_counter;
99 }
100 
101 /* static */ void AI::Stop(CompanyID company)
102 {
103  if (_networking && !_network_server) return;
104 
105  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
106  Company *c = Company::Get(company);
107 
108  delete c->ai_instance;
109  c->ai_instance = NULL;
110  c->ai_info = NULL;
111 
112  cur_company.Restore();
113 
116 }
117 
118 /* static */ void AI::Pause(CompanyID company)
119 {
120  /* The reason why dedicated servers are forbidden to execute this
121  * command is not because it is unsafe, but because there is no way
122  * for the server owner to unpause the script again. */
123  if (_network_dedicated) return;
124 
125  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
126  Company::Get(company)->ai_instance->Pause();
127 
128  cur_company.Restore();
129 }
130 
131 /* static */ void AI::Unpause(CompanyID company)
132 {
133  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
134  Company::Get(company)->ai_instance->Unpause();
135 
136  cur_company.Restore();
137 }
138 
139 /* static */ bool AI::IsPaused(CompanyID company)
140 {
141  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
142  bool paused = Company::Get(company)->ai_instance->IsPaused();
143 
144  cur_company.Restore();
145 
146  return paused;
147 }
148 
149 /* static */ void AI::KillAll()
150 {
151  /* It might happen there are no companies .. than we have nothing to loop */
152  if (Company::GetPoolSize() == 0) return;
153 
154  const Company *c;
155  FOR_ALL_COMPANIES(c) {
156  if (c->is_ai) AI::Stop(c->index);
157  }
158 }
159 
160 /* static */ void AI::Initialize()
161 {
162  if (AI::scanner_info != NULL) AI::Uninitialize(true);
163 
164  AI::frame_counter = 0;
165  if (AI::scanner_info == NULL) {
167  AI::scanner_info = new AIScannerInfo();
168  AI::scanner_info->Initialize();
169  AI::scanner_library = new AIScannerLibrary();
170  AI::scanner_library->Initialize();
171  }
172 }
173 
174 /* static */ void AI::Uninitialize(bool keepConfig)
175 {
176  AI::KillAll();
177 
178  if (keepConfig) {
179  /* Run a rescan, which indexes all AIInfos again, and check if we can
180  * still load all the AIS, while keeping the configs in place */
181  Rescan();
182  } else {
183  delete AI::scanner_info;
184  delete AI::scanner_library;
185  AI::scanner_info = NULL;
186  AI::scanner_library = NULL;
187 
188  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
189  if (_settings_game.ai_config[c] != NULL) {
190  delete _settings_game.ai_config[c];
191  _settings_game.ai_config[c] = NULL;
192  }
193  if (_settings_newgame.ai_config[c] != NULL) {
194  delete _settings_newgame.ai_config[c];
195  _settings_newgame.ai_config[c] = NULL;
196  }
197  }
198  }
199 }
200 
201 /* static */ void AI::ResetConfig()
202 {
203  /* Check for both newgame as current game if we can reload the AIInfo inside
204  * the AIConfig. If not, remove the AI from the list (which will assign
205  * a random new AI on reload). */
206  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
207  if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasScript()) {
208  if (!_settings_game.ai_config[c]->ResetInfo(true)) {
209  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());
210  _settings_game.ai_config[c]->Change(NULL);
211  if (Company::IsValidAiID(c)) {
212  /* The code belonging to an already running AI was deleted. We can only do
213  * one thing here to keep everything sane and that is kill the AI. After
214  * killing the offending AI we start a random other one in it's place, just
215  * like what would happen if the AI was missing during loading. */
216  AI::Stop(c);
217  AI::StartNew(c, false);
218  }
219  } else if (Company::IsValidAiID(c)) {
220  /* Update the reference in the Company struct. */
221  Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
222  }
223  }
225  if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
226  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  }
229  }
230  }
231 }
232 
233 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
234 {
235  /* AddRef() and Release() need to be called at least once, so do it here */
236  event->AddRef();
237 
238  /* Clients should ignore events */
239  if (_networking && !_network_server) {
240  event->Release();
241  return;
242  }
243 
244  /* Only AIs can have an event-queue */
245  if (!Company::IsValidAiID(company)) {
246  event->Release();
247  return;
248  }
249 
250  /* Queue the event */
251  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
252  Company::Get(_current_company)->ai_instance->InsertEvent(event);
253  cur_company.Restore();
254 
255  event->Release();
256 }
257 
258 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
259 {
260  /* AddRef() and Release() need to be called at least once, so do it here */
261  event->AddRef();
262 
263  /* Clients should ignore events */
264  if (_networking && !_network_server) {
265  event->Release();
266  return;
267  }
268 
269  /* Try to send the event to all AIs */
270  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
271  if (c != skip_company) AI::NewEvent(c, event);
272  }
273 
274  event->Release();
275 }
276 
277 /* static */ void AI::Save(CompanyID company)
278 {
279  if (!_networking || _network_server) {
280  Company *c = Company::GetIfValid(company);
281  assert(c != NULL && c->ai_instance != NULL);
282 
283  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
284  c->ai_instance->Save();
285  cur_company.Restore();
286  } else {
288  }
289 }
290 
291 /* static */ void AI::Load(CompanyID company, int version)
292 {
293  if (!_networking || _network_server) {
294  Company *c = Company::GetIfValid(company);
295  assert(c != NULL && c->ai_instance != NULL);
296 
297  Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
298  c->ai_instance->Load(version);
299  cur_company.Restore();
300  } else {
301  /* Read, but ignore, the load data */
303  }
304 }
305 
306 /* static */ int AI::GetStartNextTime()
307 {
308  /* Find the first company which doesn't exist yet */
309  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
311  }
312 
313  /* Currently no AI can be started, check again in a year. */
314  return DAYS_IN_YEAR;
315 }
316 
317 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
318 {
319  return AI::scanner_info->GetConsoleList(p, last, newest_only);
320 }
321 
322 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
323 {
324  return AI::scanner_library->GetConsoleList(p, last, true);
325 }
326 
327 /* static */ const ScriptInfoList *AI::GetInfoList()
328 {
329  return AI::scanner_info->GetInfoList();
330 }
331 
333 {
334  return AI::scanner_info->GetUniqueInfoList();
335 }
336 
337 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
338 {
339  return AI::scanner_info->FindInfo(name, version, force_exact_match);
340 }
341 
342 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
343 {
344  return AI::scanner_library->FindLibrary(library, version);
345 }
346 
347 /* static */ void AI::Rescan()
348 {
350 
351  AI::scanner_info->RescanDir();
352  AI::scanner_library->RescanDir();
353  ResetConfig();
354 
358 }
359 
360 #if defined(ENABLE_NETWORK)
361 
368 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
369 {
370  return AI::scanner_info->HasScript(ci, md5sum);
371 }
372 
373 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
374 {
375  return AI::scanner_library->HasScript(ci, md5sum);
376 }
377 
378 #endif /* defined(ENABLE_NETWORK) */
379 
381 {
382  return AI::scanner_info;
383 }
384 
386 {
387  return AI::scanner_library;
388 }
389 
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:37
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:327
int version
Version of the script.
static void Unpause(CompanyID company)
Resume execution of the AI.
Definition: ai_core.cpp:131
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:317
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:139
static void Pause(CompanyID company)
Suspend the AI and then pause execution of the script.
Definition: ai_core.cpp:118
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:385
static void Initialize()
Initialize the AI system.
Definition: ai_core.cpp:160
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:258
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:306
The AIInstance tracks an AI.
AI debug window; Window numbers:
Definition: window_type.h:658
static class AIInfo * FindInfo(const char *name, int version, bool force_exact_match)
Wrapper function for AIScanner::FindInfo.
Definition: ai_core.cpp:337
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:60
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:3238
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:277
static uint GetTick()
Get the current AI tick.
Definition: ai_core.cpp:96
static void ResetConfig()
Reset all AIConfigs, and make them reload their AIInfo.
Definition: ai_core.cpp:201
static void GameLoop()
Called every game-tick to let AIs do something.
Definition: ai_core.cpp:68
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:132
int GetSetting(const char *name) const
Get the value of a setting for this config.
Definition: ai_config.cpp:82
static void Uninitialize(bool keepConfig)
Uninitialize the AI system.
Definition: ai_core.cpp:174
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:38
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:94
static void Load(CompanyID company, int version)
Load data for an AI from a savegame.
Definition: ai_core.cpp:291
static void Stop(CompanyID company)
Stop a company to be controlled by an AI.
Definition: ai_core.cpp:101
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:368
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:646
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition: ai_core.cpp:233
const char * name
Full name of the script.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:39
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
Delete a window by its class and window number (if it is open).
Definition: window.cpp:1137
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:149
static void Rescan()
Rescans all searchpaths for available AIs.
Definition: ai_core.cpp:347
static class AILibrary * FindLibrary(const char *library, int version)
Wrapper function for AIScanner::FindLibrary.
Definition: ai_core.cpp:342
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:332
Scan for AIs and its libraries.
Definition: fileio_func.h:103
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:322
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:31
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:3148
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:380
Get the Script config from the current game.
Container for all important information about a piece of content.
Definition: tcp_content.h:58
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:3220