(svn r13512) [NoAI] -Fix: don't load a library over and over, but keep track of which libraries we have loaded (per AI) and re-use it where possible (reduces memory-footprint)
[NoAI] -Fix: change the fake-library-name-counter to a per AI value, not global
[NoAI] -Fix: Load the script inside the thread, not in the main thread. This avoids unneeded error-handling
/* $Id$ */
/** @file ai.cpp Handles the communication between the AI layer and the OpenTTD core */
#include "../stdafx.h"
#include "../openttd.h"
#include "../variables.h"
#include "../command_func.h"
#include "../network/network.h"
#include "../core/alloc_func.hpp"
#include "../settings_type.h"
#include "../player_base.h"
#include "../player_func.h"
#include "../debug.h"
#ifndef NO_THREADS
#include <squirrel.h>
#include "../squirrel.hpp"
#include "../squirrel_helper.hpp"
#include "../squirrel_class.hpp"
#include "../squirrel_std.hpp"
#include "api/ai_controller.hpp"
#include "ai_squirrel.hpp"
#include "ai_info.hpp"
#include "ai.h"
#include "ai_threads.h"
#include "api/ai_base.hpp"
#include "../signal_func.h"
#include "../window_func.h"
static AIController *_ai_player[MAX_PLAYERS];
static AIInfo *_ai_info[MAX_PLAYERS];
static AISquirrel *_ai_squirrel = NULL;
static uint _ai_frame_counter;
static bool _ai_enabled;
static char *_forced_ai_name = NULL;
/**
* The gameloop for AIs.
* Handles one tick for all the AIs.
*/
void AI_RunGameLoop()
{
/* Don't do anything if ai is disabled */
if (!_ai_enabled) return;
/* Don't do anything if we are a network-client, or the AI has been disabled */
if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
/* New tick */
_ai_frame_counter++;
/* Make sure the AI follows the difficulty rule.. */
assert(_settings_game.difficulty.competitor_speed <= 4);
if ((_ai_frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
/* Check for AI-client (so joining a network with an AI) */
if (!_networking || _network_server) {
/* Check if we want to run AIs (server or SP only) */
const Player *p;
FOR_ALL_PLAYERS(p) {
if (p->is_active && p->is_ai) {
/* Run the script */
if (_ai_player[p->index] == NULL) continue;
_current_player = p->index;
AI_RunTick(p->index);
}
}
}
_current_player = OWNER_NONE;
}
void AI_Event(PlayerID player, AIEvent *event)
{
if (player >= MAX_PLAYERS || !GetPlayer(player)->is_active || !GetPlayer(player)->is_ai) return;
DEBUG(ai, 5, "Event (%d) for player %d\n", event->GetEventType(), player);
PlayerID old_player = _current_player;
_current_player = player;
AIEventController::InsertEvent(event);
_current_player = old_player;
}
/**
* A new AI sees the day of light. You can do here what ever you think is needed.
* @return True if the AI is started.
*/
bool AI_StartNewAI(PlayerID player)
{
assert(IsValidPlayer(player));
if (!_ai_enabled) return false;
if (_networking && !_network_server) return false;
AIInfo *info;
if (_forced_ai_name == NULL) {
info = _ai_squirrel->SelectRandomAI();
} else {
info = _ai_squirrel->SelectAI(_forced_ai_name);
}
if (info == NULL) {
if (_forced_ai_name == NULL) {
DEBUG(ai, 0, "Couldn't find a suitable AI to start for company '%d'", player);
} else {
DEBUG(ai, 0, "The AI named \"%s\" is unknown or not suitable", _forced_ai_name);
}
return false;
}
_current_player = player;
_ai_info[player] = info;
AIObject::ResetInternalPlayerData();
_ai_player[player] = info->CreateInstance();
AI_StartPlayer(player, _ai_player[player]);
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
return true;
}
/**
* This AI player died. Give it some chance to make a final puf.
*/
void AI_PlayerDied(PlayerID player)
{
if (!_ai_enabled) return;
_current_player = player;
AI_StopPlayer(player);
/* Called if this AI died */
delete _ai_player[player];
_ai_player[player] = NULL;
_ai_info[player] = NULL;
InvalidateWindowData(WC_AI_DEBUG, 0, -1);
}
/**
* Initialize some AI-related stuff.
*/
void AI_Initialize()
{
/* First, make sure all AIs are DEAD! */
AI_Uninitialize();
memset(&_ai_player, 0, sizeof(_ai_player));
memset(&_ai_info, 0, sizeof(_ai_info));
_ai_frame_counter = 0;
_ai_enabled = true;
/* Create the Squirrel collector, which scans for all AIs available */
assert(_ai_squirrel == NULL);
_ai_squirrel = new AISquirrel();
}
/**
* Forces the given AI to be used for all players.
* Pass NULL to use a random AI.
* @param forced_ai the AI to force.
*/
void AI_ForceAI(const char *forced_ai)
{
free(_forced_ai_name);
_forced_ai_name = (forced_ai == NULL) ? NULL : strdup(forced_ai);
}
char *AI_GetConsoleList(char *p, const char *last)
{
return _ai_squirrel->GetAIConsoleList(p, last);
}
AIInfo *AI_GetPlayerInfo(PlayerID player)
{
return _ai_info[player];
}
/**
* Kill all AIs.
*/
void AI_KillAll()
{
const Player* p;
FOR_ALL_PLAYERS(p) {
if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
}
}
/**
* Deinitializer for AI-related stuff.
*/
void AI_Uninitialize()
{
AI_KillAll();
delete _ai_squirrel;
_ai_squirrel = NULL;
}
/**
* Is it allowed to start a new AI.
* This function checks some boundries to see if we should launch a new AI.
* @return True if we can start a new AI.
*/
bool AI_AllowNewAI()
{
/* If disabled, no AI */
if (!_ai_enabled) return false;
/* If in network, but no server, no AI */
if (_networking && !_network_server) return false;
/* If in network, and server, possible AI */
if (_networking && _network_server) {
/* Do we want AIs in multiplayer? */
if (!_settings_game.ai.ai_in_multiplayer) return false;
}
return true;
}
bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
{
return _ai_squirrel->ImportLibrary(library, class_name, version, vm, _ai_player[_current_player]);
}
void AI_Rescan()
{
_ai_squirrel->RescanAIDir();
}
#else /* NO_THREADS */
/* Stub functions for when we do not have threads. */
void AI_StartNewAI(PlayerID player) { DEBUG(ai, 0, "Threading is disabled and therefor the AI too."); }
void AI_PlayerDied(PlayerID player) {}
void AI_RunGameLoop() {}
void AI_Event(PlayerID player, AIEvent *event) {}
void AI_Initialize() {}
void AI_Uninitialize() {}
bool AI_AllowNewAI() { return false; }
void AI_ForceAI(const char *forced_ai) {}
char *AI_GetConsoleList(char *p, const char *last) { return p; }
AIInfo *AI_GetPlayerInfo(PlayerID player) { return NULL; }
bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm) { return false; }
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) {}
void AI_Rescan() {}
#endif /* NO_THREADS */