ai_core.cpp

Go to the documentation of this file.
00001 /* $Id: ai_core.cpp 24537 2012-09-21 19:58:18Z zuu $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "../stdafx.h"
00013 #include "../core/backup_type.hpp"
00014 #include "../core/bitmath_func.hpp"
00015 #include "../company_base.h"
00016 #include "../company_func.h"
00017 #include "../network/network.h"
00018 #include "../window_func.h"
00019 #include "ai_scanner.hpp"
00020 #include "ai_instance.hpp"
00021 #include "ai_config.hpp"
00022 #include "ai_info.hpp"
00023 #include "ai.hpp"
00024 
00025 /* static */ uint AI::frame_counter = 0;
00026 /* static */ AIScannerInfo *AI::scanner_info = NULL;
00027 /* static */ AIScannerLibrary *AI::scanner_library = NULL;
00028 
00029 /* static */ bool AI::CanStartNew()
00030 {
00031   /* Only allow new AIs on the server and only when that is allowed in multiplayer */
00032   return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer);
00033 }
00034 
00035 /* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai)
00036 {
00037   assert(Company::IsValidID(company));
00038 
00039   /* Clients shouldn't start AIs */
00040   if (_networking && !_network_server) return;
00041 
00042   AIConfig *config = AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME);
00043   AIInfo *info = config->GetInfo();
00044   if (info == NULL || (rerandomise_ai && config->IsRandom())) {
00045     info = AI::scanner_info->SelectRandomAI();
00046     assert(info != NULL);
00047     /* Load default data and store the name in the settings */
00048     config->Change(info->GetName(), -1, false, true);
00049   }
00050 
00051   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00052   Company *c = Company::Get(company);
00053 
00054   c->ai_info = info;
00055   assert(c->ai_instance == NULL);
00056   c->ai_instance = new AIInstance();
00057   c->ai_instance->Initialize(info);
00058 
00059   cur_company.Restore();
00060 
00061   InvalidateWindowData(WC_AI_DEBUG, 0, -1);
00062   return;
00063 }
00064 
00065 /* static */ void AI::GameLoop()
00066 {
00067   /* If we are in networking, only servers run this function, and that only if it is allowed */
00068   if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
00069 
00070   /* The speed with which AIs go, is limited by the 'competitor_speed' */
00071   AI::frame_counter++;
00072   assert(_settings_game.difficulty.competitor_speed <= 4);
00073   if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
00074 
00075   Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
00076   const Company *c;
00077   FOR_ALL_COMPANIES(c) {
00078     if (c->is_ai) {
00079       cur_company.Change(c->index);
00080       c->ai_instance->GameLoop();
00081     }
00082   }
00083   cur_company.Restore();
00084 
00085   /* Occasionally collect garbage; every 255 ticks do one company.
00086    * Effectively collecting garbage once every two months per AI. */
00087   if ((AI::frame_counter & 255) == 0) {
00088     CompanyID cid = (CompanyID)GB(AI::frame_counter, 8, 4);
00089     if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
00090   }
00091 }
00092 
00093 /* static */ uint AI::GetTick()
00094 {
00095   return AI::frame_counter;
00096 }
00097 
00098 /* static */ void AI::Stop(CompanyID company)
00099 {
00100   if (_networking && !_network_server) return;
00101 
00102   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00103   Company *c = Company::Get(company);
00104 
00105   delete c->ai_instance;
00106   c->ai_instance = NULL;
00107   c->ai_info = NULL;
00108 
00109   cur_company.Restore();
00110 
00111   InvalidateWindowData(WC_AI_DEBUG, 0, -1);
00112   DeleteWindowById(WC_AI_SETTINGS, company);
00113 }
00114 
00115 /* static */ void AI::Pause(CompanyID company)
00116 {
00117   /* The reason why dedicated servers are forbidden to execute this
00118    * command is not because it is unsafe, but because there is no way
00119    * for the server owner to unpause the script again. */
00120   if (_network_dedicated) return;
00121 
00122   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00123   Company::Get(company)->ai_instance->Pause();
00124 
00125   cur_company.Restore();
00126 }
00127 
00128 /* static */ void AI::Unpause(CompanyID company)
00129 {
00130   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00131   Company::Get(company)->ai_instance->Unpause();
00132 
00133   cur_company.Restore();
00134 }
00135 
00136 /* static */ bool AI::IsPaused(CompanyID company)
00137 {
00138   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00139   bool paused = Company::Get(company)->ai_instance->IsPaused();
00140 
00141   cur_company.Restore();
00142 
00143   return paused;
00144 }
00145 
00146 /* static */ void AI::KillAll()
00147 {
00148   /* It might happen there are no companies .. than we have nothing to loop */
00149   if (Company::GetPoolSize() == 0) return;
00150 
00151   const Company *c;
00152   FOR_ALL_COMPANIES(c) {
00153     if (c->is_ai) AI::Stop(c->index);
00154   }
00155 }
00156 
00157 /* static */ void AI::Initialize()
00158 {
00159   if (AI::scanner_info != NULL) AI::Uninitialize(true);
00160 
00161   AI::frame_counter = 0;
00162   if (AI::scanner_info == NULL) {
00163     TarScanner::DoScan(TarScanner::AI);
00164     AI::scanner_info = new AIScannerInfo();
00165     AI::scanner_info->Initialize();
00166     AI::scanner_library = new AIScannerLibrary();
00167     AI::scanner_library->Initialize();
00168   }
00169 }
00170 
00171 /* static */ void AI::Uninitialize(bool keepConfig)
00172 {
00173   AI::KillAll();
00174 
00175   if (keepConfig) {
00176     /* Run a rescan, which indexes all AIInfos again, and check if we can
00177      *  still load all the AIS, while keeping the configs in place */
00178     Rescan();
00179   } else {
00180     delete AI::scanner_info;
00181     delete AI::scanner_library;
00182     AI::scanner_info = NULL;
00183     AI::scanner_library = NULL;
00184 
00185     for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
00186       if (_settings_game.ai_config[c] != NULL) {
00187         delete _settings_game.ai_config[c];
00188         _settings_game.ai_config[c] = NULL;
00189       }
00190       if (_settings_newgame.ai_config[c] != NULL) {
00191         delete _settings_newgame.ai_config[c];
00192         _settings_newgame.ai_config[c] = NULL;
00193       }
00194     }
00195   }
00196 }
00197 
00198 /* static */ void AI::ResetConfig()
00199 {
00200   /* Check for both newgame as current game if we can reload the AIInfo insde
00201    *  the AIConfig. If not, remove the AI from the list (which will assign
00202    *  a random new AI on reload). */
00203   for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
00204     if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasScript()) {
00205       if (!_settings_game.ai_config[c]->ResetInfo(true)) {
00206         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());
00207         _settings_game.ai_config[c]->Change(NULL);
00208         if (Company::IsValidAiID(c)) {
00209           /* The code belonging to an already running AI was deleted. We can only do
00210            * one thing here to keep everything sane and that is kill the AI. After
00211            * killing the offending AI we start a random other one in it's place, just
00212            * like what would happen if the AI was missing during loading. */
00213           AI::Stop(c);
00214           AI::StartNew(c, false);
00215         }
00216       } else if (Company::IsValidAiID(c)) {
00217         /* Update the reference in the Company struct. */
00218         Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
00219       }
00220     }
00221     if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasScript()) {
00222       if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
00223         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());
00224         _settings_newgame.ai_config[c]->Change(NULL);
00225       }
00226     }
00227   }
00228 }
00229 
00230 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
00231 {
00232   /* AddRef() and Release() need to be called at least once, so do it here */
00233   event->AddRef();
00234 
00235   /* Clients should ignore events */
00236   if (_networking && !_network_server) {
00237     event->Release();
00238     return;
00239   }
00240 
00241   /* Only AIs can have an event-queue */
00242   if (!Company::IsValidAiID(company)) {
00243     event->Release();
00244     return;
00245   }
00246 
00247   /* Queue the event */
00248   Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00249   Company::Get(_current_company)->ai_instance->InsertEvent(event);
00250   cur_company.Restore();
00251 
00252   event->Release();
00253 }
00254 
00255 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
00256 {
00257   /* AddRef() and Release() need to be called at least once, so do it here */
00258   event->AddRef();
00259 
00260   /* Clients should ignore events */
00261   if (_networking && !_network_server) {
00262     event->Release();
00263     return;
00264   }
00265 
00266   /* Try to send the event to all AIs */
00267   for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
00268     if (c != skip_company) AI::NewEvent(c, event);
00269   }
00270 
00271   event->Release();
00272 }
00273 
00274 /* static */ void AI::Save(CompanyID company)
00275 {
00276   if (!_networking || _network_server) {
00277     Company *c = Company::GetIfValid(company);
00278     assert(c != NULL && c->ai_instance != NULL);
00279 
00280     Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00281     c->ai_instance->Save();
00282     cur_company.Restore();
00283   } else {
00284     AIInstance::SaveEmpty();
00285   }
00286 }
00287 
00288 /* static */ void AI::Load(CompanyID company, int version)
00289 {
00290   if (!_networking || _network_server) {
00291     Company *c = Company::GetIfValid(company);
00292     assert(c != NULL && c->ai_instance != NULL);
00293 
00294     Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
00295     c->ai_instance->Load(version);
00296     cur_company.Restore();
00297   } else {
00298     /* Read, but ignore, the load data */
00299     AIInstance::LoadEmpty();
00300   }
00301 }
00302 
00303 /* static */ int AI::GetStartNextTime()
00304 {
00305   /* Find the first company which doesn't exist yet */
00306   for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
00307     if (!Company::IsValidID(c)) return AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->GetSetting("start_date");
00308   }
00309 
00310   /* Currently no AI can be started, check again in a year. */
00311   return DAYS_IN_YEAR;
00312 }
00313 
00314 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
00315 {
00316   return AI::scanner_info->GetConsoleList(p, last, newest_only);
00317 }
00318 
00319 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
00320 {
00321    return AI::scanner_library->GetConsoleList(p, last, true);
00322 }
00323 
00324 /* static */ const ScriptInfoList *AI::GetInfoList()
00325 {
00326   return AI::scanner_info->GetInfoList();
00327 }
00328 
00329 /* static */ const ScriptInfoList *AI::GetUniqueInfoList()
00330 {
00331   return AI::scanner_info->GetUniqueInfoList();
00332 }
00333 
00334 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
00335 {
00336   return AI::scanner_info->FindInfo(name, version, force_exact_match);
00337 }
00338 
00339 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
00340 {
00341   return AI::scanner_library->FindLibrary(library, version);
00342 }
00343 
00344 /* static */ void AI::Rescan()
00345 {
00346   TarScanner::DoScan(TarScanner::AI);
00347 
00348   AI::scanner_info->RescanDir();
00349   AI::scanner_library->RescanDir();
00350   ResetConfig();
00351 
00352   InvalidateWindowData(WC_AI_LIST, 0, 1);
00353   SetWindowClassesDirty(WC_AI_DEBUG);
00354   InvalidateWindowClassesData(WC_AI_SETTINGS);
00355 }
00356 
00357 #if defined(ENABLE_NETWORK)
00358 
00365 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
00366 {
00367   return AI::scanner_info->HasScript(ci, md5sum);
00368 }
00369 
00370 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
00371 {
00372   return AI::scanner_library->HasScript(ci, md5sum);
00373 }
00374 
00375 #endif /* defined(ENABLE_NETWORK) */
00376 
00377 /* static */ AIScannerInfo *AI::GetScannerInfo()
00378 {
00379   return AI::scanner_info;
00380 }
00381 
00382 /* static */ AIScannerLibrary *AI::GetScannerLibrary()
00383 {
00384   return AI::scanner_library;
00385 }
00386