diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -86,11 +86,11 @@ public: /** * Load data for an AI from a savegame. */ - static void Load(CompanyID company); + static void Load(CompanyID company, int version); static char *GetConsoleList(char *p, const char *last); static const AIInfoList *GetInfoList(); - static AIInfo *GetCompanyInfo(const char *name); + static AIInfo *FindInfo(const char *name, int version); static bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm); static void Rescan(); diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp --- a/src/ai/ai_config.cpp +++ b/src/ai/ai_config.cpp @@ -9,11 +9,11 @@ #include "ai_config.hpp" #include "ai_info.hpp" -void AIConfig::ChangeAI(const char *name) +void AIConfig::ChangeAI(const char *name, int version) { free((void *)this->name); this->name = (name == NULL) ? NULL : strdup(name); - this->info = (name == NULL) ? NULL : AI::GetCompanyInfo(this->name); + this->info = (name == NULL) ? NULL : AI::FindInfo(this->name, version); this->version = (info == NULL) ? -1 : info->GetVersion(); for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) { @@ -45,7 +45,7 @@ AIInfo *AIConfig::GetInfo() bool AIConfig::ResetInfo() { - this->info = AI::GetCompanyInfo(this->name); + this->info = AI::FindInfo(this->name, this->version); return this->info != NULL; } diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp --- a/src/ai/ai_config.hpp +++ b/src/ai/ai_config.hpp @@ -26,8 +26,10 @@ public: /** * Set another AI to be loaded in this slot. + * @param name The name of the AI. + * @param version The version of the AI to load, or -1 of latest. */ - void ChangeAI(const char *name); + void ChangeAI(const char *name, int version = -1); /** * When ever the AI Scanner is reloaded, all infos become invalid. This diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -211,7 +211,7 @@ void CcAI(bool success, TileIndex tile, } } -/* static */ void AI::Load(CompanyID company) +/* static */ void AI::Load(CompanyID company, int version) { if (!_networking || _network_server) { assert(IsValidCompanyID(company)); @@ -219,7 +219,7 @@ void CcAI(bool success, TileIndex tile, CompanyID old_company = _current_company; _current_company = company; - GetCompany(company)->ai_instance->Load(); + GetCompany(company)->ai_instance->Load(version); _current_company = old_company; } else { /* Read, but ignore, the load data */ @@ -237,9 +237,9 @@ void CcAI(bool success, TileIndex tile, return AI::ai_scanner->GetAIInfoList(); } -/* static */ AIInfo *AI::GetCompanyInfo(const char *name) +/* static */ AIInfo *AI::FindInfo(const char *name, int version) { - return AI::ai_scanner->FindAI(name); + return AI::ai_scanner->FindInfo(name, version); } /* static */ bool AI::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm) diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -70,9 +70,26 @@ const char *AIFileInfo::GetInstanceName( return this->instance_name; } -bool AIFileInfo::AllowStartup() +bool AIFileInfo::CanLoadFromVersion(int version) { - return true; + if (version == -1) return true; + if (!this->engine->MethodExists(*this->SQ_instance, "CanLoadFromVersion")) return true; + + HSQUIRRELVM vm = this->engine->GetVM(); + int top = sq_gettop(vm); + + sq_pushobject(vm, *this->SQ_instance); + sq_pushstring(vm, OTTD2FS("CanLoadFromVersion"), -1); + sq_get(vm, -2); + sq_pushobject(vm, *this->SQ_instance); + sq_pushinteger(vm, version); + sq_call(vm, 2, SQTrue, SQFalse); + + HSQOBJECT ret; + sq_getstackobj(vm, -1, &ret); + + sq_settop(vm, top); + return sq_objtobool(&ret); } const char *AIFileInfo::GetDirName() diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp --- a/src/ai/ai_info.hpp +++ b/src/ai/ai_info.hpp @@ -74,7 +74,7 @@ public: /** * Check if we can start this AI. */ - bool AllowStartup(); + bool CanLoadFromVersion(int version); /** * Get the name of the dir this AI is in. diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -592,37 +592,38 @@ void AIInstance::Save() LoadObjects(NULL); } -bool AIInstance::Load() +void AIInstance::Load(int version) { - HSQUIRRELVM vm = (this->engine == NULL) ? NULL : this->engine->GetVM(); + if (this->engine == NULL || version == -1) { + LoadEmpty(); + return; + } + HSQUIRRELVM vm = this->engine->GetVM(); SlObject(NULL, _ai_byte); /* Check if there was anything saved at all. */ - if (_ai_sl_byte == 0) return true; + if (_ai_sl_byte == 0) return; AIObject::SetAllowDoCommand(false); - if (vm != NULL) { - /* Go to the instance-root */ - sq_pushobject(vm, *this->instance); - /* Find the function-name inside the script */ - sq_pushstring(vm, OTTD2FS("Load"), -1); - if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm); - sq_pushobject(vm, *this->instance); - } + /* Go to the instance-root */ + sq_pushobject(vm, *this->instance); + /* Find the function-name inside the script */ + sq_pushstring(vm, OTTD2FS("Load"), -1); + if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm); + sq_pushobject(vm, *this->instance); + sq_pushinteger(vm, version); LoadObjects(vm); - if (this->engine != NULL) { - if (this->engine->MethodExists(*this->instance, "Load")) { - sq_call(vm, 2, SQFalse, SQFalse); - } else { - AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function."); - } + if (this->engine->MethodExists(*this->instance, "Load")) { + sq_call(vm, 3, SQFalse, SQFalse); + } else { + AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function."); } - /* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the table. */ - if (vm != NULL) sq_pop(vm, 4); + /* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the (null) result. */ + sq_pop(vm, 4); AIObject::SetAllowDoCommand(true); - return true; + return; } diff --git a/src/ai/ai_instance.hpp b/src/ai/ai_instance.hpp --- a/src/ai/ai_instance.hpp +++ b/src/ai/ai_instance.hpp @@ -87,9 +87,10 @@ public: /** * Load data from a savegame and call the AI Load function if it * exists. - * @return True if the loading was successfull. + * @param version The version of the AI when saving, or -1 if this was + * not the original AI saving the game. */ - bool Load(); + void Load(int version); /** * Load and discard data from a savegame. diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp --- a/src/ai/ai_scanner.cpp +++ b/src/ai/ai_scanner.cpp @@ -344,26 +344,10 @@ AIInfo *AIScanner::SelectRandomAI() AIInfoList::iterator it = this->info_list.begin(); for (; pos > 0; pos--) it++; AIInfoList::iterator first_it = it; - AIInfo *i = (*it).second; - - if (!i->AllowStartup()) { - /* We can't start this AI, try to find the next best */ - do { - it++; - if (it == this->info_list.end()) it = this->info_list.begin(); - /* Back at the beginning? We can't start an AI. */ - if (first_it == it) { - DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI."); - return this->info_dummy; - } - - i = (*it).second; - } while (!i->AllowStartup()); - } - return i; + return (*it).second; } -AIInfo *AIScanner::FindAI(const char *name) +AIInfo *AIScanner::FindInfo(const char *name, int version) { if (this->info_list.size() == 0) return NULL; if (name == NULL) return NULL; @@ -372,7 +356,7 @@ AIInfo *AIScanner::FindAI(const char *na for (; it != this->info_list.end(); it++) { AIInfo *i = (*it).second; - if (strcasecmp(name, (*it).first) == 0 && i->AllowStartup()) { + if (strcasecmp(name, (*it).first) == 0 && i->CanLoadFromVersion(version)) { return i; } } @@ -386,8 +370,7 @@ char *AIScanner::GetAIConsoleList(char * AIInfoList::iterator it = this->info_list.begin(); for (; it != this->info_list.end(); it++) { AIInfo *i = (*it).second; - if (!i->AllowStartup()) continue; - p += seprintf(p, last, "%10s: %s\n", (*it).first, i->GetDescription()); + p += seprintf(p, last, "%10s (v%d): %s\n", (*it).first, i->GetVersion(), i->GetDescription()); } p += seprintf(p, last, "\n"); diff --git a/src/ai/ai_scanner.hpp b/src/ai/ai_scanner.hpp --- a/src/ai/ai_scanner.hpp +++ b/src/ai/ai_scanner.hpp @@ -42,7 +42,7 @@ public: /** * Find an AI by name. */ - class AIInfo *FindAI(const char *name); + class AIInfo *FindInfo(const char *name, int version); /** * Get the list of available AIs for the console. diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp --- a/src/saveload/ai_sl.cpp +++ b/src/saveload/ai_sl.cpp @@ -13,12 +13,14 @@ #include "../ai/ai.hpp" #include "../ai/ai_config.hpp" -static char _ai_saveload_ainame[64]; -static char _ai_company_convert_array[1024]; +static char _ai_saveload_name[64]; +static int _ai_saveload_version; +static char _ai_saveload_settings[1024]; static const SaveLoad _ai_company[] = { - SLEG_STR(_ai_saveload_ainame, SLE_STRB), - SLEG_STR(_ai_company_convert_array, SLE_STRB), + SLEG_STR(_ai_saveload_name, SLE_STRB), + SLEG_STR(_ai_saveload_settings, SLE_STRB), + SLEG_CONDVAR(_ai_saveload_version, SLE_UINT32, 108, SL_MAX_VERSION), SLE_END() }; @@ -27,10 +29,11 @@ static void SaveReal_AIPL(int *index_ptr CompanyID index = (CompanyID)*index_ptr; AIConfig *config = AIConfig::GetConfig(index); - ttd_strlcpy(_ai_saveload_ainame, config->GetName(), lengthof(_ai_saveload_ainame)); + ttd_strlcpy(_ai_saveload_name, config->GetName(), lengthof(_ai_saveload_name)); + _ai_saveload_version = config->GetVersion(); - _ai_company_convert_array[0] = '\0'; - config->SettingsToString(_ai_company_convert_array, lengthof(_ai_company_convert_array)); + _ai_saveload_settings[0] = '\0'; + config->SettingsToString(_ai_saveload_settings, lengthof(_ai_saveload_settings)); SlObject(NULL, _ai_company); /* If the AI was active, store his data too */ @@ -47,27 +50,30 @@ static void Load_AIPL() CompanyID index; while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) { AIConfig *config = AIConfig::GetConfig(index); + + _ai_saveload_version = -1; SlObject(NULL, _ai_company); - if (_ai_saveload_ainame[0] == '\0' || AI::GetCompanyInfo(_ai_saveload_ainame) == NULL) { - if (strcmp(_ai_saveload_ainame, "%_dummy") != 0) { - DEBUG(ai, 0, "The savegame has an AI by the name '%s' which is no longer available.", _ai_saveload_ainame); + config->ChangeAI(_ai_saveload_name, _ai_saveload_version); + if (!config->HasAI()) { + if (strcmp(_ai_saveload_name, "%_dummy") != 0) { + DEBUG(ai, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name, _ai_saveload_version); DEBUG(ai, 0, "A random other AI will be loaded in its place."); } else { DEBUG(ai, 0, "The savegame had no AIs available at the time of saving."); DEBUG(ai, 0, "A random available AI will be loaded now."); } - config->ChangeAI(NULL); - } else { - config->ChangeAI(_ai_saveload_ainame); + /* Make sure the AI doesn't get the saveload data, as he was not the + * writer of the saveload data in the first place */ + _ai_saveload_version = -1; } - config->StringToSettings(_ai_company_convert_array); + config->StringToSettings(_ai_saveload_settings); /* Start the AI directly if it was active in the savegame */ if (IsValidCompanyID(index) && !IsHumanCompany(index)) { AI::StartNew(index); - AI::Load(index); + AI::Load(index, _ai_saveload_version); } } } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -42,7 +42,7 @@ #include -extern const uint16 SAVEGAME_VERSION = 107; +extern const uint16 SAVEGAME_VERSION = 108; SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/script/squirrel.hpp b/src/script/squirrel.hpp --- a/src/script/squirrel.hpp +++ b/src/script/squirrel.hpp @@ -48,6 +48,7 @@ public: friend class AIController; friend class AIScanner; friend class AIInstance; + friend class AIFileInfo; Squirrel(); ~Squirrel();