From 090d6fb8b7be9906cd4dea1ed152ffc9bac8bef0 Mon Sep 17 00:00:00 2001 From: rubidium Date: Wed, 24 Nov 2010 17:00:37 +0000 Subject: (svn r21311) -Fix [FS#4260]: AIs in an infinite loop in e.g. autosave, but also getting settings and such from info.nut, would not be interrupted after a while causing OpenTTD to seem to not respond --- src/ai/ai_info.cpp | 13 ++++++++----- src/ai/ai_instance.cpp | 13 +++++++++---- src/script/script_info.cpp | 21 +++++++++++++-------- src/script/squirrel.hpp | 10 +++++----- 4 files changed, 35 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index 54725cd48..2194f20c5 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -19,6 +19,9 @@ #include "../debug.h" #include "../rev.h" +/** Maximum number of operations allowed for getting a particular setting. */ +static const int MAX_GET_SETTING_OPS = 100000; + /** Configuration for AI start date, every AI has this setting. */ AIConfigItem _start_date_config = { "start_date", @@ -78,19 +81,19 @@ static bool CheckAPIVersion(const char *api_version) if (!info->GetSettings()) return SQ_ERROR; } if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) { - if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version)) return SQ_ERROR; + if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_SETTING_OPS)) return SQ_ERROR; } else { info->min_loadable_version = info->GetVersion(); } /* When there is an UseAsRandomAI function, call it. */ if (info->engine->MethodExists(*info->SQ_instance, "UseAsRandomAI")) { - if (!info->engine->CallBoolMethod(*info->SQ_instance, "UseAsRandomAI", &info->use_as_random)) return SQ_ERROR; + if (!info->engine->CallBoolMethod(*info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_SETTING_OPS)) return SQ_ERROR; } else { info->use_as_random = true; } /* Try to get the API version the AI is written for. */ if (info->engine->MethodExists(*info->SQ_instance, "GetAPIVersion")) { - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAPIVersion", &info->api_version)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_SETTING_OPS)) return SQ_ERROR; if (!CheckAPIVersion(info->api_version)) { DEBUG(ai, 1, "Loading info.nut from (%s.%d): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion()); return SQ_ERROR; @@ -130,7 +133,7 @@ static bool CheckAPIVersion(const char *api_version) bool AIInfo::GetSettings() { - return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, -1); + return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, MAX_GET_SETTING_OPS); } AIInfo::AIInfo() : @@ -360,7 +363,7 @@ int AIInfo::GetSettingDefaultValue(const char *name) const } /* Cache the category */ - if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category)) { + if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category, MAX_GET_SETTING_OPS)) { delete library; return SQ_ERROR; } diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index 80cc06a7f..628a32e29 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -76,6 +76,11 @@ #include "../company_base.h" #include "../fileio_func.h" +/** The maximum number of operations for saving or loading the data of an AI. */ +static const int MAX_SL_OPS = 100000; +/** The maximum number of operations for initial start of an AI. */ +static const int MAX_CONSTRUCTOR_OPS = 100000; + AIStorage::~AIStorage() { /* Free our pointers */ @@ -344,7 +349,7 @@ void AIInstance::GameLoop() AIObject::SetAllowDoCommand(false); /* Run the constructor if it exists. Don't allow any DoCommands in it. */ if (this->engine->MethodExists(*this->instance, "constructor")) { - if (!this->engine->CallMethod(*this->instance, "constructor", 100000) || this->engine->IsSuspended()) { + if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) { if (this->engine->IsSuspended()) AILog::Error("This AI took too long to initialize. AI is not started."); this->Died(); return; @@ -611,7 +616,7 @@ void AIInstance::Save() bool backup_allow = AIObject::GetAllowDoCommand(); AIObject::SetAllowDoCommand(false); try { - if (!this->engine->CallMethod(*this->instance, "Save", &savedata)) { + if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) { /* The script crashed in the Save function. We can't kill * it here, but do so in the next AI tick. */ SaveEmpty(); @@ -634,7 +639,7 @@ void AIInstance::Save() AIObject::SetAllowDoCommand(backup_allow); if (!sq_istable(savedata)) { - AILog::Error("Save function should return a table."); + AILog::Error(this->engine->IsSuspended() ? "This AI took too long to Save." : "Save function should return a table."); SaveEmpty(); this->engine->CrashOccurred(); return; @@ -776,7 +781,7 @@ bool AIInstance::CallLoad() /* Call the AI load function. sq_call removes the arguments (but not the * function pointer) from the stack. */ - if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, 100000))) return false; + if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false; /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */ sq_pop(vm, 4); diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp index 807c42bc1..152bb02f3 100644 --- a/src/script/script_info.cpp +++ b/src/script/script_info.cpp @@ -16,6 +16,11 @@ #include "script_info.hpp" #include "script_scanner.hpp" +/** Number of operations to get the author and similar information. */ +static const int MAX_GET_OPS = 1000; +/** Number of operations to create an instance of an AI. */ +static const int MAX_CREATEINSTANCE_OPS = 100000; + ScriptFileInfo::~ScriptFileInfo() { free((void *)this->author); @@ -66,17 +71,17 @@ bool ScriptFileInfo::CheckMethod(const char *name) const info->main_script = strdup(scanner->GetMainScript()); /* Cache the data the info file gives us. */ - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAuthor", &info->author)) return SQ_ERROR; - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetName", &info->name)) return SQ_ERROR; - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetShortName", &info->short_name)) return SQ_ERROR; - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDescription", &info->description)) return SQ_ERROR; - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDate", &info->date)) return SQ_ERROR; - if (!info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion", &info->version)) return SQ_ERROR; - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance", &info->instance_name)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAuthor", &info->author, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetName", &info->name, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetShortName", &info->short_name, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDescription", &info->description, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDate", &info->date, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion", &info->version, MAX_GET_OPS)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance", &info->instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR; /* The GetURL function is optional. */ if (info->engine->MethodExists(*info->SQ_instance, "GetURL")) { - if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetURL", &info->url)) return SQ_ERROR; + if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetURL", &info->url, MAX_GET_OPS)) return SQ_ERROR; } return 0; diff --git a/src/script/squirrel.hpp b/src/script/squirrel.hpp index 53a74eaaa..d50dc356a 100644 --- a/src/script/squirrel.hpp +++ b/src/script/squirrel.hpp @@ -140,11 +140,11 @@ public: * Call a method of an instance, in various flavors. * @return False if the script crashed or returned a wrong type. */ - bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend = -1); - bool CallMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { return this->CallMethod(instance, method_name, NULL, suspend); } - bool CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend = -1); - bool CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend = -1); - bool CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend = -1); + bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend); + bool CallMethod(HSQOBJECT instance, const char *method_name, int suspend) { return this->CallMethod(instance, method_name, NULL, suspend); } + bool CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend); + bool CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend); + bool CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend); /** * Check if a method exists in an instance. -- cgit v1.2.3-70-g09d2