summaryrefslogtreecommitdiff
path: root/src/ai
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2010-11-24 17:00:37 +0000
committerrubidium <rubidium@openttd.org>2010-11-24 17:00:37 +0000
commit090d6fb8b7be9906cd4dea1ed152ffc9bac8bef0 (patch)
treec405672538e408932352426daaa4883997546bdd /src/ai
parent3330dee7e013d110d36c54a1e833b272a8762cab (diff)
downloadopenttd-090d6fb8b7be9906cd4dea1ed152ffc9bac8bef0.tar.xz
(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
Diffstat (limited to 'src/ai')
-rw-r--r--src/ai/ai_info.cpp13
-rw-r--r--src/ai/ai_instance.cpp13
2 files changed, 17 insertions, 9 deletions
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);