ai_instance.cpp

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

Generated on Wed Dec 30 20:40:00 2009 for OpenTTD by  doxygen 1.5.6