ai_instance.cpp

Go to the documentation of this file.
00001 /* $Id: ai_instance.cpp 21890 2011-01-22 14:52:20Z rubidium $ */
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 "../debug.h"
00014 #include "../saveload/saveload.h"
00015 #include "../gui.h"
00016 
00017 #include "../script/squirrel_class.hpp"
00018 
00019 #include "ai_config.hpp"
00020 #include "ai_storage.hpp"
00021 #include "ai_instance.hpp"
00022 #include "ai_gui.hpp"
00023 
00024 /* Convert all AI related classes to Squirrel data.
00025  * Note: this line a marker in squirrel_export.sh. Do not change! */
00026 #include "api/ai_accounting.hpp.sq"
00027 #include "api/ai_airport.hpp.sq"
00028 #include "api/ai_base.hpp.sq"
00029 #include "api/ai_basestation.hpp.sq"
00030 #include "api/ai_bridge.hpp.sq"
00031 #include "api/ai_bridgelist.hpp.sq"
00032 #include "api/ai_cargo.hpp.sq"
00033 #include "api/ai_cargolist.hpp.sq"
00034 #include "api/ai_company.hpp.sq"
00035 #include "api/ai_controller.hpp.sq"
00036 #include "api/ai_date.hpp.sq"
00037 #include "api/ai_depotlist.hpp.sq"
00038 #include "api/ai_engine.hpp.sq"
00039 #include "api/ai_enginelist.hpp.sq"
00040 #include "api/ai_error.hpp.sq"
00041 #include "api/ai_event.hpp.sq"
00042 #include "api/ai_event_types.hpp.sq"
00043 #include "api/ai_execmode.hpp.sq"
00044 #include "api/ai_gamesettings.hpp.sq"
00045 #include "api/ai_group.hpp.sq"
00046 #include "api/ai_grouplist.hpp.sq"
00047 #include "api/ai_industry.hpp.sq"
00048 #include "api/ai_industrylist.hpp.sq"
00049 #include "api/ai_industrytype.hpp.sq"
00050 #include "api/ai_industrytypelist.hpp.sq"
00051 #include "api/ai_list.hpp.sq"
00052 #include "api/ai_log.hpp.sq"
00053 #include "api/ai_map.hpp.sq"
00054 #include "api/ai_marine.hpp.sq"
00055 #include "api/ai_order.hpp.sq"
00056 #include "api/ai_rail.hpp.sq"
00057 #include "api/ai_railtypelist.hpp.sq"
00058 #include "api/ai_road.hpp.sq"
00059 #include "api/ai_sign.hpp.sq"
00060 #include "api/ai_signlist.hpp.sq"
00061 #include "api/ai_station.hpp.sq"
00062 #include "api/ai_stationlist.hpp.sq"
00063 #include "api/ai_subsidy.hpp.sq"
00064 #include "api/ai_subsidylist.hpp.sq"
00065 #include "api/ai_testmode.hpp.sq"
00066 #include "api/ai_tile.hpp.sq"
00067 #include "api/ai_tilelist.hpp.sq"
00068 #include "api/ai_town.hpp.sq"
00069 #include "api/ai_townlist.hpp.sq"
00070 #include "api/ai_tunnel.hpp.sq"
00071 #include "api/ai_vehicle.hpp.sq"
00072 #include "api/ai_vehiclelist.hpp.sq"
00073 #include "api/ai_waypoint.hpp.sq"
00074 #include "api/ai_waypointlist.hpp.sq"
00075 
00076 #include "../company_base.h"
00077 #include "../company_func.h"
00078 #include "../fileio_func.h"
00079 
00081 static const int MAX_SL_OPS          = 100000;
00083 static const int MAX_CONSTRUCTOR_OPS = 100000;
00084 
00085 AIStorage::~AIStorage()
00086 {
00087   /* Free our pointers */
00088   if (event_data != NULL) AIEventController::FreeEventPointer();
00089   if (log_data != NULL) AILog::FreeLogPointer();
00090 }
00091 
00097 static void PrintFunc(bool error_msg, const SQChar *message)
00098 {
00099   /* Convert to OpenTTD internal capable string */
00100   AIController::Print(error_msg, SQ2OTTD(message));
00101 }
00102 
00103 AIInstance::AIInstance(AIInfo *info) :
00104   controller(NULL),
00105   storage(NULL),
00106   engine(NULL),
00107   instance(NULL),
00108   is_started(false),
00109   is_dead(false),
00110   is_save_data_on_stack(false),
00111   suspend(0),
00112   callback(NULL)
00113 {
00114   /* Set the instance already, so we can use AIObject::Set commands */
00115   Company::Get(_current_company)->ai_instance = this;
00116 
00117   this->controller = new AIController();
00118   this->storage    = new AIStorage();
00119   this->engine     = new Squirrel();
00120   this->engine->SetPrintFunction(&PrintFunc);
00121 
00122   /* The import method is available at a very early stage */
00123   this->engine->AddMethod("import", &AILibrary::Import, 4, ".ssi");
00124 
00125   /* Register the AIController */
00126   SQAIController_Register(this->engine);
00127 
00128   /* Register the API functions and classes */
00129   this->RegisterAPI();
00130 
00131   if (!this->LoadCompatibilityScripts(info->GetAPIVersion())) {
00132     this->Died();
00133     return;
00134   }
00135 
00136   try {
00137     AIObject::SetAllowDoCommand(false);
00138     /* Load and execute the script for this AI */
00139     const char *main_script = info->GetMainScript();
00140     if (strcmp(main_script, "%_dummy") == 0) {
00141       extern void AI_CreateAIDummy(HSQUIRRELVM vm);
00142       AI_CreateAIDummy(this->engine->GetVM());
00143     } else if (!this->engine->LoadScript(main_script) || this->engine->IsSuspended()) {
00144       if (this->engine->IsSuspended()) AILog::Error("This AI took too long to load script. AI is not started.");
00145       this->Died();
00146       return;
00147     }
00148 
00149     /* Create the main-class */
00150     this->instance = MallocT<SQObject>(1);
00151     if (!this->engine->CreateClassInstance(info->GetInstanceName(), this->controller, this->instance)) {
00152       this->Died();
00153       return;
00154     }
00155     AIObject::SetAllowDoCommand(true);
00156   } catch (AI_FatalError e) {
00157     this->is_dead = true;
00158     this->engine->ThrowError(e.GetErrorMessage());
00159     this->engine->ResumeError();
00160     this->Died();
00161   }
00162 }
00163 
00164 AIInstance::~AIInstance()
00165 {
00166   if (instance != NULL) this->engine->ReleaseObject(this->instance);
00167   if (engine != NULL) delete this->engine;
00168   delete this->storage;
00169   delete this->controller;
00170   free(this->instance);
00171 }
00172 
00173 void AIInstance::RegisterAPI()
00174 {
00175 /* Register all classes */
00176   squirrel_register_std(this->engine);
00177   SQAIList_Register(this->engine);
00178   SQAIAccounting_Register(this->engine);
00179   SQAIAirport_Register(this->engine);
00180   SQAIBase_Register(this->engine);
00181   SQAIBaseStation_Register(this->engine);
00182   SQAIBridge_Register(this->engine);
00183   SQAIBridgeList_Register(this->engine);
00184   SQAIBridgeList_Length_Register(this->engine);
00185   SQAICargo_Register(this->engine);
00186   SQAICargoList_Register(this->engine);
00187   SQAICargoList_IndustryAccepting_Register(this->engine);
00188   SQAICargoList_IndustryProducing_Register(this->engine);
00189   SQAICompany_Register(this->engine);
00190   SQAIDate_Register(this->engine);
00191   SQAIDepotList_Register(this->engine);
00192   SQAIEngine_Register(this->engine);
00193   SQAIEngineList_Register(this->engine);
00194   SQAIError_Register(this->engine);
00195   SQAIEvent_Register(this->engine);
00196   SQAIEventCompanyAskMerger_Register(this->engine);
00197   SQAIEventCompanyBankrupt_Register(this->engine);
00198   SQAIEventCompanyInTrouble_Register(this->engine);
00199   SQAIEventCompanyMerger_Register(this->engine);
00200   SQAIEventCompanyNew_Register(this->engine);
00201   SQAIEventController_Register(this->engine);
00202   SQAIEventDisasterZeppelinerCleared_Register(this->engine);
00203   SQAIEventDisasterZeppelinerCrashed_Register(this->engine);
00204   SQAIEventEngineAvailable_Register(this->engine);
00205   SQAIEventEnginePreview_Register(this->engine);
00206   SQAIEventIndustryClose_Register(this->engine);
00207   SQAIEventIndustryOpen_Register(this->engine);
00208   SQAIEventStationFirstVehicle_Register(this->engine);
00209   SQAIEventSubsidyAwarded_Register(this->engine);
00210   SQAIEventSubsidyExpired_Register(this->engine);
00211   SQAIEventSubsidyOffer_Register(this->engine);
00212   SQAIEventSubsidyOfferExpired_Register(this->engine);
00213   SQAIEventTownFounded_Register(this->engine);
00214   SQAIEventVehicleCrashed_Register(this->engine);
00215   SQAIEventVehicleLost_Register(this->engine);
00216   SQAIEventVehicleUnprofitable_Register(this->engine);
00217   SQAIEventVehicleWaitingInDepot_Register(this->engine);
00218   SQAIExecMode_Register(this->engine);
00219   SQAIGameSettings_Register(this->engine);
00220   SQAIGroup_Register(this->engine);
00221   SQAIGroupList_Register(this->engine);
00222   SQAIIndustry_Register(this->engine);
00223   SQAIIndustryList_Register(this->engine);
00224   SQAIIndustryList_CargoAccepting_Register(this->engine);
00225   SQAIIndustryList_CargoProducing_Register(this->engine);
00226   SQAIIndustryType_Register(this->engine);
00227   SQAIIndustryTypeList_Register(this->engine);
00228   SQAILog_Register(this->engine);
00229   SQAIMap_Register(this->engine);
00230   SQAIMarine_Register(this->engine);
00231   SQAIOrder_Register(this->engine);
00232   SQAIRail_Register(this->engine);
00233   SQAIRailTypeList_Register(this->engine);
00234   SQAIRoad_Register(this->engine);
00235   SQAISign_Register(this->engine);
00236   SQAISignList_Register(this->engine);
00237   SQAIStation_Register(this->engine);
00238   SQAIStationList_Register(this->engine);
00239   SQAIStationList_Vehicle_Register(this->engine);
00240   SQAISubsidy_Register(this->engine);
00241   SQAISubsidyList_Register(this->engine);
00242   SQAITestMode_Register(this->engine);
00243   SQAITile_Register(this->engine);
00244   SQAITileList_Register(this->engine);
00245   SQAITileList_IndustryAccepting_Register(this->engine);
00246   SQAITileList_IndustryProducing_Register(this->engine);
00247   SQAITileList_StationType_Register(this->engine);
00248   SQAITown_Register(this->engine);
00249   SQAITownList_Register(this->engine);
00250   SQAITunnel_Register(this->engine);
00251   SQAIVehicle_Register(this->engine);
00252   SQAIVehicleList_Register(this->engine);
00253   SQAIVehicleList_DefaultGroup_Register(this->engine);
00254   SQAIVehicleList_Depot_Register(this->engine);
00255   SQAIVehicleList_Group_Register(this->engine);
00256   SQAIVehicleList_SharedOrders_Register(this->engine);
00257   SQAIVehicleList_Station_Register(this->engine);
00258   SQAIWaypoint_Register(this->engine);
00259   SQAIWaypointList_Register(this->engine);
00260   SQAIWaypointList_Vehicle_Register(this->engine);
00261 
00262   this->engine->SetGlobalPointer(this->engine);
00263 }
00264 
00265 bool AIInstance::LoadCompatibilityScripts(const char *api_version)
00266 {
00267   char script_name[32];
00268   seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version);
00269   char buf[MAX_PATH];
00270   Searchpath sp;
00271   FOR_ALL_SEARCHPATHS(sp) {
00272     FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR);
00273     ttd_strlcat(buf, script_name, MAX_PATH);
00274     if (!FileExists(buf)) continue;
00275 
00276     if (this->engine->LoadScript(buf)) return true;
00277 
00278     AILog::Error("Failed to load API compatibility script");
00279     DEBUG(ai, 0, "Error compiling / running API compatibility script: %s", buf);
00280     return false;
00281   }
00282 
00283   AILog::Warning("API compatibility script not found");
00284   return true;
00285 }
00286 
00287 void AIInstance::Continue()
00288 {
00289   assert(this->suspend < 0);
00290   this->suspend = -this->suspend - 1;
00291 }
00292 
00293 void AIInstance::Died()
00294 {
00295   DEBUG(ai, 0, "The AI died unexpectedly.");
00296   this->is_dead = true;
00297 
00298   if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
00299   delete this->engine;
00300   this->instance = NULL;
00301   this->engine = NULL;
00302 
00303   ShowAIDebugWindow(_current_company);
00304 
00305   const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
00306   if (info != NULL) {
00307     ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
00308 
00309     if (info->GetURL() != NULL) {
00310       AILog::Info("Please report the error to the following URL:");
00311       AILog::Info(info->GetURL());
00312     }
00313   }
00314 }
00315 
00316 void AIInstance::GameLoop()
00317 {
00318   if (this->IsDead()) return;
00319   if (this->engine->HasScriptCrashed()) {
00320     /* The script crashed during saving, kill it here. */
00321     this->Died();
00322     return;
00323   }
00324   this->controller->ticks++;
00325 
00326   if (this->suspend   < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
00327   if (this->suspend   < 0)  return;          // Multiplayer suspend, wait for Continue().
00328   if (--this->suspend > 0)  return;          // Singleplayer suspend, decrease to 0.
00329 
00330   /* If there is a callback to call, call that first */
00331   if (this->callback != NULL) {
00332     if (this->is_save_data_on_stack) {
00333       sq_poptop(this->engine->GetVM());
00334       this->is_save_data_on_stack = false;
00335     }
00336     try {
00337       this->callback(this);
00338     } catch (AI_VMSuspend e) {
00339       this->suspend  = e.GetSuspendTime();
00340       this->callback = e.GetSuspendCallback();
00341 
00342       return;
00343     }
00344   }
00345 
00346   this->suspend  = 0;
00347   this->callback = NULL;
00348 
00349   if (!this->is_started) {
00350     try {
00351       AIObject::SetAllowDoCommand(false);
00352       /* Run the constructor if it exists. Don't allow any DoCommands in it. */
00353       if (this->engine->MethodExists(*this->instance, "constructor")) {
00354         if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
00355           if (this->engine->IsSuspended()) AILog::Error("This AI took too long to initialize. AI is not started.");
00356           this->Died();
00357           return;
00358         }
00359       }
00360       if (!this->CallLoad() || this->engine->IsSuspended()) {
00361         if (this->engine->IsSuspended()) AILog::Error("This AI took too long in the Load function. AI is not started.");
00362         this->Died();
00363         return;
00364       }
00365       AIObject::SetAllowDoCommand(true);
00366       /* Start the AI by calling Start() */
00367       if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.ai.ai_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00368     } catch (AI_VMSuspend e) {
00369       this->suspend  = e.GetSuspendTime();
00370       this->callback = e.GetSuspendCallback();
00371     } catch (AI_FatalError e) {
00372       this->is_dead = true;
00373       this->engine->ThrowError(e.GetErrorMessage());
00374       this->engine->ResumeError();
00375       this->Died();
00376     }
00377 
00378     this->is_started = true;
00379     return;
00380   }
00381   if (this->is_save_data_on_stack) {
00382     sq_poptop(this->engine->GetVM());
00383     this->is_save_data_on_stack = false;
00384   }
00385 
00386   /* Continue the VM */
00387   try {
00388     if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died();
00389   } catch (AI_VMSuspend e) {
00390     this->suspend  = e.GetSuspendTime();
00391     this->callback = e.GetSuspendCallback();
00392   } catch (AI_FatalError e) {
00393     this->is_dead = true;
00394     this->engine->ThrowError(e.GetErrorMessage());
00395     this->engine->ResumeError();
00396     this->Died();
00397   }
00398 }
00399 
00400 void AIInstance::CollectGarbage() const
00401 {
00402   if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
00403 }
00404 
00405 /* static */ void AIInstance::DoCommandReturn(AIInstance *instance)
00406 {
00407   instance->engine->InsertResult(AIObject::GetLastCommandRes());
00408 }
00409 
00410 /* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance)
00411 {
00412   instance->engine->InsertResult(AIObject::GetNewVehicleID());
00413 }
00414 
00415 /* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance)
00416 {
00417   instance->engine->InsertResult(AIObject::GetNewSignID());
00418 }
00419 
00420 /* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance)
00421 {
00422   instance->engine->InsertResult(AIObject::GetNewGroupID());
00423 }
00424 
00425 /* static */ AIStorage *AIInstance::GetStorage()
00426 {
00427   assert(Company::IsValidAiID(_current_company));
00428   return Company::Get(_current_company)->ai_instance->storage;
00429 }
00430 
00431 /*
00432  * All data is stored in the following format:
00433  * First 1 byte indicating if there is a data blob at all.
00434  * 1 byte indicating the type of data.
00435  * The data itself, this differs per type:
00436  *  - integer: a binary representation of the integer (int32).
00437  *  - string:  First one byte with the string length, then a 0-terminated char
00438  *             array. The string can't be longer than 255 bytes (including
00439  *             terminating '\0').
00440  *  - array:   All data-elements of the array are saved recursive in this
00441  *             format, and ended with an element of the type
00442  *             SQSL_ARRAY_TABLE_END.
00443  *  - table:   All key/value pairs are saved in this format (first key 1, then
00444  *             value 1, then key 2, etc.). All keys and values can have an
00445  *             arbitrary type (as long as it is supported by the save function
00446  *             of course). The table is ended with an element of the type
00447  *             SQSL_ARRAY_TABLE_END.
00448  *  - bool:    A single byte with value 1 representing true and 0 false.
00449  *  - null:    No data.
00450  */
00451 
00453 enum SQSaveLoadType {
00454   SQSL_INT             = 0x00, 
00455   SQSL_STRING          = 0x01, 
00456   SQSL_ARRAY           = 0x02, 
00457   SQSL_TABLE           = 0x03, 
00458   SQSL_BOOL            = 0x04, 
00459   SQSL_NULL            = 0x05, 
00460   SQSL_ARRAY_TABLE_END = 0xFF, 
00461 };
00462 
00463 static byte _ai_sl_byte; 
00464 
00466 static const SaveLoad _ai_byte[] = {
00467   SLEG_VAR(_ai_sl_byte, SLE_UINT8),
00468   SLE_END()
00469 };
00470 
00471 static const uint AISAVE_MAX_DEPTH = 25; 
00472 
00473 /* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00474 {
00475   if (max_depth == 0) {
00476     AILog::Error("Savedata can only be nested to 25 deep. No data saved.");
00477     return false;
00478   }
00479 
00480   switch (sq_gettype(vm, index)) {
00481     case OT_INTEGER: {
00482       if (!test) {
00483         _ai_sl_byte = SQSL_INT;
00484         SlObject(NULL, _ai_byte);
00485       }
00486       SQInteger res;
00487       sq_getinteger(vm, index, &res);
00488       if (!test) {
00489         int value = (int)res;
00490         SlArray(&value, 1, SLE_INT32);
00491       }
00492       return true;
00493     }
00494 
00495     case OT_STRING: {
00496       if (!test) {
00497         _ai_sl_byte = SQSL_STRING;
00498         SlObject(NULL, _ai_byte);
00499       }
00500       const SQChar *res;
00501       sq_getstring(vm, index, &res);
00502       /* @bug if a string longer than 512 characters is given to SQ2OTTD, the
00503        *  internal buffer overflows. */
00504       const char *buf = SQ2OTTD(res);
00505       size_t len = strlen(buf) + 1;
00506       if (len >= 255) {
00507         AILog::Error("Maximum string length is 254 chars. No data saved.");
00508         return false;
00509       }
00510       if (!test) {
00511         _ai_sl_byte = (byte)len;
00512         SlObject(NULL, _ai_byte);
00513         SlArray((void*)buf, len, SLE_CHAR);
00514       }
00515       return true;
00516     }
00517 
00518     case OT_ARRAY: {
00519       if (!test) {
00520         _ai_sl_byte = SQSL_ARRAY;
00521         SlObject(NULL, _ai_byte);
00522       }
00523       sq_pushnull(vm);
00524       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00525         /* Store the value */
00526         bool res = SaveObject(vm, -1, max_depth - 1, test);
00527         sq_pop(vm, 2);
00528         if (!res) {
00529           sq_pop(vm, 1);
00530           return false;
00531         }
00532       }
00533       sq_pop(vm, 1);
00534       if (!test) {
00535         _ai_sl_byte = SQSL_ARRAY_TABLE_END;
00536         SlObject(NULL, _ai_byte);
00537       }
00538       return true;
00539     }
00540 
00541     case OT_TABLE: {
00542       if (!test) {
00543         _ai_sl_byte = SQSL_TABLE;
00544         SlObject(NULL, _ai_byte);
00545       }
00546       sq_pushnull(vm);
00547       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00548         /* Store the key + value */
00549         bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00550         sq_pop(vm, 2);
00551         if (!res) {
00552           sq_pop(vm, 1);
00553           return false;
00554         }
00555       }
00556       sq_pop(vm, 1);
00557       if (!test) {
00558         _ai_sl_byte = SQSL_ARRAY_TABLE_END;
00559         SlObject(NULL, _ai_byte);
00560       }
00561       return true;
00562     }
00563 
00564     case OT_BOOL: {
00565       if (!test) {
00566         _ai_sl_byte = SQSL_BOOL;
00567         SlObject(NULL, _ai_byte);
00568       }
00569       SQBool res;
00570       sq_getbool(vm, index, &res);
00571       if (!test) {
00572         _ai_sl_byte = res ? 1 : 0;
00573         SlObject(NULL, _ai_byte);
00574       }
00575       return true;
00576     }
00577 
00578     case OT_NULL: {
00579       if (!test) {
00580         _ai_sl_byte = SQSL_NULL;
00581         SlObject(NULL, _ai_byte);
00582       }
00583       return true;
00584     }
00585 
00586     default:
00587       AILog::Error("You tried to save an unsupported type. No data saved.");
00588       return false;
00589   }
00590 }
00591 
00592 /* static */ void AIInstance::SaveEmpty()
00593 {
00594   _ai_sl_byte = 0;
00595   SlObject(NULL, _ai_byte);
00596 }
00597 
00598 void AIInstance::Save()
00599 {
00600   /* Don't save data if the AI didn't start yet or if it crashed. */
00601   if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00602     SaveEmpty();
00603     return;
00604   }
00605 
00606   HSQUIRRELVM vm = this->engine->GetVM();
00607   if (this->is_save_data_on_stack) {
00608     _ai_sl_byte = 1;
00609     SlObject(NULL, _ai_byte);
00610     /* Save the data that was just loaded. */
00611     SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
00612   } else if (!this->is_started) {
00613     SaveEmpty();
00614     return;
00615   } else if (this->engine->MethodExists(*this->instance, "Save")) {
00616     HSQOBJECT savedata;
00617     /* We don't want to be interrupted during the save function. */
00618     bool backup_allow = AIObject::GetAllowDoCommand();
00619     AIObject::SetAllowDoCommand(false);
00620     try {
00621       if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
00622         /* The script crashed in the Save function. We can't kill
00623          * it here, but do so in the next AI tick. */
00624         SaveEmpty();
00625         this->engine->CrashOccurred();
00626         return;
00627       }
00628     } catch (AI_FatalError e) {
00629       /* If we don't mark the AI as dead here cleaning up the squirrel
00630        * stack could throw AI_FatalError again. */
00631       this->is_dead = true;
00632       this->engine->ThrowError(e.GetErrorMessage());
00633       this->engine->ResumeError();
00634       SaveEmpty();
00635       /* We can't kill the AI here, so mark it as crashed (not dead) and
00636        * kill it in the next AI tick. */
00637       this->is_dead = false;
00638       this->engine->CrashOccurred();
00639       return;
00640     }
00641     AIObject::SetAllowDoCommand(backup_allow);
00642 
00643     if (!sq_istable(savedata)) {
00644       AILog::Error(this->engine->IsSuspended() ? "This AI took too long to Save." : "Save function should return a table.");
00645       SaveEmpty();
00646       this->engine->CrashOccurred();
00647       return;
00648     }
00649     sq_pushobject(vm, savedata);
00650     if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) {
00651       _ai_sl_byte = 1;
00652       SlObject(NULL, _ai_byte);
00653       SaveObject(vm, -1, AISAVE_MAX_DEPTH, false);
00654       this->is_save_data_on_stack = true;
00655     } else {
00656       SaveEmpty();
00657       this->engine->CrashOccurred();
00658     }
00659   } else {
00660     AILog::Warning("Save function is not implemented");
00661     _ai_sl_byte = 0;
00662     SlObject(NULL, _ai_byte);
00663   }
00664 
00665 }
00666 
00667 void AIInstance::Suspend()
00668 {
00669   HSQUIRRELVM vm = this->engine->GetVM();
00670   Squirrel::DecreaseOps(vm, _settings_game.ai.ai_max_opcode_till_suspend);
00671 }
00672 
00673 /* static */ bool AIInstance::LoadObjects(HSQUIRRELVM vm)
00674 {
00675   SlObject(NULL, _ai_byte);
00676   switch (_ai_sl_byte) {
00677     case SQSL_INT: {
00678       int value;
00679       SlArray(&value, 1, SLE_INT32);
00680       if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00681       return true;
00682     }
00683 
00684     case SQSL_STRING: {
00685       SlObject(NULL, _ai_byte);
00686       static char buf[256];
00687       SlArray(buf, _ai_sl_byte, SLE_CHAR);
00688       if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
00689       return true;
00690     }
00691 
00692     case SQSL_ARRAY: {
00693       if (vm != NULL) sq_newarray(vm, 0);
00694       while (LoadObjects(vm)) {
00695         if (vm != NULL) sq_arrayappend(vm, -2);
00696         /* The value is popped from the stack by squirrel. */
00697       }
00698       return true;
00699     }
00700 
00701     case SQSL_TABLE: {
00702       if (vm != NULL) sq_newtable(vm);
00703       while (LoadObjects(vm)) {
00704         LoadObjects(vm);
00705         if (vm != NULL) sq_rawset(vm, -3);
00706         /* The key (-2) and value (-1) are popped from the stack by squirrel. */
00707       }
00708       return true;
00709     }
00710 
00711     case SQSL_BOOL: {
00712       SlObject(NULL, _ai_byte);
00713       if (vm != NULL) sq_pushinteger(vm, (SQBool)(_ai_sl_byte != 0));
00714       return true;
00715     }
00716 
00717     case SQSL_NULL: {
00718       if (vm != NULL) sq_pushnull(vm);
00719       return true;
00720     }
00721 
00722     case SQSL_ARRAY_TABLE_END: {
00723       return false;
00724     }
00725 
00726     default: NOT_REACHED();
00727   }
00728 }
00729 
00730 /* static */ void AIInstance::LoadEmpty()
00731 {
00732   SlObject(NULL, _ai_byte);
00733   /* Check if there was anything saved at all. */
00734   if (_ai_sl_byte == 0) return;
00735 
00736   LoadObjects(NULL);
00737 }
00738 
00739 void AIInstance::Load(int version)
00740 {
00741   if (this->engine == NULL || version == -1) {
00742     LoadEmpty();
00743     return;
00744   }
00745   HSQUIRRELVM vm = this->engine->GetVM();
00746 
00747   SlObject(NULL, _ai_byte);
00748   /* Check if there was anything saved at all. */
00749   if (_ai_sl_byte == 0) return;
00750 
00751   sq_pushinteger(vm, version);
00752   LoadObjects(vm);
00753   this->is_save_data_on_stack = true;
00754 }
00755 
00756 bool AIInstance::CallLoad()
00757 {
00758   HSQUIRRELVM vm = this->engine->GetVM();
00759   /* Is there save data that we should load? */
00760   if (!this->is_save_data_on_stack) return true;
00761   /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
00762   this->is_save_data_on_stack = false;
00763 
00764   if (!this->engine->MethodExists(*this->instance, "Load")) {
00765     AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
00766 
00767     /* Pop the savegame data and version. */
00768     sq_pop(vm, 2);
00769     return true;
00770   }
00771 
00772   /* Go to the instance-root */
00773   sq_pushobject(vm, *this->instance);
00774   /* Find the function-name inside the script */
00775   sq_pushstring(vm, OTTD2SQ("Load"), -1);
00776   /* Change the "Load" string in a function pointer */
00777   sq_get(vm, -2);
00778   /* Push the main instance as "this" object */
00779   sq_pushobject(vm, *this->instance);
00780   /* Push the version data and savegame data as arguments */
00781   sq_push(vm, -5);
00782   sq_push(vm, -5);
00783 
00784   /* Call the AI load function. sq_call removes the arguments (but not the
00785    * function pointer) from the stack. */
00786   if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00787 
00788   /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
00789   sq_pop(vm, 4);
00790   return true;
00791 }

Generated on Fri Mar 18 23:17:32 2011 for OpenTTD by  doxygen 1.6.1