(svn r15467) -Fix [NoAI]: AIs with an error in their info.nut are no longer available in-game.

replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
yexo 16 years ago
parent 15b9366f06
commit 8e14d77d05

@ -45,9 +45,9 @@ AILibrary::~AILibrary()
free((void *)this->category); free((void *)this->category);
} }
void AIFileInfo::GetSettings() const bool AIFileInfo::GetSettings()
{ {
this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, -1); return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, -1);
} }
bool AIFileInfo::CheckMethod(const char *name) const bool AIFileInfo::CheckMethod(const char *name) const
@ -90,13 +90,13 @@ bool AIFileInfo::CheckMethod(const char *name) const
info->main_script = strdup(info->base->GetMainScript()); info->main_script = strdup(info->base->GetMainScript());
/* Cache the data the info file gives us. */ /* Cache the data the info file gives us. */
info->author = info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAuthor"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAuthor", &info->author)) return SQ_ERROR;
info->name = info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetName"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetName", &info->name)) return SQ_ERROR;
info->short_name = info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetShortName"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetShortName", &info->short_name)) return SQ_ERROR;
info->description = info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDescription"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDescription", &info->description)) return SQ_ERROR;
info->date = info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDate"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDate", &info->date)) return SQ_ERROR;
info->version = info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion"); if (!info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion", &info->version)) return SQ_ERROR;
info->instance_name = info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance"); if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance", &info->instance_name)) return SQ_ERROR;
return 0; return 0;
} }
@ -118,10 +118,10 @@ bool AIFileInfo::CheckMethod(const char *name) const
/* Check if we have settings */ /* Check if we have settings */
if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) { if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) {
info->GetSettings(); if (!info->GetSettings()) return SQ_ERROR;
} }
if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) { if (info->engine->MethodExists(*info->SQ_instance, "MinVersionToLoad")) {
info->min_loadable_version = info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad"); if (!info->engine->CallIntegerMethod(*info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version)) return SQ_ERROR;
} else { } else {
info->min_loadable_version = info->GetVersion(); info->min_loadable_version = info->GetVersion();
} }
@ -367,7 +367,10 @@ int AIInfo::GetSettingDefaultValue(const char *name) const
} }
/* Cache the category */ /* Cache the category */
library->category = library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory"); if (!library->engine->CallStringMethodStrdup(*library->SQ_instance, "GetCategory", &library->category)) {
delete library;
return SQ_ERROR;
}
/* Register the Library to the base system */ /* Register the Library to the base system */
library->base->RegisterLibrary(library); library->base->RegisterLibrary(library);

@ -72,7 +72,7 @@ public:
/** /**
* Get the settings of the AI. * Get the settings of the AI.
*/ */
void GetSettings() const; bool GetSettings();
/** /**
* Get the date of the AI. * Get the date of the AI.

@ -249,6 +249,11 @@ void AIInstance::Died()
void AIInstance::GameLoop() void AIInstance::GameLoop()
{ {
if (this->is_dead) return; if (this->is_dead) return;
if (this->engine->HasScriptCrashed()) {
/* The script crashed during saving, kill it here. */
this->Died();
return;
}
this->controller->ticks++; this->controller->ticks++;
if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1. if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
@ -275,12 +280,12 @@ void AIInstance::GameLoop()
AIObject::SetAllowDoCommand(false); AIObject::SetAllowDoCommand(false);
/* Run the constructor if it exists. Don't allow any DoCommands in it. */ /* Run the constructor if it exists. Don't allow any DoCommands in it. */
if (this->engine->MethodExists(*this->instance, "constructor")) { if (this->engine->MethodExists(*this->instance, "constructor")) {
this->engine->CallMethod(*this->instance, "constructor"); if (!this->engine->CallMethod(*this->instance, "constructor")) { this->Died(); return; }
} }
this->CallLoad(); if (!this->CallLoad()) { this->Died(); return; }
AIObject::SetAllowDoCommand(true); AIObject::SetAllowDoCommand(true);
/* Start the AI by calling Start() */ /* Start the AI by calling Start() */
if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend)) this->Died(); if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
} catch (AI_VMSuspend e) { } catch (AI_VMSuspend e) {
this->suspend = e.GetSuspendTime(); this->suspend = e.GetSuspendTime();
this->callback = e.GetSuspendCallback(); this->callback = e.GetSuspendCallback();
@ -500,8 +505,8 @@ enum {
void AIInstance::Save() void AIInstance::Save()
{ {
/* Don't save data if the AI didn't start yet. */ /* Don't save data if the AI didn't start yet or if it crashed. */
if (this->engine == NULL) { if (this->engine == NULL || this->engine->HasScriptCrashed()) {
SaveEmpty(); SaveEmpty();
return; return;
} }
@ -526,7 +531,12 @@ void AIInstance::Save()
/* We don't want to be interrupted during the save function. */ /* We don't want to be interrupted during the save function. */
bool backup_allow = AIObject::GetAllowDoCommand(); bool backup_allow = AIObject::GetAllowDoCommand();
AIObject::SetAllowDoCommand(false); AIObject::SetAllowDoCommand(false);
this->engine->CallMethod(*this->instance, "Save", &savedata); if (!this->engine->CallMethod(*this->instance, "Save", &savedata)) {
/* The script crashed in the Save function. We can't kill
* it here, but do so in the next AI tick. */
SaveEmpty();
return;
}
AIObject::SetAllowDoCommand(backup_allow); AIObject::SetAllowDoCommand(backup_allow);
if (!sq_istable(savedata)) { if (!sq_istable(savedata)) {
@ -637,21 +647,21 @@ void AIInstance::Load(int version)
sq_pushbool(vm, true); sq_pushbool(vm, true);
} }
void AIInstance::CallLoad() bool AIInstance::CallLoad()
{ {
HSQUIRRELVM vm = this->engine->GetVM(); HSQUIRRELVM vm = this->engine->GetVM();
/* Is there save data that we should load? */ /* Is there save data that we should load? */
SQBool res; SQBool res;
sq_getbool(vm, -1, &res); sq_getbool(vm, -1, &res);
sq_poptop(vm); sq_poptop(vm);
if (!res) return; if (!res) return true;
if (!this->engine->MethodExists(*this->instance, "Load")) { if (!this->engine->MethodExists(*this->instance, "Load")) {
AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function."); AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function.");
/* Pop the savegame data and version. */ /* Pop the savegame data and version. */
sq_pop(vm, 2); sq_pop(vm, 2);
return; return true;
} }
/* Go to the instance-root */ /* Go to the instance-root */
@ -668,8 +678,9 @@ void AIInstance::CallLoad()
/* Call the AI load function. sq_call removes the arguments (but not the /* Call the AI load function. sq_call removes the arguments (but not the
* function pointer) from the stack. */ * function pointer) from the stack. */
sq_call(vm, 3, SQFalse, SQFalse); if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse))) return false;
/* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */ /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
sq_pop(vm, 4); sq_pop(vm, 4);
return true;
} }

@ -100,7 +100,7 @@ public:
* Call the AI Load function if it exists and data was loaded * Call the AI Load function if it exists and data was loaded
* from a savegame. * from a savegame.
*/ */
void CallLoad(); bool CallLoad();
/** /**
* Load and discard data from a savegame. * Load and discard data from a savegame.

@ -88,6 +88,8 @@ void AIScanner::ScanDir(const char *dirname, bool library_scan, bool library_rec
if (!FioCheckFileExists(info_script, AI_DIR) || !FioCheckFileExists(main_script, AI_DIR)) continue; if (!FioCheckFileExists(info_script, AI_DIR) || !FioCheckFileExists(main_script, AI_DIR)) continue;
DEBUG(ai, 6, "Loading AI at location '%s'", main_script); DEBUG(ai, 6, "Loading AI at location '%s'", main_script);
/* We don't care if one of the other scripst failed to load. */
this->engine->ResetCrashed();
this->engine->LoadScript(info_script); this->engine->LoadScript(info_script);
} else { } else {
char library_script[MAX_PATH]; char library_script[MAX_PATH];
@ -100,6 +102,8 @@ void AIScanner::ScanDir(const char *dirname, bool library_scan, bool library_rec
if (!FioCheckFileExists(library_script, AI_LIBRARY_DIR) || !FioCheckFileExists(main_script, AI_LIBRARY_DIR)) continue; if (!FioCheckFileExists(library_script, AI_LIBRARY_DIR) || !FioCheckFileExists(main_script, AI_LIBRARY_DIR)) continue;
DEBUG(ai, 6, "Loading AI Library at location '%s'", main_script); DEBUG(ai, 6, "Loading AI Library at location '%s'", main_script);
/* We don't care if one of the other scripst failed to load. */
this->engine->ResetCrashed();
this->engine->LoadScript(library_script); this->engine->LoadScript(library_script);
} }
} }
@ -157,6 +161,7 @@ AIScanner::AIScanner() :
this->ScanAIDir(); this->ScanAIDir();
/* Create the dummy AI */ /* Create the dummy AI */
this->engine->ResetCrashed();
extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm); extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm);
AI_CreateAIInfoDummy(this->engine->GetVM()); AI_CreateAIInfoDummy(this->engine->GetVM());
} }

@ -24,7 +24,9 @@ void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *so
#endif #endif
/* Check if we have a custom print function */ /* Check if we have a custom print function */
SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; Squirrel *engine = (Squirrel *)sq_getforeignptr(vm);
engine->crashed = true;
SQPrintFunc *func = engine->print_func;
if (func == NULL) { if (func == NULL) {
scfprintf(stderr, _SC("%s"), buf); scfprintf(stderr, _SC("%s"), buf);
} else { } else {
@ -59,7 +61,9 @@ void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error)
/* Check if we have a custom print function */ /* Check if we have a custom print function */
SQChar buf[1024]; SQChar buf[1024];
scsnprintf(buf, lengthof(buf), _SC("Your script made an error: %s\n"), error); scsnprintf(buf, lengthof(buf), _SC("Your script made an error: %s\n"), error);
SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; Squirrel *engine = (Squirrel *)sq_getforeignptr(vm);
engine->crashed = true;
SQPrintFunc *func = engine->print_func;
if (func == NULL) { if (func == NULL) {
scfprintf(stderr, _SC("%s"), buf); scfprintf(stderr, _SC("%s"), buf);
} else { } else {
@ -156,6 +160,7 @@ void Squirrel::AddClassEnd()
bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name) bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
{ {
assert(!this->crashed);
int top = sq_gettop(this->vm); int top = sq_gettop(this->vm);
/* Go to the instance-root */ /* Go to the instance-root */
sq_pushobject(this->vm, instance); sq_pushobject(this->vm, instance);
@ -171,6 +176,7 @@ bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
bool Squirrel::Resume(int suspend) bool Squirrel::Resume(int suspend)
{ {
assert(!this->crashed);
sq_resumecatch(this->vm, suspend); sq_resumecatch(this->vm, suspend);
return this->vm->_suspended != 0; return this->vm->_suspended != 0;
} }
@ -182,6 +188,7 @@ void Squirrel::CollectGarbage()
bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend) bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
{ {
assert(!this->crashed);
/* Store the stack-location for the return value. We need to /* Store the stack-location for the return value. We need to
* restore this after saving or the stack will be corrupted * restore this after saving or the stack will be corrupted
* if we're in the middle of a DoCommand. */ * if we're in the middle of a DoCommand. */
@ -199,7 +206,7 @@ bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT
} }
/* Call the method */ /* Call the method */
sq_pushobject(this->vm, instance); sq_pushobject(this->vm, instance);
sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQTrue, suspend); if (SQ_FAILED(sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQTrue, suspend))) return false;
if (ret != NULL) sq_getstackobj(vm, -1, ret); if (ret != NULL) sq_getstackobj(vm, -1, ret);
/* Reset the top, but don't do so for the AI main function, as we need /* Reset the top, but don't do so for the AI main function, as we need
* a correct stack when resuming. */ * a correct stack when resuming. */
@ -207,7 +214,25 @@ bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT
/* Restore the return-value location. */ /* Restore the return-value location. */
this->vm->_suspended_target = last_target; this->vm->_suspended_target = last_target;
return this->vm->_suspended != 0; return true;
}
bool Squirrel::CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
if (ret._type != OT_STRING) return false;
*res = strdup(ObjectToString(&ret));
return true;
}
bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
if (ret._type != OT_INTEGER) return false;
*res = ObjectToInteger(&ret);
return true;
} }
/* static */ bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook) /* static */ bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook)
@ -258,6 +283,7 @@ Squirrel::Squirrel()
this->vm = sq_open(1024); this->vm = sq_open(1024);
this->print_func = NULL; this->print_func = NULL;
this->global_pointer = NULL; this->global_pointer = NULL;
this->crashed = false;
/* Handle compile-errors ourself, so we can display it nicely */ /* Handle compile-errors ourself, so we can display it nicely */
sq_setcompilererrorhandler(this->vm, &Squirrel::CompileError); sq_setcompilererrorhandler(this->vm, &Squirrel::CompileError);
@ -459,3 +485,18 @@ void Squirrel::InsertResult(int result)
{ {
vm->DecreaseOps(ops); vm->DecreaseOps(ops);
} }
bool Squirrel::IsSuspended()
{
return this->vm->_suspended != 0;
}
bool Squirrel::HasScriptCrashed()
{
return this->crashed;
}
void Squirrel::ResetCrashed()
{
this->crashed = false;
}

@ -12,6 +12,7 @@ private:
HSQUIRRELVM vm; ///< The VirtualMachine instnace for squirrel HSQUIRRELVM vm; ///< The VirtualMachine instnace for squirrel
void *global_pointer; ///< Can be set by who ever initializes Squirrel void *global_pointer; ///< Can be set by who ever initializes Squirrel
SQPrintFunc *print_func; ///< Points to either NULL, or a custom print handler SQPrintFunc *print_func; ///< Points to either NULL, or a custom print handler
bool crashed; ///< True if the squirrel script made an error.
/** /**
* The internal RunError handler. It looks up the real error and calls RunError with it. * The internal RunError handler. It looks up the real error and calls RunError with it.
@ -111,11 +112,12 @@ public:
/** /**
* Call a method of an instance, in various flavors. * 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, 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 CallMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { return this->CallMethod(instance, method_name, NULL, suspend); }
const char *CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, int suspend = -1) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret, suspend); return strdup(ObjectToString(&ret)); } bool CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend = -1);
int CallIntegerMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret, suspend); return ObjectToInteger(&ret); } bool CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend = -1);
/** /**
* Check if a method exists in an instance. * Check if a method exists in an instance.
@ -191,6 +193,22 @@ public:
* Tell the VM to remove \c amount ops from the number of ops till suspend. * Tell the VM to remove \c amount ops from the number of ops till suspend.
*/ */
static void DecreaseOps(HSQUIRRELVM vm, int amount); static void DecreaseOps(HSQUIRRELVM vm, int amount);
/**
* Did the squirrel code suspend or return normally.
* @return True if the function suspended.
*/
bool IsSuspended();
/**
* Find out if the squirrel script made an error before.
*/
bool HasScriptCrashed();
/**
* Reset the crashed status.
*/
void ResetCrashed();
}; };
#endif /* SQUIRREL_HPP */ #endif /* SQUIRREL_HPP */

Loading…
Cancel
Save