Codechange: [Script] rework how compat-scripts work and are loaded (#13504)

- compat_NNN.nut files now only defines what is needed to downgrade from API NNN + 1 to NNN.
- Automatically load all required compatibility files based on the API version of the script, starting with the latest.
This commit is contained in:
Patric Stout
2025-02-09 16:04:04 +01:00
committed by GitHub
parent 2824e790ec
commit f60b3d7f79
42 changed files with 78 additions and 850 deletions

View File

@@ -24,8 +24,7 @@
*/
static bool CheckAPIVersion(const std::string &api_version)
{
static const std::set<std::string> versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" };
return versions.find(api_version) != versions.end();
return std::ranges::find(AIInfo::ApiVersions, api_version) != AIInfo::ApiVersions.end();
}
#if defined(_WIN32)

View File

@@ -15,6 +15,9 @@
/** All static information from an AI like name, version, etc. */
class AIInfo : public ScriptInfo {
public:
/* All valid AI API versions, in order. */
static constexpr std::initializer_list<std::string_view> ApiVersions{ "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" };
AIInfo();
/**

View File

@@ -54,7 +54,7 @@ void AIInstance::RegisterAPI()
/* Register all classes */
SQAI_RegisterAll(this->engine);
if (!this->LoadCompatibilityScripts(this->versionAPI, AI_DIR)) this->Died();
if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died();
}
void AIInstance::Died()

View File

@@ -22,8 +22,7 @@
*/
static bool CheckAPIVersion(const std::string &api_version)
{
static const std::set<std::string> versions = { "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" };
return versions.find(api_version) != versions.end();
return std::ranges::find(GameInfo::ApiVersions, api_version) != GameInfo::ApiVersions.end();
}
#if defined(_WIN32)

View File

@@ -15,6 +15,9 @@
/** All static information from an Game like name, version, etc. */
class GameInfo : public ScriptInfo {
public:
/* All valid GameScript API versions, in order. */
static constexpr std::initializer_list<std::string_view> ApiVersions{ "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" };
GameInfo();
/**

View File

@@ -50,7 +50,7 @@ void GameInstance::RegisterAPI()
RegisterGameTranslation(this->engine);
if (!this->LoadCompatibilityScripts(this->versionAPI, GAME_DIR)) this->Died();
if (!this->LoadCompatibilityScripts(GAME_DIR, GameInfo::ApiVersions)) this->Died();
}
int GameInstance::GetSetting(const std::string &name)

View File

@@ -116,9 +116,10 @@ void ScriptInstance::RegisterAPI()
squirrel_register_std(this->engine);
}
bool ScriptInstance::LoadCompatibilityScripts(const std::string &api_version, Subdirectory dir)
bool ScriptInstance::LoadCompatibilityScript(std::string_view api_version, Subdirectory dir)
{
std::string script_name = fmt::format("compat_{}.nut", api_version);
for (Searchpath sp : _valid_searchpaths) {
std::string buf = FioGetDirectory(sp, dir);
buf += script_name;
@@ -126,12 +127,30 @@ bool ScriptInstance::LoadCompatibilityScripts(const std::string &api_version, Su
if (this->engine->LoadScript(buf)) return true;
ScriptLog::Error("Failed to load API compatibility script");
ScriptLog::Error(fmt::format("Failed to load API compatibility script for {}", api_version));
Debug(script, 0, "Error compiling / running API compatibility script: {}", buf);
return false;
}
ScriptLog::Warning("API compatibility script not found");
ScriptLog::Warning(fmt::format("API compatibility script for {} not found", api_version));
return true;
}
bool ScriptInstance::LoadCompatibilityScripts(Subdirectory dir, std::span<const std::string_view> api_versions)
{
/* Don't try to load compatibility scripts for the current version. */
if (this->versionAPI == std::rbegin(api_versions)->data()) return true;
ScriptLog::Info(fmt::format("Downgrading API to be compatible with version {}", this->versionAPI));
/* Downgrade the API till we are the same version as the script. The last
* entry in the list is always the current version, so skip that one. */
for (auto it = std::rbegin(api_versions) + 1; it != std::rend(api_versions); ++it) {
if (!this->LoadCompatibilityScript(*it, dir)) return false;
if (*it == this->versionAPI) break;
}
return true;
}

View File

@@ -261,11 +261,11 @@ protected:
/**
* Load squirrel scripts to emulate an older API.
* @param api_version: API version to load scripts for
* @param dir Subdirectory to find the scripts in
* @return true iff script loading should proceed
* @param dir Subdirectory to find the scripts in.
* @param api_versions List of available versions of the script type.
* @return true iff script loading should proceed.
*/
bool LoadCompatibilityScripts(const std::string &api_version, Subdirectory dir);
bool LoadCompatibilityScripts(Subdirectory dir, std::span<const std::string_view> api_versions);
/**
* Tell the script it died.
@@ -302,6 +302,14 @@ private:
*/
bool CallLoad();
/**
* Load squirrel script for a specific version to emulate an older API.
* @param api_version: API version to load scripts for.
* @param dir Subdirectory to find the scripts in.
* @return true iff script loading should proceed.
*/
bool LoadCompatibilityScript(std::string_view api_version, Subdirectory dir);
/**
* Save one object (int / string / array / table) to the savegame.
* @param vm The virtual machine to get all the data from.