ai_instance.cpp

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

Generated on Wed Jan 20 23:38:33 2010 for OpenTTD by  doxygen 1.5.6