Problems with AIError

Discuss the new AI features ("NoAI") introduced into OpenTTD 0.7, allowing you to implement custom AIs, and the new Game Scripts available in OpenTTD 1.2 and higher.

Moderator: OpenTTD Developers

Post Reply
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Problems with AIError

Post by R2dical »

So I am having some trouble with errorhandling in my AI:

I have a "generic" errorhandler function which currently just reports any error (via a huge switch for the code enums) with some details. Later on I plan to use this to also do some runtime errorhandling with it(raise funds, demolish, landscape, etc). It is a member of my main class like so:

Code: Select all

/*
 *	Generic error handler which displays the latest error in log and breaks for debug.
 *	@param error Call AIError.GetLastError() and pass the variable in as parameter.
 */
function RadAI::ErrorHandler(error) {
Note RadAI is my AI and I have a line in the constructor like so "::main <- this;" to call the instance from other classes.

I have had a number of issues calling this function from within other class methods and such, an older one which I shrugged of at the time:

In my RoadBuilder class, after a number of if...else possible Do commands where returning true I have "success = true", then after:

Code: Select all

if(!success) {
local last_error = AIError.GetLastError();	
::main.ErrorHandler(last_error);
...
Running it like so, which I tried first doesn't work:

Code: Select all

if(!success) {
::main.ErrorHandler(AIError.GetLastError());
...
"Doesn't work" in that the first call picks up the error code and the second passes in "AIError.ERR_NONE" to my errorhandler :(

More recently, and I think is the same issue (this is a segment of a landscaping function, to flatten a square of tiles corner by corner):

Code: Select all

while((corner_height = AITile.GetCornerHeight(tile, corner)) != preferred_height) {

	// Raise if lower.
	if(corner_height < preferred_height) {
		if(!AITile.RaiseTile(tile, slope_array[index]))
			// Error handler.
			::main.ErrorHandler(AIError.GetLastError());
		else
			landscape_success = true;
	}
	// Lower if higher.
	else if(corner_height > preferred_height) {
		if(!AITile.LowerTile(tile, slope_array[index]))
			// Error handler.
			::main.ErrorHandler(AIError.GetLastError());
		else
			landscape_success = true;
	}
		
	// Check for success.
	if(!landscape_success) {
	
		Log.Warning(" - Landscapeing failed.", Log.DETAIL);
		
		//Debug
		Sign.BuildDebugSign(tile, "landscape fail: " + preferred_height);
		
		return false;
	}
}
Works as expected, while:

Code: Select all

while((corner_height = AITile.GetCornerHeight(tile, corner)) != preferred_height) {

	// Raise if lower.
	if(corner_height < preferred_height) {
		landscape_success = AITile.RaiseTile(tile, slope_array[index]);
	}
	// Lower if higher.
	else if(corner_height > preferred_height) {
		landscape_success = AITile.LowerTile(tile, slope_array[index]);
	}
		
	// Check for success.
	if(!landscape_success) {
	
		Log.Warning(" - Landscapeing failed.", Log.DETAIL);
		
		//Debug
		Sign.BuildDebugSign(tile, "landscape fail: " + preferred_height);
		
		// Error handler.
		::main.ErrorHandler(AIError.GetLastError());
			
		return false;
	}
}
Will show my warning message but not the error and reason why. More worryingly in one test variation I got messages of "AIOrder.ERR_ORDER_TOO_MANY" instead of the "correct" "AIError.ERR_NOT_ENOUGH_CASH", which is nonsensical for this Landscaping function.

In both of the above examples AIExecMode is active, which I thought may be the cause. The issue seems to lie with scope...

So am I doing something wrong?
krinn
Transport Coordinator
Transport Coordinator
Posts: 342
Joined: 29 Dec 2010 19:36

Re: Problems with AIError

Post by krinn »

R2dical wrote:function RadAI::ErrorHandler(error)
First it's only useful if you want provide a special code, else it's easier to pass no parameter and let the function load the error itself.

Code: Select all

if(!success) {
local last_error = AIError.GetLastError();	
::main.ErrorHandler(last_error);
...

Code: Select all

if(!success) {
::main.ErrorHandler(AIError.GetLastError());
...
Both are identical.
If "AIError.ERR_NONE" is pass, it's because there's simply no error. The function return the last error code, the function doesn't return the last error code if an error occurs : so on success a function can set AIError.ERR_NONE.
if(!landscape_success) {

Log.Warning(" - Landscapeing failed.", Log.DETAIL);

//Debug
Sign.BuildDebugSign(tile, "landscape fail: " + preferred_height);

// Error handler.
::main.ErrorHandler(AIError.GetLastError());
Like i said upper : you get the last error code.
So you expect to get the error code for Raising/Lowering tile, but you get the sign error code :
if the sign is built you get AIError.ERR_NONE
and if it fail to build the sign, ERR_SIGN_TOO_MANY_SIGNS.
And if you don't look at the category of errors, maybe "ERR_SIGN_TOO_MANY_SIGNS" == 255 and other category also use 255 for "TOO_MANY."
And you assume 255 == AIOrder.ORDER_TOO_MANY in your switch.
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Re: Problems with AIError

Post by R2dical »

Thanks for your reply.
krinn wrote:First it's only useful if you want provide a special code, else it's easier to pass no parameter and let the function load the error itself.
Yeah I guess I can remove that, it was originally put in as I wanted to use getstackinfos() from squirrel docs to print the calling function name in the error debug. But IIRC it was posted(long ago) that this feature was removed due to then upcoming NAIL code compatibility.

Ahh okay, so part of my problem here is I thought AIError.GetLastError() returns last error not including AIError.ERR_NONE. So the last error is actually a report of the last Do command? This error is reset each time?

The other thing I saw now is that for:

Code: Select all

local last_error = AIError.GetLastError();
"last_error" is actually a pointer? Not the stored value?

I say because doing this:

Code: Select all

if(!AITile.LowerTile(tile, slope_array[index])) {
	local last_error = AIError.GetLastError();
	::main.ErrorHandler(last_error);
	Sign.BuildDebugSign(tile, "landscape fail: " + preferred_height);
	::main.ErrorHandler(last_error);
}
Results in different error reporting.

So with the above 2 clarified I think my mass of "problems" can be explained, including the "AIOrder.ORDER_TOO_MANY" (which was fine in my switch, but may have pulled from a parent class in that case). I had the wrong assumptions about the workings of GetLastError.
Kogut
Tycoon
Tycoon
Posts: 2493
Joined: 26 Aug 2009 06:33
Location: Poland

Re: Problems with AIError

Post by Kogut »

So the last error is actually a report of the last Do command? This error is reset each time?
Yes and yes.
Correct me If I am wrong - PM me if my English is bad
AIAI - AI for OpenTTD
R2dical
Traffic Manager
Traffic Manager
Posts: 163
Joined: 18 Mar 2013 22:22

Re: Problems with AIError

Post by R2dical »

Thanks guys I think I got it figured now :)
Post Reply

Return to “OpenTTD AIs and Game Scripts”

Who is online

Users browsing this forum: No registered users and 17 guests