Changeset - r26641:01889b29735a
[Not reviewed]
master
0 4 0
Loïc Guilloux - 18 months ago 2022-12-15 20:43:07
glx22@users.noreply.github.com
Fix #10206: Disable scripts in intro game (#10241)
4 files changed with 11 insertions and 2 deletions:
0 comments (0 inline, 0 general)
src/ai/ai_instance.cpp
Show inline comments
 
@@ -16,96 +16,99 @@
 
#include "ai_config.hpp"
 
#include "ai_gui.hpp"
 
#include "ai.hpp"
 

	
 
#include "../script/script_storage.hpp"
 
#include "../script/script_cmd.h"
 
#include "ai_info.hpp"
 
#include "ai_instance.hpp"
 

	
 
/* Manually include the Text glue. */
 
#include "../script/api/template/template_text.hpp.sq"
 

	
 
/* Convert all AI related classes to Squirrel data. */
 
#include "../script/api/ai/ai_includes.hpp"
 

	
 
#include "../company_base.h"
 
#include "../company_func.h"
 

	
 
#include "../safeguards.h"
 

	
 
AIInstance::AIInstance() :
 
	ScriptInstance("AI")
 
{}
 

	
 
void AIInstance::Initialize(AIInfo *info)
 
{
 
	this->versionAPI = info->GetAPIVersion();
 

	
 
	/* Register the AIController (including the "import" command) */
 
	SQAIController_Register(this->engine);
 

	
 
	ScriptInstance::Initialize(info->GetMainScript(), info->GetInstanceName(), _current_company);
 
}
 

	
 
void AIInstance::RegisterAPI()
 
{
 
	ScriptInstance::RegisterAPI();
 

	
 
	/* Register all classes */
 
	SQAI_RegisterAll(this->engine);
 

	
 
	if (!this->LoadCompatibilityScripts(this->versionAPI, AI_DIR)) this->Died();
 
}
 

	
 
void AIInstance::Died()
 
{
 
	ScriptInstance::Died();
 

	
 
	/* Intro is not supposed to use AI, but it may have 'dummy' AI which instant dies. */
 
	if (_game_mode == GM_MENU) return;
 

	
 
	ShowAIDebugWindow(_current_company);
 

	
 
	const AIInfo *info = AIConfig::GetConfig(_current_company, AIConfig::SSS_FORCE_GAME)->GetInfo();
 
	if (info != nullptr) {
 
		ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING);
 

	
 
		if (info->GetURL() != nullptr) {
 
			ScriptLog::Info("Please report the error to the following URL:");
 
			ScriptLog::Info(info->GetURL());
 
		}
 
	}
 
}
 

	
 
void AIInstance::LoadDummyScript()
 
{
 
	ScriptAllocatorScope alloc_scope(this->engine);
 
	extern void Script_CreateDummy(HSQUIRRELVM vm, StringID string, const char *type);
 
	Script_CreateDummy(this->engine->GetVM(), STR_ERROR_AI_NO_AI_FOUND, "AI");
 
}
 

	
 
int AIInstance::GetSetting(const char *name)
 
{
 
	return AIConfig::GetConfig(_current_company)->GetSetting(name);
 
}
 

	
 
ScriptInfo *AIInstance::FindLibrary(const char *library, int version)
 
{
 
	return (ScriptInfo *)AI::FindLibrary(library, version);
 
}
 

	
 
/**
 
 * DoCommand callback function for all commands executed by AIs.
 
 * @param cmd cmd as given to DoCommandPInternal.
 
 * @param result The result of the command.
 
 * @param tile The tile on which the command was executed.
 
 * @param data Command data as given to Command<>::Post.
 
 * @param result_data Additional returned data from the command.
 
 */
 
void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data)
 
{
 
	/*
 
	 * The company might not exist anymore. Check for this.
 
	 * The command checks are not useful since this callback
 
	 * is also called when the command fails, which is does
 
	 * when the company does not exist anymore.
 
	 */
 
	const Company *c = Company::GetIfValid(_current_company);
 
	if (c == nullptr || c->ai_instance == nullptr) return;
src/ai/ai_scanner.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file ai_scanner.cpp allows scanning AI scripts */
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../network/network.h"
 
#include "../openttd.h"
 
#include "../core/random_func.hpp"
 

	
 
#include "../script/squirrel_class.hpp"
 
#include "ai_info.hpp"
 
#include "ai_scanner.hpp"
 

	
 
#include "../safeguards.h"
 

	
 

	
 
AIScannerInfo::AIScannerInfo() :
 
	ScriptScanner(),
 
	info_dummy(nullptr)
 
{
 
}
 

	
 
void AIScannerInfo::Initialize()
 
{
 
	ScriptScanner::Initialize("AIScanner");
 

	
 
	ScriptAllocatorScope alloc_scope(this->engine);
 

	
 
	/* Create the dummy AI */
 
	this->main_script = "%_dummy";
 
	extern void Script_CreateDummyInfo(HSQUIRRELVM vm, const char *type, const char *dir);
 
	Script_CreateDummyInfo(this->engine->GetVM(), "AI", "ai");
 
}
 

	
 
void AIScannerInfo::SetDummyAI(class AIInfo *info)
 
{
 
	this->info_dummy = info;
 
}
 

	
 
AIScannerInfo::~AIScannerInfo()
 
{
 
	delete this->info_dummy;
 
}
 

	
 
void AIScannerInfo::GetScriptName(ScriptInfo *info, char *name, const char *last)
 
{
 
	seprintf(name, last, "%s", info->GetName());
 
}
 

	
 
void AIScannerInfo::RegisterAPI(class Squirrel *engine)
 
{
 
	AIInfo::RegisterAPI(engine);
 
}
 

	
 
AIInfo *AIScannerInfo::SelectRandomAI() const
 
{
 
	if (_game_mode == GM_MENU) {
 
		Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI.");
 
		return this->info_dummy;
 
	}
 

	
 
	uint num_random_ais = 0;
 
	for (const auto &item : info_single_list) {
 
		AIInfo *i = static_cast<AIInfo *>(item.second);
 
		if (i->UseAsRandomAI()) num_random_ais++;
 
	}
 

	
 
	if (num_random_ais == 0) {
 
		Debug(script, 0, "No suitable AI found, loading 'dummy' AI.");
 
		return this->info_dummy;
 
	}
 

	
 
	/* Find a random AI */
 
	uint pos;
 
	if (_networking) {
 
		pos = InteractiveRandomRange(num_random_ais);
 
	} else {
 
		pos = RandomRange(num_random_ais);
 
	}
 

	
 
	/* Find the Nth item from the array */
 
	ScriptInfoList::const_iterator it = this->info_single_list.begin();
 

	
 
#define GetAIInfo(it) static_cast<AIInfo *>((*it).second)
 
	while (!GetAIInfo(it)->UseAsRandomAI()) it++;
 
	for (; pos > 0; pos--) {
 
		it++;
 
		while (!GetAIInfo(it)->UseAsRandomAI()) it++;
 
	}
 
	return GetAIInfo(it);
 
#undef GetAIInfo
 
}
 

	
 
AIInfo *AIScannerInfo::FindInfo(const char *nameParam, int versionParam, bool force_exact_match)
 
{
 
	if (this->info_list.size() == 0) return nullptr;
 
	if (nameParam == nullptr) return nullptr;
 

	
 
	char ai_name[1024];
 
	strecpy(ai_name, nameParam, lastof(ai_name));
 
	strtolower(ai_name);
 

	
 
	if (versionParam == -1) {
 
		/* We want to load the latest version of this AI; so find it */
 
		if (this->info_single_list.find(ai_name) != this->info_single_list.end()) return static_cast<AIInfo *>(this->info_single_list[ai_name]);
 
		return nullptr;
 
	}
 

	
 
	if (force_exact_match) {
src/saveload/ai_sl.cpp
Show inline comments
 
@@ -32,97 +32,97 @@ static const SaveLoad _ai_company_desc[]
 
	   SLEG_SSTR("name",      _ai_saveload_name,         SLE_STR),
 
	   SLEG_SSTR("settings",  _ai_saveload_settings,     SLE_STR),
 
	SLEG_CONDVAR("version",   _ai_saveload_version,   SLE_UINT32, SLV_108, SL_MAX_VERSION),
 
	SLEG_CONDVAR("is_random", _ai_saveload_is_random,   SLE_BOOL, SLV_136, SL_MAX_VERSION),
 
};
 

	
 
static void SaveReal_AIPL(int *index_ptr)
 
{
 
	CompanyID index = (CompanyID)*index_ptr;
 
	AIConfig *config = AIConfig::GetConfig(index);
 

	
 
	if (config->HasScript()) {
 
		_ai_saveload_name = config->GetName();
 
		_ai_saveload_version = config->GetVersion();
 
	} else {
 
		/* No AI is configured for this so store an empty string as name. */
 
		_ai_saveload_name.clear();
 
		_ai_saveload_version = -1;
 
	}
 

	
 
	_ai_saveload_is_random = config->IsRandom();
 
	_ai_saveload_settings = config->SettingsToString();
 

	
 
	SlObject(nullptr, _ai_company_desc);
 
	/* If the AI was active, store its data too */
 
	if (Company::IsValidAiID(index)) AI::Save(index);
 
}
 

	
 
struct AIPLChunkHandler : ChunkHandler {
 
	AIPLChunkHandler() : ChunkHandler('AIPL', CH_TABLE) {}
 

	
 
	void Load() const override
 
	{
 
		const std::vector<SaveLoad> slt = SlCompatTableHeader(_ai_company_desc, _ai_company_sl_compat);
 

	
 
		/* Free all current data */
 
		for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 
			AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr);
 
		}
 

	
 
		CompanyID index;
 
		while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) {
 
			if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs");
 

	
 
			_ai_saveload_is_random = false;
 
			_ai_saveload_version = -1;
 
			SlObject(nullptr, slt);
 

	
 
			if (_networking && !_network_server) {
 
			if (_game_mode == GM_MENU || (_networking && !_network_server)) {
 
				if (Company::IsValidAiID(index)) AIInstance::LoadEmpty();
 
				continue;
 
			}
 

	
 
			AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME);
 
			if (_ai_saveload_name.empty()) {
 
				/* A random AI. */
 
				config->Change(nullptr, -1, false, true);
 
			} else {
 
				config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random);
 
				if (!config->HasScript()) {
 
					/* No version of the AI available that can load the data. Try to load the
 
					 * latest version of the AI instead. */
 
					config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random);
 
					if (!config->HasScript()) {
 
						if (_ai_saveload_name.compare("%_dummy") != 0) {
 
							Debug(script, 0, "The savegame has an AI by the name '{}', version {} which is no longer available.", _ai_saveload_name, _ai_saveload_version);
 
							Debug(script, 0, "A random other AI will be loaded in its place.");
 
						} else {
 
							Debug(script, 0, "The savegame had no AIs available at the time of saving.");
 
							Debug(script, 0, "A random available AI will be loaded now.");
 
						}
 
					} else {
 
						Debug(script, 0, "The savegame has an AI by the name '{}', version {} which is no longer available.", _ai_saveload_name, _ai_saveload_version);
 
						Debug(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible.");
 
					}
 
					/* Make sure the AI doesn't get the saveload data, as it was not the
 
					 *  writer of the saveload data in the first place */
 
					_ai_saveload_version = -1;
 
				}
 
			}
 

	
 
			config->StringToSettings(_ai_saveload_settings);
 

	
 
			/* Start the AI directly if it was active in the savegame */
 
			if (Company::IsValidAiID(index)) {
 
				AI::StartNew(index, false);
 
				AI::Load(index, _ai_saveload_version);
 
			}
 
		}
 
	}
 

	
 
	void Save() const override
 
	{
 
		SlTableHeader(_ai_company_desc);
 

	
 
		for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
 
			SlSetArrayIndex(i);
src/saveload/game_sl.cpp
Show inline comments
 
@@ -24,97 +24,97 @@
 

	
 
static std::string _game_saveload_name;
 
static int         _game_saveload_version;
 
static std::string _game_saveload_settings;
 
static bool        _game_saveload_is_random;
 

	
 
static const SaveLoad _game_script_desc[] = {
 
	   SLEG_SSTR("name",      _game_saveload_name,         SLE_STR),
 
	   SLEG_SSTR("settings",  _game_saveload_settings,     SLE_STR),
 
	    SLEG_VAR("version",   _game_saveload_version,   SLE_UINT32),
 
	    SLEG_VAR("is_random", _game_saveload_is_random,   SLE_BOOL),
 
};
 

	
 
static void SaveReal_GSDT(int *index_ptr)
 
{
 
	GameConfig *config = GameConfig::GetConfig();
 

	
 
	if (config->HasScript()) {
 
		_game_saveload_name = config->GetName();
 
		_game_saveload_version = config->GetVersion();
 
	} else {
 
		/* No GameScript is configured for this so store an empty string as name. */
 
		_game_saveload_name.clear();
 
		_game_saveload_version = -1;
 
	}
 

	
 
	_game_saveload_is_random = config->IsRandom();
 
	_game_saveload_settings = config->SettingsToString();
 

	
 
	SlObject(nullptr, _game_script_desc);
 
	Game::Save();
 
}
 

	
 
struct GSDTChunkHandler : ChunkHandler {
 
	GSDTChunkHandler() : ChunkHandler('GSDT', CH_TABLE) {}
 

	
 
	void Load() const override
 
	{
 
		const std::vector<SaveLoad> slt = SlCompatTableHeader(_game_script_desc, _game_script_sl_compat);
 

	
 
		/* Free all current data */
 
		GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr);
 

	
 
		if (SlIterateArray() == -1) return;
 

	
 
		_game_saveload_version = -1;
 
		SlObject(nullptr, slt);
 

	
 
		if (_networking && !_network_server) {
 
		if (_game_mode == GM_MENU || (_networking && !_network_server)) {
 
			GameInstance::LoadEmpty();
 
			if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs");
 
			return;
 
		}
 

	
 
		GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME);
 
		if (!_game_saveload_name.empty()) {
 
			config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random);
 
			if (!config->HasScript()) {
 
				/* No version of the GameScript available that can load the data. Try to load the
 
				 * latest version of the GameScript instead. */
 
				config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random);
 
				if (!config->HasScript()) {
 
					if (_game_saveload_name.compare("%_dummy") != 0) {
 
						Debug(script, 0, "The savegame has an GameScript by the name '{}', version {} which is no longer available.", _game_saveload_name, _game_saveload_version);
 
						Debug(script, 0, "This game will continue to run without GameScript.");
 
					} else {
 
						Debug(script, 0, "The savegame had no GameScript available at the time of saving.");
 
						Debug(script, 0, "This game will continue to run without GameScript.");
 
					}
 
				} else {
 
					Debug(script, 0, "The savegame has an GameScript by the name '{}', version {} which is no longer available.", _game_saveload_name, _game_saveload_version);
 
					Debug(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible.");
 
				}
 
				/* Make sure the GameScript doesn't get the saveload data, as it was not the
 
				 *  writer of the saveload data in the first place */
 
				_game_saveload_version = -1;
 
			}
 
		}
 

	
 
		config->StringToSettings(_game_saveload_settings);
 

	
 
		/* Start the GameScript directly if it was active in the savegame */
 
		Game::StartNew();
 
		Game::Load(_game_saveload_version);
 

	
 
		if (SlIterateArray() != -1) SlErrorCorrupt("Too many GameScript configs");
 
	}
 

	
 
	void Save() const override
 
	{
 
		SlTableHeader(_game_script_desc);
 
		SlSetArrayIndex(0);
 
		SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr);
 
	}
 
};
 

	
 
extern GameStrings *_current_data;
0 comments (0 inline, 0 general)