(svn r3260) -Add: add events for AIs to check if a command execution failed or succeeded
authortruelight
Mon, 05 Dec 2005 12:27:58 +0000
changeset 2715 d406c6ed777e
parent 2714 2d0366113f47
child 2716 2eb2687665e7
(svn r3260) -Add: add events for AIs to check if a command execution failed or succeeded
ai/ai.c
ai/ai.h
ai/ai_event.h
command.c
--- a/ai/ai.c	Sun Dec 04 22:43:40 2005 +0000
+++ b/ai/ai.c	Mon Dec 05 12:27:58 2005 +0000
@@ -15,6 +15,13 @@
 #include "ai_event.h"
 #undef DEF_EVENTS
 
+/* Here we keep track of all commands that are called via AI_DoCommandChecked,
+ *  in special we save the unique_id here. Now this id is given back
+ *  when the command fails or succeeds and is detected as added in this storage. */
+static AICommand *command_uid[MAX_PLAYERS];
+static AICommand *command_uid_tail[MAX_PLAYERS];
+static uint uids[MAX_PLAYERS];
+
 /**
  * Dequeues commands put in the queue via AI_PutCommandInQueue.
  */
@@ -122,6 +129,72 @@
 	return res;
 }
 
+int32 AI_DoCommandChecked(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
+{
+	AICommand *new;
+	uint unique_id = uids[_current_player]++;
+	int32 res;
+
+	res = AI_DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
+	if (CmdFailed(res))
+		return -1;
+
+	/* Save the command and his things, together with the unique_id */
+	new = malloc(sizeof(AICommand));
+	new->tile  = tile;
+	new->p1    = p1;
+	new->p2    = p2;
+	new->procc = procc;
+	new->next  = NULL;
+	new->dp[0] = unique_id;
+
+	/* Add it to the back of the list */
+	if (command_uid_tail[_current_player] == NULL)
+		command_uid_tail[_current_player] = new;
+	else
+		command_uid_tail[_current_player]->next = new;
+
+	if (command_uid[_current_player] == NULL)
+		command_uid[_current_player] = new;
+
+	AI_DoCommand(tile, p1, p2, flags, procc);
+	return unique_id;
+}
+
+/**
+ * A command is executed for real, and is giving us his result (failed yes/no). Inform the AI with it via
+ *  an event.Z
+ */
+void AI_CommandResult(uint32 cmd, uint32 p1, uint32 p2, TileIndex tile, bool succeeded)
+{
+	AICommand *command = command_uid[_current_player];
+
+	if (command == NULL)
+		return;
+
+	if (command->procc != (cmd & 0xFF) || command->p1 != p1 || command->p2 != p2 || command->tile != tile) {
+		/* If we come here, we see a command that isn't at top in our list. This is okay, if the command isn't
+		 *  anywhere else in our list, else we have a big problem.. so check for that. If it isn't in our list,
+		 *  it is okay, else, drop the game.
+		 * Why do we have a big problem in the case it is in our list? Simply, we have a command sooner in
+		 *  our list that wasn't triggered to be failed or succeeded, so it is sitting there without an
+		 *  event, so the script will never know if it succeeded yes/no, so it can hang.. this is VERY bad
+		 *  and should never ever happen. */
+		while (command != NULL && (command->procc != (cmd & 0xFF) || command->p1 != p1 || command->p2 != p2 || command->tile != tile)) {
+			command = command->next;
+		}
+		assert(command == NULL);
+		return;
+	}
+
+	command_uid[_current_player] = command_uid[_current_player]->next;
+	if (command_uid[_current_player] == NULL)
+		command_uid_tail[_current_player] = NULL;
+
+	ai_event(_current_player, succeeded ? ottd_Event_CommandSucceeded : ottd_Event_CommandFailed, tile, command->dp[0]);
+	free(command);
+}
+
 /**
  * Run 1 tick of the AI. Don't overdo it, keep it realistic.
  */
@@ -249,7 +322,7 @@
  */
 void AI_PrintErrorStack(gpmi_err_stack_t *entry, char *string)
 {
-	DEBUG(ai, 0)(string);
+	DEBUG(ai, 0)("%s", string);
 }
 
 #endif /* GPMI */
@@ -305,6 +378,17 @@
 	/* Called if this AI died */
 	_ai_player[player].active = false;
 
+	if (command_uid[player] != NULL) {
+		while (command_uid[player] != NULL) {
+			AICommand *command = command_uid[player];
+			command_uid[player] = command->next;
+			free(command);
+		}
+
+		command_uid[player] = NULL;
+		command_uid_tail[player] = NULL;
+	}
+
 #ifdef GPMI
 	if (_ai_player[player].module != NULL)
 		gpmi_mod_unload(_ai_player[player].module);
@@ -326,7 +410,10 @@
 	AI_Uninitialize();
 
 	memset(&_ai, 0, sizeof(_ai));
-	memset(&_ai_player, 0, sizeof(_ai_player));
+	memset(_ai_player, 0, sizeof(_ai_player));
+	memset(uids, 0, sizeof(uids));
+	memset(command_uid, 0, sizeof(command_uid));
+	memset(command_uid_tail, 0, sizeof(command_uid_tail));
 
 	_ai.network_client = tmp_ai_network_client;
 	_ai.network_playas = OWNER_SPECTATOR;
--- a/ai/ai.h	Sun Dec 04 22:43:40 2005 +0000
+++ b/ai/ai.h	Mon Dec 05 12:27:58 2005 +0000
@@ -57,6 +57,8 @@
 void AI_Initialize(void);
 void AI_Uninitialize(void);
 int32 AI_DoCommand(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
+int32 AI_DoCommandChecked(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
+void AI_CommandResult(uint32 cmd, uint32 p1, uint32 p2, TileIndex tile, bool failed);
 
 /** Is it allowed to start a new AI.
  * This function checks some boundries to see if we should launch a new AI.
--- a/ai/ai_event.h	Sun Dec 04 22:43:40 2005 +0000
+++ b/ai/ai_event.h	Mon Dec 05 12:27:58 2005 +0000
@@ -9,9 +9,9 @@
 /* This is how we call events (with safety-check) to GPMI */
 /* XXX -- This macro (vararg macro) isn't supported on old GCCs, but GPMI
  *         is using them too, so it isn't a real problem, yet */
-#	define ai_event(player, params...) \
+#	define ai_event(player, event, params...) \
 		if (player < MAX_PLAYERS && _ai_player[player].module != NULL) \
-			gpmi_event(_ai_player[player].module, params)
+			gpmi_event(_ai_player[player].module, event, params)
 
 #else
 /* If GPMI isn't loaded, don't do a thing with the events (for now at least)
@@ -40,6 +40,9 @@
 #endif /* DEF_EVENTS */
 
 /* ------------ All available events -------------- */
+DEF_EVENTS int ottd_Event_CommandFailed								INITIAL_SET; // (tile, unique_id)
+DEF_EVENTS int ottd_Event_CommandSucceeded						INITIAL_SET; // (tile, unique_id)
+
 DEF_EVENTS int ottd_Event_BuildStation								INITIAL_SET; // (station_index, station_tile)
 DEF_EVENTS int ottd_Event_BuildRoadStation						INITIAL_SET; // (station_index, station_tile)
 
--- a/command.c	Sun Dec 04 22:43:40 2005 +0000
+++ b/command.c	Mon Dec 05 12:27:58 2005 +0000
@@ -10,6 +10,7 @@
 #include "player.h"
 #include "network.h"
 #include "variables.h"
+#include "ai/ai.h"
 
 const char* _cmd_text = NULL;
 
@@ -476,10 +477,16 @@
 		res = proc(x,y, flags, p1, p2);
 		if (CmdFailed(res)) {
 			if (res & 0xFFFF) _error_message = res & 0xFFFF;
+			/* Trigger an event special for the AI, so it knows the build has failed
+			 *  Because the commands are always delayed, this is the only way. */
+			AI_CommandResult(cmd, p1, p2, tile, false);
 			goto show_error;
 		}
 		// no money? Only check if notest is off
-		if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
+		if (!notest && res != 0 && !CheckPlayerHasMoney(res)) {
+			AI_CommandResult(cmd, p1, p2, tile, false);
+			goto show_error;
+		}
 	}
 
 #ifdef ENABLE_NETWORK
@@ -514,10 +521,13 @@
 	} else {
 		if (CmdFailed(res2)) {
 			if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF;
+			AI_CommandResult(cmd, p1, p2, tile, false);
 			goto show_error;
 		}
 	}
 
+	AI_CommandResult(cmd, p1, p2, tile, true);
+
 	SubtractMoneyFromPlayer(res2);
 
 	if (IsLocalPlayer() && _game_mode != GM_EDITOR) {