(svn r13656) [NoAI] -Add: added suspend control. If an AI consumes N (default = 4000) opcodes, a sleep is forced (for 1 tick). This to give fair play for all AIs. Also, an infinite loop won't hang the GUI of the player. (Morloth) noai
authortruebrain
Mon, 30 Jun 2008 12:27:24 +0000
branchnoai
changeset 11098 37d15a8951b8
parent 11097 6967c52c78c5
child 11099 7683a97b89f9
(svn r13656) [NoAI] -Add: added suspend control. If an AI consumes N (default = 4000) opcodes, a sleep is forced (for 1 tick). This to give fair play for all AIs. Also, an infinite loop won't hang the GUI of the player. (Morloth)
src/ai/ai.cpp
src/ai/ai.h
src/ai/api/ai_controller.cpp
src/lang/english.txt
src/settings.cpp
src/settings_gui.cpp
src/settings_type.h
src/squirrel.cpp
src/squirrel.hpp
--- a/src/ai/ai.cpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/ai/ai.cpp	Mon Jun 30 12:27:24 2008 +0000
@@ -133,6 +133,11 @@
 	InvalidateWindowData(WC_AI_DEBUG, 0, -1);
 }
 
+AIController *AI_GetPlayerController(PlayerID player)
+{
+	return _ai_player[player];
+}
+
 /**
  * Initialize some AI-related stuff.
  */
--- a/src/ai/ai.h	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/ai/ai.h	Mon Jun 30 12:27:24 2008 +0000
@@ -22,4 +22,6 @@
 
 void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2);
 
+class AIController *AI_GetPlayerController(PlayerID player);
+
 #endif /* AI_H */
--- a/src/ai/api/ai_controller.cpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/ai/api/ai_controller.cpp	Mon Jun 30 12:27:24 2008 +0000
@@ -7,6 +7,7 @@
 #include "../../player_func.h"
 #include "../../core/alloc_func.hpp"
 #include "../../string_func.h"
+#include "../../settings_type.h"
 #include "table/strings.h"
 
 #include <squirrel.h>
@@ -17,6 +18,7 @@
 #include "ai_controller.hpp"
 #include "../ai_threads.h"
 #include "../ai_info.hpp"
+#include "../ai.h"
 
 /* Convert all AI related classes to Squirrel data.
  * Note: this line a marker in squirrel_export.sh. Do not change! */
@@ -76,6 +78,8 @@
 		AILog::Warning("Sleep() value should be > 0. Assuming value 1.");
 		ticks = 1;
 	}
+	AIController *controller = AI_GetPlayerController(_current_player);
+	controller->engine->ResetOpcodeCounter();
 	AI_SuspendPlayer(_current_player, ticks);
 }
 
@@ -169,6 +173,11 @@
 	AIController::Print(error_msg, FS2OTTD(message));
 }
 
+static void SquirrelSuspendControl()
+{
+	AIController::Sleep(1);
+}
+
 AIController::AIController(const char *script, const char *class_name) :
 	tick(0),
 	engine(NULL),
@@ -205,6 +214,10 @@
 		this->engine->CallMethod(*this->SQ_instance, "constructor");
 	}
 
+	/* Set the maximum opcodes before suspend and suspend control */
+	this->engine->SetMaxOpcodes(_settings_game.ai.ai_max_opcode_till_suspend);
+	this->engine->SetSuspendControl(&SquirrelSuspendControl);
+
 	/* When that is done, start the Start() function */
 	this->engine->CallMethod(*this->SQ_instance, "Start");
 }
--- a/src/lang/english.txt	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/lang/english.txt	Mon Jun 30 12:27:24 2008 +0000
@@ -1149,6 +1149,7 @@
 
 STR_CONFIG_PATCHES_AINEW_ACTIVE                                 :{LTBLUE}Enable new AI (alpha): {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_AI_IN_MULTIPLAYER                            :{LTBLUE}Allow AIs in multiplayer (experimental): {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_AI_MAX_OPCODES                               :{LTBLUE}#opcodes before AI is suspended: {ORANGE}{STRING1}
 
 STR_CONFIG_PATCHES_SERVINT_TRAINS                               :{LTBLUE}Default service interval for trains: {ORANGE}{STRING1} days/%
 STR_CONFIG_PATCHES_SERVINT_TRAINS_DISABLED                      :{LTBLUE}Default service interval for trains: {ORANGE}disabled
--- a/src/settings.cpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/settings.cpp	Mon Jun 30 12:27:24 2008 +0000
@@ -1668,6 +1668,7 @@
 	    SDT_BOOL(GameSettings, ai.ai_disable_veh_roadveh,                                           0, 0, false,                    STR_CONFIG_PATCHES_AI_BUILDS_ROADVEH,      NULL),
 	    SDT_BOOL(GameSettings, ai.ai_disable_veh_aircraft,                                          0, 0, false,                    STR_CONFIG_PATCHES_AI_BUILDS_AIRCRAFT,     NULL),
 	    SDT_BOOL(GameSettings, ai.ai_disable_veh_ship,                                              0, 0, false,                    STR_CONFIG_PATCHES_AI_BUILDS_SHIPS,        NULL),
+	    SDT_VAR(GameSettings, ai.ai_max_opcode_till_suspend,  SLE_UINT32,                     0, NG, 4000,   100, 40000, 100, STR_CONFIG_PATCHES_AI_MAX_OPCODES,         NULL),
 
 	     SDT_VAR(GameSettings, vehicle.extend_vehicle_life,          SLE_UINT8,                     0, 0,     0,     0,     100, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, economy.dist_local_authority,         SLE_UINT8,                     0, 0,    20,     5,      60, 0, STR_NULL,                                  NULL),
--- a/src/settings_gui.cpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/settings_gui.cpp	Mon Jun 30 12:27:24 2008 +0000
@@ -667,6 +667,7 @@
 	"ai.ai_disable_veh_roadveh",
 	"ai.ai_disable_veh_aircraft",
 	"ai.ai_disable_veh_ship",
+	"ai.ai_max_opcode_till_suspend",
 };
 
 static const char *_patches_vehicles[] = {
--- a/src/settings_type.h	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/settings_type.h	Mon Jun 30 12:27:24 2008 +0000
@@ -157,6 +157,7 @@
 	bool   ai_disable_veh_roadveh;           ///< disable types for AI
 	bool   ai_disable_veh_aircraft;          ///< disable types for AI
 	bool   ai_disable_veh_ship;              ///< disable types for AI
+	uint32 ai_max_opcode_till_suspend;       ///< max opcode calls till the squirrel VM will be suspended
 };
 
 /** Settings related to the old pathfinder. */
--- a/src/squirrel.cpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/squirrel.cpp	Mon Jun 30 12:27:24 2008 +0000
@@ -85,6 +85,11 @@
 	return 0;
 }
 
+void Squirrel::SetSuspendControl(SQSuspendControlFunc func)
+{
+       sq_setsuspendcontrol(this->vm, func);
+}
+
 void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
 {
 	va_list arglist;
--- a/src/squirrel.hpp	Mon Jun 30 12:15:10 2008 +0000
+++ b/src/squirrel.hpp	Mon Jun 30 12:27:24 2008 +0000
@@ -155,6 +155,11 @@
 	void SetPrintFunction(SQPrintFunc *func) { this->print_func = func; }
 
 	/**
+	 * Set a custom suspend function.
+	 */
+	void SetSuspendControl(SQSuspendControlFunc func);
+
+	/**
 	 * Throw a Squirrel error that will be nicely displayed to the user.
 	 */
 	void ThrowError(const char *error) { sq_throwerror(this->vm, OTTD2FS(error)); }
@@ -163,6 +168,17 @@
 	 * Release a SQ object.
 	 */
 	void ReleaseObject(HSQOBJECT *ptr) { sq_release(this->vm, ptr); }
+
+	/**
+	 * Reset the internal SQ counter which counts the opcodes executed and
+	 *  forces SQ to pause if the counter overflows.
+	 */
+	void ResetOpcodeCounter() { sq_resetopcodecounter(this->vm); }
+
+	/**
+	 * Set the maximum of opcodes before the suspend handler is called.
+	 */
+	void SetMaxOpcodes(int32 opcodes) { sq_setmaxopcodes(this->vm, opcodes); }
 };
 
 #endif /* SQUIRREL_HPP */