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/>.
8 /** @file ai_core.cpp Implementation of AI. */
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"
24 #include "../safeguards.h"
26 /* static */ uint
AI::frame_counter
= 0;
27 /* static */ AIScannerInfo
*AI::scanner_info
= nullptr;
28 /* static */ AIScannerLibrary
*AI::scanner_library
= nullptr;
30 /* static */ bool AI::CanStartNew()
32 /* Only allow new AIs on the server and only when that is allowed in multiplayer */
33 return !_networking
|| (_network_server
&& _settings_game
.ai
.ai_in_multiplayer
);
36 /* static */ void AI::StartNew(CompanyID company
, bool rerandomise_ai
)
38 assert(Company::IsValidID(company
));
40 /* Clients shouldn't start AIs */
41 if (_networking
&& !_network_server
) return;
43 AIConfig
*config
= AIConfig::GetConfig(company
, AIConfig::SSS_FORCE_GAME
);
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);
51 config
->AnchorUnchangeableSettings();
53 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
54 Company
*c
= Company::Get(company
);
57 assert(c
->ai_instance
== nullptr);
58 c
->ai_instance
= new AIInstance();
59 c
->ai_instance
->Initialize(info
);
61 cur_company
.Restore();
63 InvalidateWindowData(WC_AI_DEBUG
, 0, -1);
67 /* static */ void AI::GameLoop()
69 /* If we are in networking, only servers run this function, and that only if it is allowed */
70 if (_networking
&& (!_network_server
|| !_settings_game
.ai
.ai_in_multiplayer
)) return;
72 /* The speed with which AIs go, is limited by the 'competitor_speed' */
74 assert(_settings_game
.difficulty
.competitor_speed
<= 4);
75 if ((AI::frame_counter
& ((1 << (4 - _settings_game
.difficulty
.competitor_speed
)) - 1)) != 0) return;
77 Backup
<CompanyID
> cur_company(_current_company
, FILE_LINE
);
78 for (const Company
*c
: Company::Iterate()) {
80 PerformanceMeasurer
framerate((PerformanceElement
)(PFE_AI0
+ c
->index
));
81 cur_company
.Change(c
->index
);
82 c
->ai_instance
->GameLoop();
84 PerformanceMeasurer::SetInactive((PerformanceElement
)(PFE_AI0
+ c
->index
));
87 cur_company
.Restore();
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) {
92 CompanyID cid
= (CompanyID
)GB(AI::frame_counter
, 8, 4);
93 if (Company::IsValidAiID(cid
)) Company::Get(cid
)->ai_instance
->CollectGarbage();
97 /* static */ uint
AI::GetTick()
99 return AI::frame_counter
;
102 /* static */ void AI::Stop(CompanyID company
)
104 if (_networking
&& !_network_server
) return;
105 PerformanceMeasurer::SetInactive((PerformanceElement
)(PFE_AI0
+ company
));
107 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
108 Company
*c
= Company::Get(company
);
110 delete c
->ai_instance
;
111 c
->ai_instance
= nullptr;
112 c
->ai_info
= nullptr;
114 cur_company
.Restore();
116 InvalidateWindowData(WC_AI_DEBUG
, 0, -1);
117 CloseWindowById(WC_AI_SETTINGS
, company
);
120 /* static */ void AI::Pause(CompanyID company
)
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;
127 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
128 Company::Get(company
)->ai_instance
->Pause();
130 cur_company
.Restore();
133 /* static */ void AI::Unpause(CompanyID company
)
135 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
136 Company::Get(company
)->ai_instance
->Unpause();
138 cur_company
.Restore();
141 /* static */ bool AI::IsPaused(CompanyID company
)
143 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
144 bool paused
= Company::Get(company
)->ai_instance
->IsPaused();
146 cur_company
.Restore();
151 /* static */ void AI::KillAll()
153 /* It might happen there are no companies .. than we have nothing to loop */
154 if (Company::GetPoolSize() == 0) return;
156 for (const Company
*c
: Company::Iterate()) {
157 if (c
->is_ai
) AI::Stop(c
->index
);
161 /* static */ void AI::Initialize()
163 if (AI::scanner_info
!= nullptr) AI::Uninitialize(true);
165 AI::frame_counter
= 0;
166 if (AI::scanner_info
== nullptr) {
167 TarScanner::DoScan(TarScanner::AI
);
168 AI::scanner_info
= new AIScannerInfo();
169 AI::scanner_info
->Initialize();
170 AI::scanner_library
= new AIScannerLibrary();
171 AI::scanner_library
->Initialize();
175 /* static */ void AI::Uninitialize(bool 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 */
184 delete AI::scanner_info
;
185 delete AI::scanner_library
;
186 AI::scanner_info
= nullptr;
187 AI::scanner_library
= nullptr;
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;
194 if (_settings_newgame
.ai_config
[c
] != nullptr) {
195 delete _settings_newgame
.ai_config
[c
];
196 _settings_newgame
.ai_config
[c
] = nullptr;
202 /* static */ void AI::ResetConfig()
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 '{}' 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. */
218 AI::StartNew(c
, false);
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();
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 '{}' was no longer found, and removed from the list.", _settings_newgame
.ai_config
[c
]->GetName());
228 _settings_newgame
.ai_config
[c
]->Change(nullptr);
234 /* static */ void AI::NewEvent(CompanyID company
, ScriptEvent
*event
)
236 /* AddRef() and Release() need to be called at least once, so do it here */
239 /* Clients should ignore events */
240 if (_networking
&& !_network_server
) {
245 /* Only AIs can have an event-queue */
246 if (!Company::IsValidAiID(company
)) {
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();
259 /* static */ void AI::BroadcastNewEvent(ScriptEvent
*event
, CompanyID skip_company
)
261 /* AddRef() and Release() need to be called at least once, so do it here */
264 /* Clients should ignore events */
265 if (_networking
&& !_network_server
) {
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
);
278 /* static */ void AI::Save(CompanyID company
)
280 if (!_networking
|| _network_server
) {
281 Company
*c
= Company::GetIfValid(company
);
282 assert(c
!= nullptr && c
->ai_instance
!= nullptr);
284 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
285 c
->ai_instance
->Save();
286 cur_company
.Restore();
288 AIInstance::SaveEmpty();
292 /* static */ void AI::Load(CompanyID company
, int version
)
294 if (!_networking
|| _network_server
) {
295 Company
*c
= Company::GetIfValid(company
);
296 assert(c
!= nullptr && c
->ai_instance
!= nullptr);
298 Backup
<CompanyID
> cur_company(_current_company
, company
, FILE_LINE
);
299 c
->ai_instance
->Load(version
);
300 cur_company
.Restore();
302 /* Read, but ignore, the load data */
303 AIInstance::LoadEmpty();
307 /* static */ int AI::GetStartNextTime()
309 /* Find the first company which doesn't exist yet */
310 for (CompanyID c
= COMPANY_FIRST
; c
< MAX_COMPANIES
; c
++) {
311 if (!Company::IsValidID(c
)) return AIConfig::GetConfig(c
, AIConfig::SSS_FORCE_GAME
)->GetSetting("start_date");
314 /* Currently no AI can be started, check again in a year. */
318 /* static */ char *AI::GetConsoleList(char *p
, const char *last
, bool newest_only
)
320 return AI::scanner_info
->GetConsoleList(p
, last
, newest_only
);
323 /* static */ char *AI::GetConsoleLibraryList(char *p
, const char *last
)
325 return AI::scanner_library
->GetConsoleList(p
, last
, true);
328 /* static */ const ScriptInfoList
*AI::GetInfoList()
330 return AI::scanner_info
->GetInfoList();
333 /* static */ const ScriptInfoList
*AI::GetUniqueInfoList()
335 return AI::scanner_info
->GetUniqueInfoList();
338 /* static */ AIInfo
*AI::FindInfo(const char *name
, int version
, bool force_exact_match
)
340 return AI::scanner_info
->FindInfo(name
, version
, force_exact_match
);
343 /* static */ AILibrary
*AI::FindLibrary(const char *library
, int version
)
345 return AI::scanner_library
->FindLibrary(library
, version
);
348 /* static */ void AI::Rescan()
350 TarScanner::DoScan(TarScanner::AI
);
352 AI::scanner_info
->RescanDir();
353 AI::scanner_library
->RescanDir();
356 InvalidateWindowData(WC_AI_LIST
, 0, 1);
357 SetWindowClassesDirty(WC_AI_DEBUG
);
358 InvalidateWindowClassesData(WC_AI_SETTINGS
);
362 * Check whether we have an AI (library) with the exact characteristics as ci.
363 * @param ci the characteristics to search on (shortname and md5sum)
364 * @param md5sum whether to check the MD5 checksum
365 * @return true iff we have an AI (library) matching.
367 /* static */ bool AI::HasAI(const ContentInfo
*ci
, bool md5sum
)
369 return AI::scanner_info
->HasScript(ci
, md5sum
);
372 /* static */ bool AI::HasAILibrary(const ContentInfo
*ci
, bool md5sum
)
374 return AI::scanner_library
->HasScript(ci
, md5sum
);
377 /* static */ AIScannerInfo
*AI::GetScannerInfo()
379 return AI::scanner_info
;
382 /* static */ AIScannerLibrary
*AI::GetScannerLibrary()
384 return AI::scanner_library
;