Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-09-28 02:33:49 +05:00
926 changed files with 37901 additions and 27368 deletions
+6 -6
View File
@@ -43,23 +43,23 @@ foreach(API "ai;AI" "game;GS" "template;Template")
if("${SCRIPT_API_FILE}" MATCHES ".*script_controller.*")
continue()
endif()
get_filename_component(SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE}" NAME)
get_filename_component(SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE}" NAME_WE)
string(REPLACE "script_" "${APILC}_" SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE_NAME}")
set(SCRIPT_API_BINARY_FILE "${CMAKE_BINARY_DIR}/generated/script/api/${APILC}/${SCRIPT_API_FILE_NAME}.sq")
set(SCRIPT_API_BINARY_FILE "${CMAKE_BINARY_DIR}/generated/script/api/${APILC}/${SCRIPT_API_FILE_NAME}.sq.hpp")
add_custom_command_timestamp(OUTPUT ${SCRIPT_API_BINARY_FILE}
COMMAND ${CMAKE_COMMAND}
-DSCRIPT_API_SOURCE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/squirrel_export.hpp.sq.in
-DSCRIPT_API_SOURCE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/squirrel_export.sq.hpp.in
-DSCRIPT_API_BINARY_FILE=${SCRIPT_API_BINARY_FILE}
-DSCRIPT_API_FILE=${SCRIPT_API_FILE}
-DAPIUC=${APIUC}
-DAPILC=${APILC}
-P ${CMAKE_SOURCE_DIR}/cmake/scripts/SquirrelExport.cmake
MAIN_DEPENDENCY ${SCRIPT_API_FILE}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/squirrel_export.hpp.sq.in
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/squirrel_export.sq.hpp.in
${CMAKE_SOURCE_DIR}/cmake/scripts/SquirrelExport.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating ${APILC}/${SCRIPT_API_FILE_NAME}.sq"
COMMENT "Generating ${APILC}/${SCRIPT_API_FILE_NAME}.sq.hpp"
)
list(APPEND SCRIPT_${APIUC}_BINARY_FILES ${SCRIPT_API_BINARY_FILE})
endforeach()
@@ -73,7 +73,7 @@ foreach(API "ai;AI" "game;GS" "template;Template")
)
if(NOT "${APILC}" STREQUAL "template")
list(APPEND SCRIPT_${APIUC}_BINARY_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${APILC}/${APILC}_controller.hpp.sq")
list(APPEND SCRIPT_${APIUC}_BINARY_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${APILC}/${APILC}_controller.sq.hpp")
set(INCLUDES_BINARY_FILE "${CMAKE_BINARY_DIR}/generated/script/api/${APILC}/${APILC}_includes.hpp")
set(API_FILES "${CMAKE_CURRENT_BINARY_DIR}/${APILC}.files")
file(GENERATE OUTPUT ${API_FILES} CONTENT "${SCRIPT_${APIUC}_BINARY_FILES}")
@@ -7,24 +7,24 @@
#include "../script_controller.hpp"
template <> SQInteger PushClassName<ScriptController, ScriptType::AI>(HSQUIRRELVM vm) { sq_pushstring(vm, "AIController", -1); return 1; }
template <> SQInteger PushClassName<ScriptController, ScriptType::AI>(HSQUIRRELVM vm) { sq_pushstring(vm, "AIController"); return 1; }
void SQAIController_Register(Squirrel *engine)
void SQAIController_Register(Squirrel &engine)
{
DefSQClass<ScriptController, ScriptType::AI> SQAIController("AIController");
SQAIController.PreRegister(engine);
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetTick, "GetTick", 1, ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetOpsTillSuspend, "GetOpsTillSuspend", 1, ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::SetCommandDelay, "SetCommandDelay", 2, ".i");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Sleep, "Sleep", 2, ".i");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Break, "Break", 2, ".s");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetSetting, "GetSetting", 2, ".s");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetVersion, "GetVersion", 1, ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Print, "Print", 3, ".bs");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetTick, "GetTick", ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetOpsTillSuspend, "GetOpsTillSuspend", ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::SetCommandDelay, "SetCommandDelay", ".i");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Sleep, "Sleep", ".i");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Break, "Break", ".s");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetSetting, "GetSetting", ".s");
SQAIController.DefSQStaticMethod(engine, &ScriptController::GetVersion, "GetVersion", ".");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Print, "Print", ".bs");
SQAIController.PostRegister(engine);
/* Register the import statement to the global scope */
SQAIController.DefSQStaticMethod(engine, &ScriptController::Import, "import", 4, ".ssi");
SQAIController.DefSQStaticMethod(engine, &ScriptController::Import, "import", ".ssi");
}
+4
View File
@@ -27,11 +27,15 @@
* \li AICargo::CC_NON_POURABLE
* \li AICargo::CC_POTABLE
* \li AICargo::CC_NON_POTABLE
* \li AIVehicleList_Waypoint
* \li AIError::ERR_BRIDGE_TOO_LOW
*
* Other changes:
* \li AIBridge::GetBridgeID renamed to AIBridge::GetBridgeType
* \li AIWaypoint::GetWaypointID now returns the StationID of any type of waypoint
* \li AIList instances can now be saved
* \li AIVehicleList_Station accepts an optional AIVehicle::VehicleType parameter
* \li AIList instances can now be cloned
*
* \b 14.0
*
+3 -3
View File
@@ -80,7 +80,7 @@ BEGIN {
/^( *)class/ {
if (cls_level == 0) {
if (api_selected == "") {
print "Class '"$2"' has no @api. It won't be published to any API." > "/dev/stderr"
printf "%s:%d: %s\n", FILENAME, NR, "Class '"$2"' has no @api. It won't be published to any API." > "/dev/stderr"
api_selected = "false"
}
public = "false"
@@ -105,7 +105,7 @@ BEGIN {
}
api_selected = ""
} else {
print "Classes nested too deep" > "/dev/stderr"
printf "%s:%d: %s\n", FILENAME, NR, "Classes nested too deep" > "/dev/stderr"
exit 1
}
cls_level++
@@ -279,7 +279,7 @@ BEGIN {
}
if (match($0, "~")) {
if (api_selected != "") {
print "Destructor for '"cls"' has @api. Tag ignored." > "/dev/stderr"
printf "%s:%d: %s\n", FILENAME, NR, "Destructor for '"cls"' has @api. Tag ignored." > "/dev/stderr"
api_selected = ""
}
next
@@ -7,24 +7,24 @@
#include "../script_controller.hpp"
template <> SQInteger PushClassName<ScriptController, ScriptType::GS>(HSQUIRRELVM vm) { sq_pushstring(vm, "GSController", -1); return 1; }
template <> SQInteger PushClassName<ScriptController, ScriptType::GS>(HSQUIRRELVM vm) { sq_pushstring(vm, "GSController"); return 1; }
void SQGSController_Register(Squirrel *engine)
void SQGSController_Register(Squirrel &engine)
{
DefSQClass<ScriptController, ScriptType::GS> SQGSController("GSController");
SQGSController.PreRegister(engine);
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetTick, "GetTick", 1, ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetOpsTillSuspend, "GetOpsTillSuspend", 1, ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::SetCommandDelay, "SetCommandDelay", 2, ".i");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Sleep, "Sleep", 2, ".i");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Break, "Break", 2, ".s");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetSetting, "GetSetting", 2, ".s");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetVersion, "GetVersion", 1, ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Print, "Print", 3, ".bs");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetTick, "GetTick", ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetOpsTillSuspend, "GetOpsTillSuspend", ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::SetCommandDelay, "SetCommandDelay", ".i");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Sleep, "Sleep", ".i");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Break, "Break", ".s");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetSetting, "GetSetting", ".s");
SQGSController.DefSQStaticMethod(engine, &ScriptController::GetVersion, "GetVersion", ".");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Print, "Print", ".bs");
SQGSController.PostRegister(engine);
/* Register the import statement to the global scope */
SQGSController.DefSQStaticMethod(engine, &ScriptController::Import, "import", 4, ".ssi");
SQGSController.DefSQStaticMethod(engine, &ScriptController::Import, "import", ".ssi");
}
+5
View File
@@ -27,11 +27,16 @@
* \li GSCargo::CC_NON_POURABLE
* \li GSCargo::CC_POTABLE
* \li GSCargo::CC_NON_POTABLE
* \li GSVehicleList_Waypoint
* \li GSBaseStation::GetOwner
* \li GSError:ERR_BRIDGE_TOO_LOW
*
* Other changes:
* \li GSBridge::GetBridgeID renamed to GSBridge::GetBridgeType
* \li GSWaypoint::GetWaypointID now returns the StationID of any type of waypoint
* \li GSList instances can now be saved
* \li GSVehicleList_Station accepts an optional GSVehicle::VehicleType parameter
* \li GSList instances can now be cloned
*
* \b 14.0
*
+6 -6
View File
@@ -45,10 +45,10 @@ bool ScriptAdminMakeJSON(nlohmann::json &json, HSQUIRRELVM vm, SQInteger index,
}
case OT_STRING: {
const SQChar *buf;
sq_getstring(vm, index, &buf);
std::string_view view;
sq_getstring(vm, index, view);
json = std::string(buf);
json = view;
return true;
}
@@ -78,9 +78,9 @@ bool ScriptAdminMakeJSON(nlohmann::json &json, HSQUIRRELVM vm, SQInteger index,
sq_pushnull(vm);
while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
sq_tostring(vm, -2);
const SQChar *buf;
sq_getstring(vm, -1, &buf);
std::string key = std::string(buf);
std::string_view view;
sq_getstring(vm, -1, view);
std::string key{view};
sq_pop(vm, 1);
nlohmann::json value;
+2 -2
View File
@@ -139,7 +139,7 @@
if (_settings_game.economy.station_noise_level) {
uint dist;
const auto &layout = as->layouts[0];
AirportGetNearestTown(as, layout.rotation, tile, AirportTileTableIterator(layout.tiles.data(), tile), dist);
AirportGetNearestTown(as, layout.rotation, tile, AirportTileTableIterator(layout.tiles, tile), dist);
return GetAirportNoiseLevelForDistance(as, dist);
}
@@ -156,7 +156,7 @@
uint dist;
const auto &layout = as->layouts[0];
return AirportGetNearestTown(as, layout.rotation, tile, AirportTileTableIterator(layout.tiles.data(), tile), dist)->index;
return AirportGetNearestTown(as, layout.rotation, tile, AirportTileTableIterator(layout.tiles, tile), dist)->index;
}
/* static */ SQInteger ScriptAirport::GetMaintenanceCostFactor(AirportType type)
+1 -1
View File
@@ -147,7 +147,7 @@ public:
* @exception ScriptError::ERR_AREA_NOT_CLEAR
* @exception ScriptError::ERR_FLAT_LAND_REQUIRED
* @exception ScriptError::ERR_LOCAL_AUTHORITY_REFUSES
* @exception ScriptStation::ERR_STATION_TOO_LARGE
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @return Whether the airport has been/can be build or not.
*/
+1 -1
View File
@@ -49,7 +49,7 @@ void ScriptAsyncMode::FinalRelease()
{
if (this->GetDoCommandAsyncModeInstance() != this) {
/* Ignore this error if the script is not alive. */
if (ScriptObject::GetActiveInstance()->IsAlive()) {
if (ScriptObject::GetActiveInstance().IsAlive()) {
throw Script_FatalError("Asyncmode object was removed while it was not the latest *Mode object created.");
}
}
+7 -1
View File
@@ -28,11 +28,17 @@
return st != nullptr && (st->owner == ScriptObject::GetCompany() || ScriptCompanyMode::IsDeity() || st->owner == OWNER_NONE);
}
/* static */ ScriptCompany::CompanyID ScriptBaseStation::GetOwner(StationID station_id)
{
if (!IsValidBaseStation(station_id)) return ScriptCompany::COMPANY_INVALID;
return ScriptCompany::ToScriptCompanyID(::BaseStation::Get(station_id)->owner);
}
/* static */ std::optional<std::string> ScriptBaseStation::GetName(StationID station_id)
{
if (!IsValidBaseStation(station_id)) return std::nullopt;
return ::StrMakeValid(::GetString(::Station::IsValidID(station_id) ? STR_STATION_NAME : STR_WAYPOINT_NAME, station_id));
return ::StrMakeValid(::GetString(::Station::IsValidID(station_id) ? STR_STATION_NAME : STR_WAYPOINT_NAME, station_id), {});
}
/* static */ bool ScriptBaseStation::SetName(StationID station_id, Text *name)
+10
View File
@@ -11,6 +11,7 @@
#define SCRIPT_BASESTATION_HPP
#include "script_text.hpp"
#include "script_company.hpp"
#include "script_date.hpp"
#include "../../station_type.h"
@@ -32,6 +33,15 @@ public:
*/
static bool IsValidBaseStation(StationID station_id);
/**
* Get the owner of a basestation.
* @param station_id The basestation to get the owner of.
* @pre IsValidBaseStation(station_id).
* @return The owner the basestation has.
* @api -ai
*/
static ScriptCompany::CompanyID GetOwner(StationID station_id);
/**
* Get the name of a basestation.
* @param station_id The basestation to get the name of.
+3 -3
View File
@@ -43,7 +43,7 @@
* Helper function to connect a just built bridge to nearby roads.
* @param instance The script instance we have to built the road for.
*/
static void _DoCommandReturnBuildBridge2(class ScriptInstance *instance)
static void _DoCommandReturnBuildBridge2(class ScriptInstance &instance)
{
if (!ScriptBridge::_BuildBridgeRoad2()) {
ScriptInstance::DoCommandReturn(instance);
@@ -59,7 +59,7 @@ static void _DoCommandReturnBuildBridge2(class ScriptInstance *instance)
* Helper function to connect a just built bridge to nearby roads.
* @param instance The script instance we have to built the road for.
*/
static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance)
static void _DoCommandReturnBuildBridge1(class ScriptInstance &instance)
{
if (!ScriptBridge::_BuildBridgeRoad1()) {
ScriptInstance::DoCommandReturn(instance);
@@ -135,7 +135,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance)
EnforcePrecondition(std::nullopt, vehicle_type == ScriptVehicle::VT_ROAD || vehicle_type == ScriptVehicle::VT_RAIL || vehicle_type == ScriptVehicle::VT_WATER);
if (!IsValidBridge(bridge_type)) return std::nullopt;
return ::StrMakeValid(::GetString(vehicle_type == ScriptVehicle::VT_WATER ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : ::GetBridgeSpec(bridge_type)->transport_name[vehicle_type]));
return ::StrMakeValid(::GetString(vehicle_type == ScriptVehicle::VT_WATER ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : ::GetBridgeSpec(bridge_type)->transport_name[vehicle_type]), {});
}
/* static */ SQInteger ScriptBridge::GetMaxSpeed(BridgeType bridge_type)
+1 -1
View File
@@ -32,7 +32,7 @@
{
if (!IsValidCargo(cargo_type)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_JUST_CARGO_LIST, 1ULL << cargo_type));
return ::StrMakeValid(::GetString(STR_JUST_CARGO_LIST, 1ULL << cargo_type), {});
}
/* static */ std::optional<std::string> ScriptCargo::GetCargoLabel(CargoType cargo_type)
+17 -9
View File
@@ -78,7 +78,7 @@
company = ResolveCompanyID(company);
if (company == ScriptCompany::COMPANY_INVALID) return std::nullopt;
return ::StrMakeValid(::GetString(STR_COMPANY_NAME, ScriptCompany::FromScriptCompanyID(company)));
return ::StrMakeValid(::GetString(STR_COMPANY_NAME, ScriptCompany::FromScriptCompanyID(company)), {});
}
/* static */ bool ScriptCompany::SetPresidentName(Text *name)
@@ -99,7 +99,7 @@
company = ResolveCompanyID(company);
if (company == ScriptCompany::COMPANY_INVALID) return std::nullopt;
return ::StrMakeValid(::GetString(STR_PRESIDENT_NAME, ScriptCompany::FromScriptCompanyID(company)));
return ::StrMakeValid(::GetString(STR_PRESIDENT_NAME, ScriptCompany::FromScriptCompanyID(company)), {});
}
/* static */ bool ScriptCompany::SetPresidentGender(Gender gender)
@@ -108,12 +108,18 @@
EnforcePrecondition(false, gender == GENDER_MALE || gender == GENDER_FEMALE);
EnforcePrecondition(false, GetPresidentGender(ScriptCompany::COMPANY_SELF) != gender);
Randomizer &randomizer = ScriptObject::GetRandomizer();
CompanyManagerFace cmf;
GenderEthnicity ge = (GenderEthnicity)((gender == GENDER_FEMALE ? (1 << ::GENDER_FEMALE) : 0) | (randomizer.Next() & (1 << ETHNICITY_BLACK)));
RandomCompanyManagerFaceBits(cmf, ge, false, randomizer);
assert(GetNumCompanyManagerFaceStyles() >= 2); /* At least two styles are needed to fake a gender. */
return ScriptObject::Command<CMD_SET_COMPANY_MANAGER_FACE>::Do(cmf);
/* Company faces no longer have a defined gender, so pick a random face style instead. */
Randomizer &randomizer = ScriptObject::GetRandomizer();
CompanyManagerFace cmf{};
do {
cmf.style = randomizer.Next(GetNumCompanyManagerFaceStyles());
} while ((HasBit(cmf.style, 0) ? GENDER_FEMALE : GENDER_MALE) != gender);
RandomiseCompanyManagerFaceBits(cmf, GetCompanyManagerFaceVars(cmf.style), randomizer);
return ScriptObject::Command<CMD_SET_COMPANY_MANAGER_FACE>::Do(cmf.style, cmf.bits);
}
/* static */ ScriptCompany::Gender ScriptCompany::GetPresidentGender(ScriptCompany::CompanyID company)
@@ -121,8 +127,10 @@
company = ResolveCompanyID(company);
if (company == ScriptCompany::COMPANY_INVALID) return GENDER_INVALID;
GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(Company::Get(ScriptCompany::FromScriptCompanyID(company))->face, CMFV_GEN_ETHN, GE_WM);
return HasBit(ge, ::GENDER_FEMALE) ? GENDER_FEMALE : GENDER_MALE;
/* Company faces no longer have a defined gender, so fake one based on the style index. This might not match
* the face appearance. */
const auto &cmf = ::Company::Get(ScriptCompany::FromScriptCompanyID(company))->face;
return HasBit(cmf.style, 0) ? GENDER_FEMALE : GENDER_MALE;
}
/* static */ Money ScriptCompany::GetQuarterlyIncome(ScriptCompany::CompanyID company, SQInteger quarter)
+17 -17
View File
@@ -49,7 +49,7 @@
{
if (_network_dedicated || !_settings_client.gui.ai_developer_tools) return;
ScriptObject::GetActiveInstance()->Pause();
ScriptObject::GetActiveInstance().Pause();
ScriptLog::Log(ScriptLogTypes::LOG_SQ_ERROR, fmt::format("Break: {}", message));
@@ -76,17 +76,17 @@ ScriptController::ScriptController(::CompanyID company) :
/* static */ uint ScriptController::GetTick()
{
return ScriptObject::GetActiveInstance()->GetController()->ticks;
return ScriptObject::GetActiveInstance().GetController().ticks;
}
/* static */ int ScriptController::GetOpsTillSuspend()
{
return ScriptObject::GetActiveInstance()->GetOpsTillSuspend();
return ScriptObject::GetActiveInstance().GetOpsTillSuspend();
}
/* static */ int ScriptController::GetSetting(const std::string &name)
{
return ScriptObject::GetActiveInstance()->GetSetting(name);
return ScriptObject::GetActiveInstance().GetSetting(name);
}
/* static */ uint ScriptController::GetVersion()
@@ -96,11 +96,11 @@ ScriptController::ScriptController(::CompanyID company) :
/* static */ HSQOBJECT ScriptController::Import(const std::string &library, const std::string &class_name, int version)
{
ScriptController *controller = ScriptObject::GetActiveInstance()->GetController();
Squirrel *engine = ScriptObject::GetActiveInstance()->engine;
HSQUIRRELVM vm = engine->GetVM();
ScriptController &controller = ScriptObject::GetActiveInstance().GetController();
Squirrel &engine = *ScriptObject::GetActiveInstance().engine;
HSQUIRRELVM vm = engine.GetVM();
ScriptInfo *lib = ScriptObject::GetActiveInstance()->FindLibrary(library, version);
ScriptInfo *lib = ScriptObject::GetActiveInstance().FindLibrary(library, version);
if (lib == nullptr) {
throw sq_throwerror(vm, fmt::format("couldn't find library '{}' with version {}", library, version));
}
@@ -114,37 +114,37 @@ ScriptController::ScriptController(::CompanyID company) :
std::string fake_class;
LoadedLibraryList::iterator it = controller->loaded_library.find(library_name);
if (it != controller->loaded_library.end()) {
LoadedLibraryList::iterator it = controller.loaded_library.find(library_name);
if (it != controller.loaded_library.end()) {
fake_class = (*it).second;
} else {
int next_number = ++controller->loaded_library_count;
int next_number = ++controller.loaded_library_count;
/* Create a new fake internal name */
fake_class = fmt::format("_internalNA{}", next_number);
/* Load the library in a 'fake' namespace, so we can link it to the name the user requested */
sq_pushroottable(vm);
sq_pushstring(vm, fake_class, -1);
sq_pushstring(vm, fake_class);
sq_newclass(vm, SQFalse);
/* Load the library */
if (!engine->LoadScript(vm, lib->GetMainScript(), false)) {
if (!engine.LoadScript(vm, lib->GetMainScript(), false)) {
throw sq_throwerror(vm, fmt::format("there was a compile error when importing '{}' version {}", library, version));
}
/* Create the fake class */
sq_newslot(vm, -3, SQFalse);
sq_pop(vm, 1);
controller->loaded_library[library_name] = fake_class;
controller.loaded_library[library_name] = fake_class;
}
/* Find the real class inside the fake class (like 'sets.Vector') */
sq_pushroottable(vm);
sq_pushstring(vm, fake_class, -1);
sq_pushstring(vm, fake_class);
if (SQ_FAILED(sq_get(vm, -2))) {
throw sq_throwerror(vm, "internal error assigning library class");
}
sq_pushstring(vm, lib->GetInstanceName(), -1);
sq_pushstring(vm, lib->GetInstanceName());
if (SQ_FAILED(sq_get(vm, -2))) {
throw sq_throwerror(vm, fmt::format("unable to find class '{}' in the library '{}' version {}", lib->GetInstanceName(), library, version));
}
@@ -156,7 +156,7 @@ ScriptController::ScriptController(::CompanyID company) :
/* Now link the name the user wanted to our 'fake' class */
sq_pushobject(vm, parent);
sq_pushstring(vm, class_name, -1);
sq_pushstring(vm, class_name);
sq_pushobject(vm, obj);
sq_newclass(vm, SQTrue);
sq_newslot(vm, -3, SQFalse);
+1
View File
@@ -11,6 +11,7 @@
#define SCRIPT_CONTROLLER_HPP
#include "script_types.hpp"
#include "../../string_func.h"
#include "../../company_type.h"
/**
+1 -1
View File
@@ -47,7 +47,7 @@
{
if (!IsValidEngine(engine_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_ENGINE_NAME, engine_id));
return ::StrMakeValid(::GetString(STR_ENGINE_NAME, engine_id), {});
}
/* static */ CargoType ScriptEngine::GetCargoType(EngineID engine_id)
+2 -2
View File
@@ -27,7 +27,7 @@ ScriptError::ScriptErrorMapString ScriptError::error_map_string = ScriptError::S
{
auto it = ScriptError::error_map_string.find(ScriptError::GetLastError());
assert(it != ScriptError::error_map_string.end());
return it->second;
return std::string{it->second};
}
/* static */ ScriptErrorType ScriptError::StringToError(StringID internal_string_id)
@@ -62,7 +62,7 @@ ScriptError::ScriptErrorMapString ScriptError::error_map_string = ScriptError::S
error_map[internal_string_id] = ai_error_msg;
}
/* static */ void ScriptError::RegisterErrorMapString(ScriptErrorType ai_error_msg, const char *message)
/* static */ void ScriptError::RegisterErrorMapString(ScriptErrorType ai_error_msg, std::string_view message)
{
error_map_string[ai_error_msg] = message;
}
+6 -3
View File
@@ -175,6 +175,9 @@ public:
/** Station is too spread out */
ERR_STATION_TOO_SPREAD_OUT, // [STR_ERROR_STATION_TOO_SPREAD_OUT]
/** Bridge is too low */
ERR_BRIDGE_TOO_LOW, // [STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT]
};
/**
@@ -218,11 +221,11 @@ public:
* @param ai_error_msg The script error message representation.
* @param message The string representation of this error message, used for debug purposes.
*/
static void RegisterErrorMapString(ScriptErrorType ai_error_msg, const char *message);
static void RegisterErrorMapString(ScriptErrorType ai_error_msg, std::string_view message);
private:
typedef std::map<StringID, ScriptErrorType> ScriptErrorMap; ///< The type for mapping between error (internal OpenTTD) StringID to the script error type.
typedef std::map<ScriptErrorType, const char *> ScriptErrorMapString; ///< The type for mapping between error type and textual representation.
using ScriptErrorMap = std::map<StringID, ScriptErrorType>; ///< The type for mapping between error (internal OpenTTD) StringID to the script error type.
using ScriptErrorMapString = std::map<ScriptErrorType, std::string_view>; ///< The type for mapping between error type and textual representation.
static ScriptErrorMap error_map; ///< The mapping between error (internal OpenTTD) StringID to the script error type.
static ScriptErrorMapString error_map_string; ///< The mapping between error type and textual representation.
+8 -45
View File
@@ -9,64 +9,27 @@
#include "../../stdafx.h"
#include "script_event_types.hpp"
#include <queue>
#include "../script_storage.hpp"
#include "../../safeguards.h"
/** The queue of events for a script. */
struct ScriptEventData {
std::queue<ScriptEvent *> stack; ///< The actual queue.
};
/* static */ void ScriptEventController::CreateEventPointer()
{
assert(ScriptObject::GetEventPointer() == nullptr);
ScriptObject::GetEventPointer() = new ScriptEventData();
}
/* static */ void ScriptEventController::FreeEventPointer()
{
ScriptEventData *data = (ScriptEventData *)ScriptObject::GetEventPointer();
/* Free all waiting events (if any) */
while (!data->stack.empty()) {
ScriptEvent *e = data->stack.front();
data->stack.pop();
e->Release();
}
/* Now kill our data pointer */
delete data;
}
/* static */ bool ScriptEventController::IsEventWaiting()
{
if (ScriptObject::GetEventPointer() == nullptr) ScriptEventController::CreateEventPointer();
ScriptEventData *data = (ScriptEventData *)ScriptObject::GetEventPointer();
return !data->stack.empty();
return !ScriptObject::GetEventQueue().empty();
}
/* static */ ScriptEvent *ScriptEventController::GetNextEvent()
{
if (ScriptObject::GetEventPointer() == nullptr) ScriptEventController::CreateEventPointer();
ScriptEventData *data = (ScriptEventData *)ScriptObject::GetEventPointer();
auto &queue = ScriptObject::GetEventQueue();
if (queue.empty()) return nullptr;
if (data->stack.empty()) return nullptr;
ScriptEvent *e = data->stack.front();
data->stack.pop();
return e;
auto *result = queue.front().release();
queue.pop();
return result;
}
/* static */ void ScriptEventController::InsertEvent(ScriptEvent *event)
{
if (ScriptObject::GetEventPointer() == nullptr) ScriptEventController::CreateEventPointer();
ScriptEventData *data = (ScriptEventData *)ScriptObject::GetEventPointer();
event->AddRef();
data->stack.push(event);
ScriptObject::GetEventQueue().push(event);
}
+2
View File
@@ -61,6 +61,7 @@ public:
ET_PRESIDENT_RENAMED,
};
#ifndef DOXYGEN_API
/**
* Constructor of ScriptEvent, to get the type of event.
* @param type The type of event to construct.
@@ -68,6 +69,7 @@ public:
ScriptEvent(ScriptEvent::ScriptEventType type) :
type(type)
{}
#endif /* DOXYGEN_API */
/**
* Get the event-type.
+3 -3
View File
@@ -34,7 +34,7 @@ std::optional<std::string> ScriptEventEnginePreview::GetName()
{
if (!this->IsEngineValid()) return std::nullopt;
return ::StrMakeValid(::GetString(STR_ENGINE_NAME, this->engine));
return ::StrMakeValid(::GetString(STR_ENGINE_NAME, this->engine), {});
}
CargoType ScriptEventEnginePreview::GetCargoType()
@@ -139,7 +139,7 @@ static bool ScriptEventAdminPortReadValue(HSQUIRRELVM vm, nlohmann::json &json)
case nlohmann::json::value_t::string: {
auto value = json.get<std::string>();
sq_pushstring(vm, value.data(), value.size());
sq_pushstring(vm, value);
break;
}
@@ -152,7 +152,7 @@ static bool ScriptEventAdminPortReadValue(HSQUIRRELVM vm, nlohmann::json &json)
sq_newtable(vm);
for (auto &[key, value] : json.items()) {
sq_pushstring(vm, key.data(), key.size());
sq_pushstring(vm, key);
if (!ScriptEventAdminPortReadValue(vm, value)) {
return false;
+34 -34
View File
@@ -62,7 +62,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventVehicleCrashed *Convert(ScriptEvent *instance) { return (ScriptEventVehicleCrashed *)instance; }
static ScriptEventVehicleCrashed *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventVehicleCrashed *>(instance); }
/**
* Get the VehicleID of the crashed vehicle.
@@ -123,7 +123,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventSubsidyOffer *Convert(ScriptEvent *instance) { return (ScriptEventSubsidyOffer *)instance; }
static ScriptEventSubsidyOffer *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventSubsidyOffer *>(instance); }
/**
* Get the SubsidyID of the subsidy.
@@ -156,7 +156,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventSubsidyOfferExpired *Convert(ScriptEvent *instance) { return (ScriptEventSubsidyOfferExpired *)instance; }
static ScriptEventSubsidyOfferExpired *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventSubsidyOfferExpired *>(instance); }
/**
* Get the SubsidyID of the subsidy.
@@ -189,7 +189,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventSubsidyAwarded *Convert(ScriptEvent *instance) { return (ScriptEventSubsidyAwarded *)instance; }
static ScriptEventSubsidyAwarded *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventSubsidyAwarded *>(instance); }
/**
* Get the SubsidyID of the subsidy.
@@ -222,7 +222,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventSubsidyExpired *Convert(ScriptEvent *instance) { return (ScriptEventSubsidyExpired *)instance; }
static ScriptEventSubsidyExpired *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventSubsidyExpired *>(instance); }
/**
* Get the SubsidyID of the subsidy.
@@ -257,7 +257,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventEnginePreview *Convert(ScriptEvent *instance) { return (ScriptEventEnginePreview *)instance; }
static ScriptEventEnginePreview *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventEnginePreview *>(instance); }
/**
* Get the name of the offered engine.
@@ -349,7 +349,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyNew *Convert(ScriptEvent *instance) { return (ScriptEventCompanyNew *)instance; }
static ScriptEventCompanyNew *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyNew *>(instance); }
/**
* Get the CompanyID of the company that has been created.
@@ -383,7 +383,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyRenamed *Convert(ScriptEvent *instance) { return static_cast<ScriptEventCompanyRenamed *>(instance); }
static ScriptEventCompanyRenamed *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyRenamed *>(instance); }
/**
* Get the CompanyID of the company that has been renamed.
@@ -425,7 +425,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyInTrouble *Convert(ScriptEvent *instance) { return (ScriptEventCompanyInTrouble *)instance; }
static ScriptEventCompanyInTrouble *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyInTrouble *>(instance); }
/**
* Get the CompanyID of the company that is in trouble.
@@ -460,7 +460,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyAskMerger *Convert(ScriptEvent *instance) { return (ScriptEventCompanyAskMerger *)instance; }
static ScriptEventCompanyAskMerger *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyAskMerger *>(instance); }
/**
* Get the CompanyID of the company that can be bought.
@@ -511,7 +511,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyMerger *Convert(ScriptEvent *instance) { return (ScriptEventCompanyMerger *)instance; }
static ScriptEventCompanyMerger *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyMerger *>(instance); }
/**
* Get the CompanyID of the company that has been bought.
@@ -554,7 +554,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyBankrupt *Convert(ScriptEvent *instance) { return (ScriptEventCompanyBankrupt *)instance; }
static ScriptEventCompanyBankrupt *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyBankrupt *>(instance); }
/**
* Get the CompanyID of the company that has gone bankrupt.
@@ -587,7 +587,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventVehicleLost *Convert(ScriptEvent *instance) { return (ScriptEventVehicleLost *)instance; }
static ScriptEventVehicleLost *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventVehicleLost *>(instance); }
/**
* Get the VehicleID of the vehicle that is lost.
@@ -620,7 +620,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventVehicleWaitingInDepot *Convert(ScriptEvent *instance) { return (ScriptEventVehicleWaitingInDepot *)instance; }
static ScriptEventVehicleWaitingInDepot *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventVehicleWaitingInDepot *>(instance); }
/**
* Get the VehicleID of the vehicle that is waiting in a depot.
@@ -653,7 +653,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventVehicleUnprofitable *Convert(ScriptEvent *instance) { return (ScriptEventVehicleUnprofitable *)instance; }
static ScriptEventVehicleUnprofitable *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventVehicleUnprofitable *>(instance); }
/**
* Get the VehicleID of the vehicle that lost money.
@@ -686,7 +686,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventIndustryOpen *Convert(ScriptEvent *instance) { return (ScriptEventIndustryOpen *)instance; }
static ScriptEventIndustryOpen *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventIndustryOpen *>(instance); }
/**
* Get the IndustryID of the new industry.
@@ -719,7 +719,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventIndustryClose *Convert(ScriptEvent *instance) { return (ScriptEventIndustryClose *)instance; }
static ScriptEventIndustryClose *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventIndustryClose *>(instance); }
/**
* Get the IndustryID of the closing industry.
@@ -752,7 +752,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventEngineAvailable *Convert(ScriptEvent *instance) { return (ScriptEventEngineAvailable *)instance; }
static ScriptEventEngineAvailable *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventEngineAvailable *>(instance); }
/**
* Get the EngineID of the new engine.
@@ -787,7 +787,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventStationFirstVehicle *Convert(ScriptEvent *instance) { return (ScriptEventStationFirstVehicle *)instance; }
static ScriptEventStationFirstVehicle *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventStationFirstVehicle *>(instance); }
/**
* Get the StationID of the visited station.
@@ -827,7 +827,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventDisasterZeppelinerCrashed *Convert(ScriptEvent *instance) { return (ScriptEventDisasterZeppelinerCrashed *)instance; }
static ScriptEventDisasterZeppelinerCrashed *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventDisasterZeppelinerCrashed *>(instance); }
/**
* Get the StationID of the station containing the affected airport.
@@ -860,7 +860,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventDisasterZeppelinerCleared *Convert(ScriptEvent *instance) { return (ScriptEventDisasterZeppelinerCleared *)instance; }
static ScriptEventDisasterZeppelinerCleared *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventDisasterZeppelinerCleared *>(instance); }
/**
* Get the StationID of the station containing the affected airport.
@@ -893,7 +893,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventTownFounded *Convert(ScriptEvent *instance) { return (ScriptEventTownFounded *)instance; }
static ScriptEventTownFounded *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventTownFounded *>(instance); }
/**
* Get the TownID of the town.
@@ -928,7 +928,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventAircraftDestTooFar *Convert(ScriptEvent *instance) { return (ScriptEventAircraftDestTooFar *)instance; }
static ScriptEventAircraftDestTooFar *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventAircraftDestTooFar *>(instance); }
/**
* Get the VehicleID of the aircraft whose destination is too far away.
@@ -958,7 +958,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventAdminPort *Convert(ScriptEvent *instance) { return (ScriptEventAdminPort *)instance; }
static ScriptEventAdminPort *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventAdminPort *>(instance); }
#ifndef DOXYGEN_API
/**
@@ -1003,7 +1003,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventWindowWidgetClick *Convert(ScriptEvent *instance) { return (ScriptEventWindowWidgetClick *)instance; }
static ScriptEventWindowWidgetClick *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventWindowWidgetClick *>(instance); }
/**
* Get the class of the window that was clicked.
@@ -1056,7 +1056,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventGoalQuestionAnswer *Convert(ScriptEvent *instance) { return (ScriptEventGoalQuestionAnswer *)instance; }
static ScriptEventGoalQuestionAnswer *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventGoalQuestionAnswer *>(instance); }
/**
* Get the unique id of the question.
@@ -1106,7 +1106,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventCompanyTown *Convert(ScriptEvent *instance) { return (ScriptEventCompanyTown *)instance; }
static ScriptEventCompanyTown *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventCompanyTown *>(instance); }
/**
* Get the CompanyID of the company.
@@ -1147,7 +1147,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventExclusiveTransportRights *Convert(ScriptEventCompanyTown *instance) { return (ScriptEventExclusiveTransportRights *)instance; }
static ScriptEventExclusiveTransportRights *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventExclusiveTransportRights *>(instance); }
};
/**
@@ -1172,7 +1172,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventRoadReconstruction *Convert(ScriptEventCompanyTown *instance) { return (ScriptEventRoadReconstruction *)instance; }
static ScriptEventRoadReconstruction *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventRoadReconstruction *>(instance); }
};
/**
@@ -1198,7 +1198,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventVehicleAutoReplaced *Convert(ScriptEvent *instance) { return (ScriptEventVehicleAutoReplaced *)instance; }
static ScriptEventVehicleAutoReplaced *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventVehicleAutoReplaced *>(instance); }
/**
* Get the VehicleID of the vehicle that has been replaced.
@@ -1242,7 +1242,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventStoryPageButtonClick *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageButtonClick *)instance; }
static ScriptEventStoryPageButtonClick *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventStoryPageButtonClick *>(instance); }
/**
* Get the CompanyID of the player that selected a tile.
@@ -1295,7 +1295,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventStoryPageTileSelect *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageTileSelect *)instance; }
static ScriptEventStoryPageTileSelect *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventStoryPageTileSelect *>(instance); }
/**
* Get the CompanyID of the player that selected a tile.
@@ -1355,7 +1355,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventStoryPageVehicleSelect *Convert(ScriptEvent *instance) { return (ScriptEventStoryPageVehicleSelect *)instance; }
static ScriptEventStoryPageVehicleSelect *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventStoryPageVehicleSelect *>(instance); }
/**
* Get the CompanyID of the player that selected a tile.
@@ -1413,7 +1413,7 @@ public:
* @param instance The instance to convert.
* @return The converted instance.
*/
static ScriptEventPresidentRenamed *Convert(ScriptEvent *instance) { return static_cast<ScriptEventPresidentRenamed *>(instance); }
static ScriptEventPresidentRenamed *Convert(ScriptEvent *instance) { return dynamic_cast<ScriptEventPresidentRenamed *>(instance); }
/**
* Get the CompanyID of the company that got its president renamed.
+1 -1
View File
@@ -32,7 +32,7 @@ void ScriptExecMode::FinalRelease()
{
if (this->GetDoCommandModeInstance() != this) {
/* Ignore this error if the script is not alive. */
if (ScriptObject::GetActiveInstance()->IsAlive()) {
if (ScriptObject::GetActiveInstance().IsAlive()) {
throw Script_FatalError("ScriptExecMode object was removed while it was not the latest *Mode object created.");
}
}
+1 -1
View File
@@ -73,7 +73,7 @@
{
if (!IsValidGroup(group_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_GROUP_NAME, group_id));
return ::StrMakeValid(::GetString(STR_GROUP_NAME, group_id), {});
}
/* static */ bool ScriptGroup::SetParent(GroupID group_id, GroupID parent_group_id)
+3 -13
View File
@@ -11,22 +11,12 @@
${SQUIRREL_INCLUDES}
static SQInteger ${APIUC}ObjectConstructor(HSQUIRRELVM vm)
{
return sq_throwerror(vm, "${APIUC}Object is not instantiable");
}
static SQInteger ${APIUC}ObjectCloned(HSQUIRRELVM)
{
throw Script_FatalError("This instance is not cloneable");
}
void SQ${APIUC}_RegisterAll(Squirrel *engine)
void SQ${APIUC}_RegisterAll(Squirrel &engine)
{
DefSQClass<ScriptObject, ScriptType::${APIUC}> SQ${APIUC}Object("${APIUC}Object");
SQ${APIUC}Object.PreRegister(engine);
SQ${APIUC}Object.DefSQAdvancedStaticMethod(engine, &${APIUC}ObjectConstructor, "constructor");
SQ${APIUC}Object.DefSQAdvancedStaticMethod(engine, &${APIUC}ObjectCloned, "_cloned");
SQ${APIUC}Object.DefSQAdvancedStaticMethod(engine, &ScriptObject::Constructor, "constructor");
SQ${APIUC}Object.DefSQAdvancedStaticMethod(engine, &ScriptObject::_cloned, "_cloned");
SQ${APIUC}Object.PostRegister(engine);
${SQUIRREL_REGISTER}
+1 -1
View File
@@ -46,7 +46,7 @@
{
if (!IsValidIndustry(industry_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_INDUSTRY_NAME, industry_id));
return ::StrMakeValid(::GetString(STR_INDUSTRY_NAME, industry_id), {});
}
/* static */ ScriptDate::Date ScriptIndustry::GetConstructionDate(IndustryID industry_id)
+1 -1
View File
@@ -61,7 +61,7 @@
{
if (!IsValidIndustryType(industry_type)) return std::nullopt;
return ::StrMakeValid(::GetString(::GetIndustrySpec(industry_type)->name));
return ::StrMakeValid(::GetString(::GetIndustrySpec(industry_type)->name), {});
}
/* static */ ScriptList *ScriptIndustryType::GetProducedCargo(IndustryType industry_type)
+16 -7
View File
@@ -453,6 +453,20 @@ bool ScriptList::LoadObject(HSQUIRRELVM vm)
return true;
}
ScriptObject *ScriptList::CloneObject()
{
ScriptList *clone = new ScriptList();
clone->CopyList(this);
return clone;
}
void ScriptList::CopyList(const ScriptList *list)
{
this->Sort(list->sorter_type, list->sort_ascending);
this->items = list->items;
this->buckets = list->buckets;
}
ScriptList::ScriptList()
{
/* Default sorter */
@@ -910,8 +924,7 @@ SQInteger ScriptList::Valuate(HSQUIRRELVM vm)
/* Don't allow docommand from a Valuator, as we can't resume in
* mid C++-code. */
bool backup_allow = ScriptObject::GetAllowDoCommand();
ScriptObject::SetAllowDoCommand(false);
ScriptObject::DisableDoCommandScope disabler{};
/* Limit the total number of ops that can be consumed by a valuate operation */
SQOpsLimiter limiter(vm, MAX_VALUATE_OPS, "valuator function");
@@ -932,8 +945,7 @@ SQInteger ScriptList::Valuate(HSQUIRRELVM vm)
}
/* Call the function. Squirrel pops all parameters and pushes the return value. */
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQTrue))) {
ScriptObject::SetAllowDoCommand(backup_allow);
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQFalse))) {
return SQ_ERROR;
}
@@ -956,7 +968,6 @@ SQInteger ScriptList::Valuate(HSQUIRRELVM vm)
/* See below for explanation. The extra pop is the return value. */
sq_pop(vm, nparam + 4);
ScriptObject::SetAllowDoCommand(backup_allow);
return sq_throwerror(vm, "return value of valuator is not valid (not integer/bool)");
}
}
@@ -966,7 +977,6 @@ SQInteger ScriptList::Valuate(HSQUIRRELVM vm)
/* See below for explanation. The extra pop is the return value. */
sq_pop(vm, nparam + 4);
ScriptObject::SetAllowDoCommand(backup_allow);
return sq_throwerror(vm, "modifying valuated list outside of valuator function");
}
@@ -984,6 +994,5 @@ SQInteger ScriptList::Valuate(HSQUIRRELVM vm)
* 4. The ScriptList instance object. */
sq_pop(vm, nparam + 3);
ScriptObject::SetAllowDoCommand(backup_allow);
return 0;
}
+12 -11
View File
@@ -88,11 +88,9 @@ protected:
sq_push(vm, 2);
}
/* Don't allow docommand from a Valuator, as we can't resume in
/* Don't allow docommand from a filter, as we can't resume in
* mid C++-code. */
bool backup_allow = ScriptObject::GetAllowDoCommand();
ScriptObject::SetAllowDoCommand(false);
ScriptObject::DisableDoCommandScope disabler{};
if (nparam < 1) {
ScriptList::FillList<T>(list, item_valid);
@@ -101,7 +99,7 @@ protected:
SQOpsLimiter limiter(vm, MAX_VALUATE_OPS, "list filter function");
ScriptList::FillList<T>(list, item_valid,
[vm, nparam, backup_allow](const T *item) {
[vm, nparam](const T *item) {
/* Push the root table as instance object, this is what squirrel does for meta-functions. */
sq_pushroottable(vm);
/* Push all arguments for the valuator function. */
@@ -111,9 +109,8 @@ protected:
}
/* Call the function. Squirrel pops all parameters and pushes the return value. */
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQTrue))) {
ScriptObject::SetAllowDoCommand(backup_allow);
throw sq_throwerror(vm, "failed to run filter");
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQFalse))) {
throw static_cast<SQInteger>(SQ_ERROR);
}
SQBool add = SQFalse;
@@ -125,7 +122,6 @@ protected:
break;
default:
ScriptObject::SetAllowDoCommand(backup_allow);
throw sq_throwerror(vm, "return value of filter is not valid (not bool)");
}
@@ -139,8 +135,6 @@ protected:
/* Pop the filter function */
sq_poptop(vm);
}
ScriptObject::SetAllowDoCommand(backup_allow);
}
template <typename T>
@@ -151,6 +145,13 @@ protected:
virtual bool SaveObject(HSQUIRRELVM vm) override;
virtual bool LoadObject(HSQUIRRELVM vm) override;
virtual ScriptObject *CloneObject() override;
/**
* Copy the content of a list.
* @param list The list that will be copied.
*/
void CopyList(const ScriptList *list);
public:
typedef std::set<SQInteger> ScriptItemList; ///< The list of items inside the bucket
+3
View File
@@ -123,6 +123,7 @@ public:
* @exception ScriptError::ERR_SITE_UNSUITABLE
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the dock has been/can be build or not.
*/
static bool BuildDock(TileIndex tile, StationID station_id);
@@ -135,6 +136,8 @@ public:
* @exception ScriptError::ERR_AREA_NOT_CLEAR
* @exception ScriptError::ERR_SITE_UNSUITABLE
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the buoy has been/can be build or not.
*/
static bool BuildBuoy(TileIndex tile);
+81 -68
View File
@@ -22,6 +22,7 @@
#include "../script_fatalerror.hpp"
#include "script_error.hpp"
#include "../../debug.h"
#include "../squirrel_helper.hpp"
#include "../../safeguards.h"
@@ -44,18 +45,18 @@ void SimpleCountedObject::Release()
* Get the storage associated with the current ScriptInstance.
* @return The storage.
*/
static ScriptStorage *GetStorage()
static ScriptStorage &GetStorage()
{
return ScriptObject::GetActiveInstance()->GetStorage();
return ScriptObject::GetActiveInstance().GetStorage();
}
/* static */ ScriptInstance *ScriptObject::ActiveInstance::active = nullptr;
ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance *instance) : alc_scope(instance->engine)
ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance &instance) : alc_scope(instance.engine.get())
{
this->last_active = ScriptObject::ActiveInstance::active;
ScriptObject::ActiveInstance::active = instance;
ScriptObject::ActiveInstance::active = &instance;
}
ScriptObject::ActiveInstance::~ActiveInstance()
@@ -63,209 +64,203 @@ ScriptObject::ActiveInstance::~ActiveInstance()
ScriptObject::ActiveInstance::active = this->last_active;
}
/* static */ ScriptInstance *ScriptObject::GetActiveInstance()
ScriptObject::DisableDoCommandScope::DisableDoCommandScope()
: AutoRestoreBackup(GetStorage().allow_do_command, false)
{}
/* static */ ScriptInstance &ScriptObject::GetActiveInstance()
{
assert(ScriptObject::ActiveInstance::active != nullptr);
return ScriptObject::ActiveInstance::active;
return *ScriptObject::ActiveInstance::active;
}
/* static */ void ScriptObject::SetDoCommandDelay(uint ticks)
{
assert(ticks > 0);
GetStorage()->delay = ticks;
GetStorage().delay = ticks;
}
/* static */ uint ScriptObject::GetDoCommandDelay()
{
return GetStorage()->delay;
return GetStorage().delay;
}
/* static */ void ScriptObject::SetDoCommandMode(ScriptModeProc *proc, ScriptObject *instance)
{
GetStorage()->mode = proc;
GetStorage()->mode_instance = instance;
GetStorage().mode = proc;
GetStorage().mode_instance = instance;
}
/* static */ ScriptModeProc *ScriptObject::GetDoCommandMode()
{
return GetStorage()->mode;
return GetStorage().mode;
}
/* static */ ScriptObject *ScriptObject::GetDoCommandModeInstance()
{
return GetStorage()->mode_instance;
return GetStorage().mode_instance;
}
/* static */ void ScriptObject::SetDoCommandAsyncMode(ScriptAsyncModeProc *proc, ScriptObject *instance)
{
GetStorage()->async_mode = proc;
GetStorage()->async_mode_instance = instance;
GetStorage().async_mode = proc;
GetStorage().async_mode_instance = instance;
}
/* static */ ScriptAsyncModeProc *ScriptObject::GetDoCommandAsyncMode()
{
return GetStorage()->async_mode;
return GetStorage().async_mode;
}
/* static */ ScriptObject *ScriptObject::GetDoCommandAsyncModeInstance()
{
return GetStorage()->async_mode_instance;
return GetStorage().async_mode_instance;
}
/* static */ void ScriptObject::SetLastCommand(const CommandDataBuffer &data, Commands cmd)
{
ScriptStorage *s = GetStorage();
Debug(script, 6, "SetLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data));
s->last_data = data;
s->last_cmd = cmd;
ScriptStorage &s = GetStorage();
Debug(script, 6, "SetLastCommand company={:02d} cmd={} data={}", s.root_company, cmd, FormatArrayAsHex(data));
s.last_data = data;
s.last_cmd = cmd;
}
/* static */ bool ScriptObject::CheckLastCommand(const CommandDataBuffer &data, Commands cmd)
{
ScriptStorage *s = GetStorage();
Debug(script, 6, "CheckLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data));
if (s->last_cmd != cmd) return false;
if (s->last_data != data) return false;
ScriptStorage &s = GetStorage();
Debug(script, 6, "CheckLastCommand company={:02d} cmd={} data={}", s.root_company, cmd, FormatArrayAsHex(data));
if (s.last_cmd != cmd) return false;
if (s.last_data != data) return false;
return true;
}
/* static */ void ScriptObject::SetDoCommandCosts(Money value)
{
GetStorage()->costs = CommandCost(INVALID_EXPENSES, value); // Expense type is never read.
GetStorage().costs = CommandCost(INVALID_EXPENSES, value); // Expense type is never read.
}
/* static */ void ScriptObject::IncreaseDoCommandCosts(Money value)
{
GetStorage()->costs.AddCost(value);
GetStorage().costs.AddCost(value);
}
/* static */ Money ScriptObject::GetDoCommandCosts()
{
return GetStorage()->costs.GetCost();
return GetStorage().costs.GetCost();
}
/* static */ void ScriptObject::SetLastError(ScriptErrorType last_error)
{
GetStorage()->last_error = last_error;
GetStorage().last_error = last_error;
}
/* static */ ScriptErrorType ScriptObject::GetLastError()
{
return GetStorage()->last_error;
return GetStorage().last_error;
}
/* static */ void ScriptObject::SetLastCost(Money last_cost)
{
GetStorage()->last_cost = last_cost;
GetStorage().last_cost = last_cost;
}
/* static */ Money ScriptObject::GetLastCost()
{
return GetStorage()->last_cost;
return GetStorage().last_cost;
}
/* static */ void ScriptObject::SetRoadType(RoadType road_type)
{
GetStorage()->road_type = road_type;
GetStorage().road_type = road_type;
}
/* static */ RoadType ScriptObject::GetRoadType()
{
return GetStorage()->road_type;
return GetStorage().road_type;
}
/* static */ void ScriptObject::SetRailType(RailType rail_type)
{
GetStorage()->rail_type = rail_type;
GetStorage().rail_type = rail_type;
}
/* static */ RailType ScriptObject::GetRailType()
{
return GetStorage()->rail_type;
return GetStorage().rail_type;
}
/* static */ void ScriptObject::SetLastCommandRes(bool res)
{
GetStorage()->last_command_res = res;
GetStorage().last_command_res = res;
}
/* static */ bool ScriptObject::GetLastCommandRes()
{
return GetStorage()->last_command_res;
return GetStorage().last_command_res;
}
/* static */ void ScriptObject::SetLastCommandResData(CommandDataBuffer data)
{
GetStorage()->last_cmd_ret = std::move(data);
GetStorage().last_cmd_ret = std::move(data);
}
/* static */ const CommandDataBuffer &ScriptObject::GetLastCommandResData()
{
return GetStorage()->last_cmd_ret;
}
/* static */ void ScriptObject::SetAllowDoCommand(bool allow)
{
GetStorage()->allow_do_command = allow;
}
/* static */ bool ScriptObject::GetAllowDoCommand()
{
return GetStorage()->allow_do_command;
return GetStorage().last_cmd_ret;
}
/* static */ void ScriptObject::SetCompany(::CompanyID company)
{
if (GetStorage()->root_company == INVALID_OWNER) GetStorage()->root_company = company;
GetStorage()->company = company;
if (GetStorage().root_company == INVALID_OWNER) GetStorage().root_company = company;
GetStorage().company = company;
_current_company = company;
}
/* static */ ::CompanyID ScriptObject::GetCompany()
{
return GetStorage()->company;
return GetStorage().company;
}
/* static */ ::CompanyID ScriptObject::GetRootCompany()
{
return GetStorage()->root_company;
return GetStorage().root_company;
}
/* static */ bool ScriptObject::CanSuspend()
{
Squirrel *squirrel = ScriptObject::GetActiveInstance()->engine;
return GetStorage()->allow_do_command && squirrel->CanSuspend();
Squirrel &squirrel = *ScriptObject::GetActiveInstance().engine;
return GetStorage().allow_do_command && squirrel.CanSuspend();
}
/* static */ void *&ScriptObject::GetEventPointer()
/* static */ ScriptEventQueue &ScriptObject::GetEventQueue()
{
return GetStorage()->event_data;
return GetStorage().event_queue;
}
/* static */ ScriptLogTypes::LogData &ScriptObject::GetLogData()
{
return GetStorage()->log_data;
return GetStorage().log_data;
}
/* static */ void ScriptObject::SetCallbackVariable(int index, int value)
{
if (static_cast<size_t>(index) >= GetStorage()->callback_value.size()) GetStorage()->callback_value.resize(index + 1);
GetStorage()->callback_value[index] = value;
if (static_cast<size_t>(index) >= GetStorage().callback_value.size()) GetStorage().callback_value.resize(index + 1);
GetStorage().callback_value[index] = value;
}
/* static */ int ScriptObject::GetCallbackVariable(int index)
{
return GetStorage()->callback_value[index];
return GetStorage().callback_value[index];
}
/* static */ CommandCallbackData *ScriptObject::GetDoCommandCallback()
{
return ScriptObject::GetActiveInstance()->GetDoCommandCallback();
return ScriptObject::GetActiveInstance().GetDoCommandCallback();
}
std::tuple<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
/* static */ std::tuple<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
{
if (!ScriptObject::CanSuspend()) {
throw Script_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
@@ -287,7 +282,7 @@ std::tuple<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
return { false, estimate_only, asynchronous, networking };
}
bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only, bool asynchronous)
/* static */ bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only, bool asynchronous)
{
/* Set the default callback to return a true/false result of the DoCommand */
if (callback == nullptr) callback = &ScriptInstance::DoCommandReturn;
@@ -315,8 +310,8 @@ bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_Suspend
IncreaseDoCommandCosts(res.GetCost());
if (!_generating_world) {
/* Charge a nominal fee for asynchronously executed commands */
Squirrel *engine = ScriptObject::GetActiveInstance()->engine;
Squirrel::DecreaseOps(engine->GetVM(), 100);
Squirrel &engine = *ScriptObject::GetActiveInstance().engine;
Squirrel::DecreaseOps(engine.GetVM(), 100);
}
if (callback != nullptr) {
/* Insert return value into to stack and throw a control code that
@@ -344,15 +339,33 @@ bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_Suspend
/* static */ ScriptObject::RandomizerArray ScriptObject::random_states;
Randomizer &ScriptObject::GetRandomizer(Owner owner)
/* static */ Randomizer &ScriptObject::GetRandomizer(Owner owner)
{
return ScriptObject::random_states[owner];
}
void ScriptObject::InitializeRandomizers()
/* static */ void ScriptObject::InitializeRandomizers()
{
Randomizer random = _random;
for (Owner owner = OWNER_BEGIN; owner < OWNER_END; ++owner) {
ScriptObject::GetRandomizer(owner).SetSeed(random.Next());
}
}
/* static */ SQInteger ScriptObject::Constructor(HSQUIRRELVM)
{
throw Script_FatalError("This class is not instantiatable");
}
/* static */ SQInteger ScriptObject::_cloned(HSQUIRRELVM vm)
{
ScriptObject *original = static_cast<ScriptObject *>(Squirrel::GetRealInstance(vm, 2, "Object"));
if (ScriptObject *clone = original->CloneObject(); clone != nullptr) {
clone->AddRef();
sq_setinstanceup(vm, 1, clone);
sq_setreleasehook(vm, 1, SQConvert::DefSQDestructorCallback<ScriptObject>);
return 0;
}
throw Script_FatalError("This instance is not cloneable");
}
+36 -22
View File
@@ -55,7 +55,7 @@ private:
};
/**
* Uper-parent object of all API classes. You should never use this class in
* Upper-parent object of all API classes. You should never use this class in
* your script, as it doesn't publish any public functions. It is used
* internally to have a common place to handle general things, like internal
* command processing, and command-validation checks.
@@ -75,7 +75,7 @@ protected:
class ActiveInstance {
friend class ScriptObject;
public:
ActiveInstance(ScriptInstance *instance);
ActiveInstance(ScriptInstance &instance);
~ActiveInstance();
private:
ScriptInstance *last_active; ///< The active instance before we go instantiated.
@@ -84,6 +84,11 @@ protected:
static ScriptInstance *active; ///< The global current active instance.
};
class DisableDoCommandScope : private AutoRestoreBackup<bool> {
public:
DisableDoCommandScope();
};
/**
* Save this object.
* Must push 2 elements on the stack:
@@ -100,6 +105,12 @@ protected:
*/
virtual bool LoadObject(HSQUIRRELVM) { return false; }
/**
* Clone an object.
* @return The clone if cloning this type is supported, nullptr otherwise.
*/
virtual ScriptObject *CloneObject() { return nullptr; }
public:
/**
* Store the latest result of a DoCommand per company.
@@ -117,7 +128,7 @@ public:
* Get the currently active instance.
* @return The instance.
*/
static class ScriptInstance *GetActiveInstance();
static class ScriptInstance &GetActiveInstance();
/**
* Get a reference of the randomizer that brings this script random values.
@@ -131,6 +142,16 @@ public:
*/
static void InitializeRandomizers();
/**
* Used when trying to instantiate ScriptObject from squirrel.
*/
static SQInteger Constructor(HSQUIRRELVM);
/**
* Used for 'clone' from squirrel.
*/
static SQInteger _cloned(HSQUIRRELVM);
protected:
template <Commands TCmd, typename T> struct ScriptDoCommandHelper;
@@ -265,21 +286,6 @@ protected:
*/
static const CommandDataBuffer &GetLastCommandResData();
/**
* Store a allow_do_command per company.
* @param allow The new allow.
*/
static void SetAllowDoCommand(bool allow);
/**
* Get the internal value of allow_do_command. This can differ
* from CanSuspend() if the reason we are not allowed
* to execute a DoCommand is in squirrel and not the API.
* In that case use this function to restore the previous value.
* @return True iff DoCommands are allowed in the current scope.
*/
static bool GetAllowDoCommand();
/**
* Set the current company to execute commands for or request
* information about.
@@ -327,12 +333,12 @@ protected:
static bool CanSuspend();
/**
* Get the pointer to store event data in.
* Get the reference to the event queue.
*/
static void *&GetEventPointer();
static struct ScriptEventQueue &GetEventQueue();
/**
* Get the pointer to store log message in.
* Get the reference to the log message storage.
*/
static ScriptLogTypes::LogData &GetLogData();
@@ -341,7 +347,7 @@ private:
static std::tuple<bool, bool, bool, bool> DoCommandPrep();
static bool DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only, bool asynchronous);
static CommandCallbackData *GetDoCommandCallback();
using RandomizerArray = ReferenceThroughBaseContainer<std::array<Randomizer, OWNER_END.base()>>;
using RandomizerArray = TypedIndexContainer<std::array<Randomizer, OWNER_END.base()>, Owner>;
static RandomizerArray random_states; ///< Random states for each of the scripts (game script uses OWNER_DEITY)
};
@@ -467,6 +473,14 @@ public:
if (this->data != nullptr) this->data->Release();
}
/**
* Transfer ownership to the caller.
*/
[[nodiscard]] T *release()
{
return std::exchange(this->data, nullptr);
}
/**
* Dereferencing this reference returns a reference to the reference
* counted object
+1 -1
View File
@@ -28,7 +28,7 @@
{
EnforcePrecondition(std::nullopt, IsValidObjectType(object_type));
return ::StrMakeValid(::GetString(ObjectSpec::Get(object_type)->name));
return ::StrMakeValid(::GetString(ObjectSpec::Get(object_type)->name), {});
}
/* static */ SQInteger ScriptObjectType::GetViews(ObjectType object_type)
+25 -31
View File
@@ -8,6 +8,7 @@
/** @file script_order.cpp Implementation of ScriptOrder. */
#include "../../stdafx.h"
#include <ranges>
#include "script_order.hpp"
#include "script_cargo.hpp"
#include "script_map.hpp"
@@ -34,7 +35,7 @@ static OrderType GetOrderTypeByTile(TileIndex t)
switch (::GetTileType(t)) {
default: break;
case MP_STATION:
if (IsBuoy(t) || IsRailWaypoint(t)) return OT_GOTO_WAYPOINT;
if (IsBuoy(t) || IsRailWaypoint(t) || IsRoadWaypoint(t)) return OT_GOTO_WAYPOINT;
if (IsHangar(t)) return OT_GOTO_DEPOT;
return OT_GOTO_STATION;
@@ -67,15 +68,12 @@ static const Order *ResolveOrder(VehicleID vehicle_id, ScriptOrder::OrderPositio
order_position = ScriptOrder::ResolveOrderPosition(vehicle_id, order_position);
if (order_position == ScriptOrder::ORDER_INVALID) return nullptr;
}
const Order *order = v->GetFirstOrder();
assert(order != nullptr);
while (order->GetType() == OT_IMPLICIT) order = order->next;
while (order_position > 0) {
order_position = (ScriptOrder::OrderPosition)(order_position - 1);
order = order->next;
while (order->GetType() == OT_IMPLICIT) order = order->next;
}
return order;
auto real_orders = v->Orders() | std::views::filter([](const Order &order) { return !order.IsType(OT_IMPLICIT); });
auto it = std::ranges::next(std::begin(real_orders), order_position, std::end(real_orders));
if (it != std::end(real_orders)) return &*it;
return nullptr;
}
/**
@@ -91,17 +89,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
assert(ScriptOrder::IsValidVehicleOrder(vehicle_id, order_position));
int res = (int)order_position;
const Order *order = v->orders->GetFirstOrder();
assert(order != nullptr);
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
while (order_position > 0) {
order_position = (ScriptOrder::OrderPosition)(order_position - 1);
order = order->next;
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
}
return res;
auto orders = v->Orders();
auto real_orders = orders | std::views::filter([](const Order &order) { return !order.IsType(OT_IMPLICIT); });
auto it = std::ranges::next(std::begin(real_orders), order_position, std::end(real_orders));
return static_cast<int>(std::distance(std::begin(orders), it.base()));
}
/**
@@ -111,14 +102,13 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
*/
static ScriptOrder::OrderPosition RealOrderPositionToScriptOrderPosition(VehicleID vehicle_id, int order_position)
{
const Order *order = ::Vehicle::Get(vehicle_id)->GetFirstOrder();
assert(order != nullptr);
int num_implicit_orders = 0;
for (int i = 0; i < order_position; i++) {
if (order->GetType() == OT_IMPLICIT) num_implicit_orders++;
order = order->next;
}
return static_cast<ScriptOrder::OrderPosition>(order_position - num_implicit_orders);
const Vehicle *v = ::Vehicle::Get(vehicle_id);
auto orders = v->Orders();
auto first = std::begin(orders);
auto last = std::ranges::next(first, order_position, std::end(orders));
int num_implicit = static_cast<int>(std::count_if(first, last, [](const Order &order) { return order.IsType(OT_IMPLICIT); }));
return static_cast<ScriptOrder::OrderPosition>(order_position - num_implicit);
}
/* static */ bool ScriptOrder::IsGotoStationOrder(VehicleID vehicle_id, OrderPosition order_position)
@@ -296,8 +286,12 @@ static ScriptOrder::OrderPosition RealOrderPositionToScriptOrderPosition(Vehicle
for (TileIndex t : wp->train_station) {
if (wp->TileBelongsToRailStation(t)) return t;
}
} else if (wp->road_waypoint_area.tile != INVALID_TILE) {
for (TileIndex t : wp->road_waypoint_area) {
if (::IsRoadWaypointTile(t) && ::GetStationIndex(t) == wp->index) return t;
}
}
/* If the waypoint has no rail waypoint tiles, it must have a buoy */
/* If the waypoint has no rail or road waypoint tiles, it must have a buoy */
return wp->xy;
}
default: return INVALID_TILE;
@@ -579,7 +573,7 @@ static ScriptOrder::OrderPosition RealOrderPositionToScriptOrderPosition(Vehicle
* between the wanted and the current order.
* @param instance The script instance we are doing the callback for.
*/
static void _DoCommandReturnSetOrderFlags(class ScriptInstance *instance)
static void _DoCommandReturnSetOrderFlags(class ScriptInstance &instance)
{
ScriptObject::SetLastCommandRes(ScriptOrder::_SetOrderFlags());
ScriptInstance::DoCommandReturn(instance);
+1 -1
View File
@@ -93,7 +93,7 @@ public:
OC_RELIABILITY = ::OCV_RELIABILITY, ///< Skip based on the reliability, value is percent (0..100).
OC_MAX_RELIABILITY = ::OCV_MAX_RELIABILITY, ///< Skip based on the maximum reliability. Value in percent
OC_MAX_SPEED = ::OCV_MAX_SPEED, ///< Skip based on the maximum speed, value is in OpenTTD's internal speed unit, see ScriptEngine::GetMaxSpeed.
OC_AGE = ::OCV_AGE, ///< Skip based on the age, value is in calender-years. @see \ref ScriptCalendarTime
OC_AGE = ::OCV_AGE, ///< Skip based on the age, value is in calendar-years. @see \ref ScriptCalendarTime
OC_REQUIRES_SERVICE = ::OCV_REQUIRES_SERVICE, ///< Skip when the vehicle requires service, no value.
OC_UNCONDITIONALLY = ::OCV_UNCONDITIONALLY, ///< Always skip, no compare function, no value.
OC_REMAINING_LIFETIME = ::OCV_REMAINING_LIFETIME, ///< Skip based on the remaining lifetime in calendar-years. @see \ref ScriptCalendarTime
+3 -3
View File
@@ -26,9 +26,9 @@ static bool operator==(const ScriptPriorityQueue::PriorityItem &lhs, const HSQOB
ScriptPriorityQueue::~ScriptPriorityQueue()
{
/* Release reference to stored objects. */
auto inst = ScriptObject::GetActiveInstance();
if (!inst->InShutdown()) {
for (auto &i : this->queue) inst->ReleaseSQObject(const_cast<HSQOBJECT *>(&i.second));
auto &inst = ScriptObject::GetActiveInstance();
if (!inst.InShutdown()) {
for (auto &i : this->queue) inst.ReleaseSQObject(const_cast<HSQOBJECT *>(&i.second));
}
}
+6 -8
View File
@@ -28,7 +28,7 @@
{
if (!IsRailTypeAvailable(rail_type)) return std::nullopt;
return ::StrMakeValid(::GetString(GetRailTypeInfo((::RailType)rail_type)->strings.menu_text));
return ::StrMakeValid(::GetString(GetRailTypeInfo((::RailType)rail_type)->strings.menu_text), {});
}
/* static */ bool ScriptRail::IsRailTile(TileIndex tile)
@@ -175,8 +175,7 @@
EnforcePrecondition(false, source_industry == ScriptIndustryType::INDUSTRYTYPE_UNKNOWN || source_industry == ScriptIndustryType::INDUSTRYTYPE_TOWN || ScriptIndustryType::IsValidIndustryType(source_industry));
EnforcePrecondition(false, goal_industry == ScriptIndustryType::INDUSTRYTYPE_UNKNOWN || goal_industry == ScriptIndustryType::INDUSTRYTYPE_TOWN || ScriptIndustryType::IsValidIndustryType(goal_industry));
const GRFFile *file;
uint16_t res = GetAiPurchaseCallbackResult(
auto res = GetAiPurchaseCallbackResult(
GSF_STATIONS,
cargo_type,
0,
@@ -185,17 +184,16 @@
ClampTo<uint8_t>(distance / 2),
AICE_STATION_GET_STATION_ID,
source_station ? 0 : 1,
std::min<SQInteger>(15u, num_platforms) << 4 | std::min<SQInteger>(15u, platform_length),
&file
std::min<SQInteger>(15u, num_platforms) << 4 | std::min<SQInteger>(15u, platform_length)
);
Axis axis = direction == RAILTRACK_NW_SE ? AXIS_Y : AXIS_X;
bool adjacent = station_id != ScriptStation::STATION_JOIN_ADJACENT;
StationID to_join = ScriptStation::IsValidStation(station_id) ? station_id : StationID::Invalid();
if (res != CALLBACK_FAILED) {
const StationSpec *spec = StationClass::GetByGrf(file->grfid, res);
if (res.second != CALLBACK_FAILED) {
const StationSpec *spec = StationClass::GetByGrf(res.first->grfid, res.second);
if (spec == nullptr) {
Debug(grf, 1, "{} returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename);
Debug(grf, 1, "{} returned an invalid station ID for 'AI construction/purchase selection (18)' callback", res.first->filename);
} else {
/* We might have gotten an usable station spec. Try to build it, but if it fails we'll fall back to the original station. */
if (ScriptObject::Command<CMD_BUILD_RAIL_STATION>::Do(tile, (::RailType)GetCurrentRailType(), axis, num_platforms, platform_length, spec->class_index, spec->index, to_join, adjacent)) return true;
+6
View File
@@ -262,6 +262,8 @@ public:
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the station has been/can be build or not.
*/
static bool BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id);
@@ -299,6 +301,8 @@ public:
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the station has been/can be build or not.
*/
static bool BuildNewGRFRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id, CargoType cargo_type, IndustryType source_industry, IndustryType goal_industry, SQInteger distance, bool source_station);
@@ -312,6 +316,8 @@ public:
* @pre IsRailTypeAvailable(GetCurrentRailType()).
* @game @pre ScriptCompanyMode::IsValid().
* @exception ScriptError::ERR_FLAT_LAND_REQUIRED
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the rail waypoint has been/can be build or not.
*/
static bool BuildRailWaypoint(TileIndex tile);
+1 -1
View File
@@ -30,7 +30,7 @@
{
if (!IsRoadTypeAvailable(road_type)) return std::nullopt;
return ::StrMakeValid(::GetString(GetRoadTypeInfo((::RoadType)road_type)->strings.name));
return ::StrMakeValid(::GetString(GetRoadTypeInfo((::RoadType)road_type)->strings.name), {});
}
/* static */ bool ScriptRoad::IsRoadTile(TileIndex tile)
+4
View File
@@ -446,6 +446,8 @@ public:
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the station has been/can be build or not.
*/
static bool BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id);
@@ -471,6 +473,8 @@ public:
* @exception ScriptStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS
* @exception ScriptStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
* @exception ScriptError::ERR_BRIDGE_TOO_LOW
* @exception ScriptError::ERR_STATION_TOO_SPREAD_OUT
* @return Whether the station has been/can be build or not.
*/
static bool BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id);
+1 -1
View File
@@ -52,7 +52,7 @@
{
if (!IsValidSign(sign_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_SIGN_NAME, sign_id));
return ::StrMakeValid(::GetString(STR_SIGN_NAME, sign_id), {});
}
/* static */ TileIndex ScriptSign::GetLocation(SignID sign_id)
-7
View File
@@ -26,13 +26,6 @@
return st != nullptr && (st->owner == ScriptObject::GetCompany() || ScriptCompanyMode::IsDeity() || st->owner == OWNER_NONE);
}
/* static */ ScriptCompany::CompanyID ScriptStation::GetOwner(StationID station_id)
{
if (!IsValidStation(station_id)) return ScriptCompany::COMPANY_INVALID;
return ScriptCompany::ToScriptCompanyID(::Station::Get(station_id)->owner);
}
/* static */ StationID ScriptStation::GetStationID(TileIndex tile)
{
if (!::IsValidTile(tile) || !::IsTileType(tile, MP_STATION)) return StationID::Invalid();
-9
View File
@@ -59,15 +59,6 @@ public:
*/
static bool IsValidStation(StationID station_id);
/**
* Get the owner of a station.
* @param station_id The station to get the owner of.
* @pre IsValidStation(station_id).
* @return The owner the station has.
* @api -ai
*/
static ScriptCompany::CompanyID GetOwner(StationID station_id);
/**
* Get the StationID of a tile, if there is a station.
* @param tile The tile to find the stationID of
+2 -2
View File
@@ -34,8 +34,8 @@ ScriptStationList_Vehicle::ScriptStationList_Vehicle(VehicleID vehicle_id)
const Vehicle *v = ::Vehicle::Get(vehicle_id);
for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination().ToStationID().base());
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_STATION)) this->AddItem(o.GetDestination().ToStationID().base());
}
}
+5 -5
View File
@@ -282,7 +282,7 @@ public:
/**
* Opens the Story Book if not yet open and selects the given page.
* @param story_page_id The story page to update. If it is a global page, clients of all
* companies are affecetd. Otherwise only the clients of the company which the page belongs
* companies are affected. Otherwise only the clients of the company which the page belongs
* to are affected.
* @return True if the action succeeded.
* @pre ScriptCompanyMode::IsDeity().
@@ -317,10 +317,10 @@ public:
static bool IsValidStoryPageButtonColour(StoryPageButtonColour colour);
/**
* Check whether this is a valid story page button flag.
* @param flags The StoryPageButtonFlags to check.
* @return True if and only if this story page button flag is valid.
*/
* Check whether this is a valid story page button flag.
* @param flags The StoryPageButtonFlags to check.
* @return True if and only if this story page button flag is valid.
*/
static bool IsValidStoryPageButtonFlags(StoryPageButtonFlags flags);
/**
+1 -1
View File
@@ -50,7 +50,7 @@ public:
/**
* Create a new subsidy.
* @param cargo_type The type of cargo to cary for the subsidy.
* @param cargo_type The type of cargo to carry for the subsidy.
* @param from_type The type of the subsidy on the 'from' side.
* @param from_id The ID of the 'from' side.
* @param to_type The type of the subsidy on the 'to' side.
+1 -1
View File
@@ -32,7 +32,7 @@ void ScriptTestMode::FinalRelease()
{
if (this->GetDoCommandModeInstance() != this) {
/* Ignore this error if the script is not alive. */
if (ScriptObject::GetActiveInstance()->IsAlive()) {
if (ScriptObject::GetActiveInstance().IsAlive()) {
throw Script_FatalError("Testmode object was removed while it was not the latest *Mode object created.");
}
}
+56 -27
View File
@@ -14,6 +14,7 @@
#include "script_text.hpp"
#include "script_log.hpp"
#include "../script_fatalerror.hpp"
#include "../../core/string_consumer.hpp"
#include "../../table/control_codes.h"
#include "table/strings.h"
@@ -56,14 +57,14 @@ ScriptText::ScriptText(HSQUIRRELVM vm)
SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm)
{
if (parameter >= SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR;
if (static_cast<size_t>(parameter) >= std::size(this->param)) this->param.resize(parameter + 1);
switch (sq_gettype(vm, -1)) {
case OT_STRING: {
const SQChar *value;
sq_getstring(vm, -1, &value);
std::string_view view;
sq_getstring(vm, -1, view);
this->param[parameter] = StrMakeValid(value);
this->param[parameter] = StrMakeValid(view);
break;
}
@@ -83,7 +84,7 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm)
/* Validate if it is a GSText instance */
sq_pushroottable(vm);
sq_pushstring(vm, "GSText", -1);
sq_pushstring(vm, "GSText");
sq_get(vm, -2);
sq_pushobject(vm, instance);
if (sq_instanceof(vm) != SQTrue) return SQ_ERROR;
@@ -98,10 +99,13 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm)
break;
}
case OT_NULL:
this->param[parameter] = {};
break;
default: return SQ_ERROR;
}
if (this->paramc <= parameter) this->paramc = parameter + 1;
return 0;
}
@@ -112,7 +116,6 @@ SQInteger ScriptText::SetParam(HSQUIRRELVM vm)
SQInteger k;
sq_getinteger(vm, 2, &k);
if (k > SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR;
if (k < 1) return SQ_ERROR;
k--;
@@ -122,7 +125,7 @@ SQInteger ScriptText::SetParam(HSQUIRRELVM vm)
SQInteger ScriptText::AddParam(HSQUIRRELVM vm)
{
SQInteger res;
res = this->_SetParam(this->paramc, vm);
res = this->_SetParam(static_cast<int>(std::size(this->param)), vm);
if (res != 0) return res;
/* Push our own instance back on top of the stack */
@@ -135,13 +138,15 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm)
int32_t k;
if (sq_gettype(vm, 2) == OT_STRING) {
const SQChar *key_string;
sq_getstring(vm, 2, &key_string);
std::string_view view;
sq_getstring(vm, 2, view);
std::string str = StrMakeValid(key_string);
if (!str.starts_with("param_") || str.size() > 8) return SQ_ERROR;
std::string str = StrMakeValid(view);
if (!str.starts_with("param_")) return SQ_ERROR;
k = stoi(str.substr(6));
auto key = ParseInteger<int32_t>(str.substr(6));
if (!key.has_value()) return SQ_ERROR;
k = *key;
} else if (sq_gettype(vm, 2) == OT_INTEGER) {
SQInteger key;
sq_getinteger(vm, 2, &key);
@@ -150,13 +155,35 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm)
return SQ_ERROR;
}
if (k > SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR;
if (k < 1) return SQ_ERROR;
k--;
return this->_SetParam(k, vm);
}
/**
* Set the number of padding parameters to use, for compatibility with old scripts.
* This is called during RegisterGameTranslation.
*/
void ScriptText::SetPadParameterCount(HSQUIRRELVM vm)
{
ScriptText::pad_parameter_count = 0;
SQInteger top = sq_gettop(vm);
sq_pushroottable(vm);
sq_pushstring(vm, "GSText");
if (!SQ_FAILED(sq_get(vm, -2))) {
sq_pushstring(vm, "SCRIPT_TEXT_MAX_PARAMETERS");
if (!SQ_FAILED(sq_get(vm, -2))) {
SQInteger value;
if (!SQ_FAILED(sq_getinteger(vm, -1, &value))) {
ScriptText::pad_parameter_count = value;
}
}
}
sq_settop(vm, top);
}
EncodedString ScriptText::GetEncodedText()
{
ScriptTextList seen_texts;
@@ -166,7 +193,6 @@ EncodedString ScriptText::GetEncodedText()
StringBuilder builder(result);
this->_FillParamList(params, seen_texts);
this->_GetEncodedText(builder, param_count, params, true);
if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string)));
return ::EncodedString{std::move(result)};
}
@@ -175,21 +201,21 @@ void ScriptText::_FillParamList(ParamList &params, ScriptTextList &seen_texts)
if (std::ranges::find(seen_texts, this) != seen_texts.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", GetGameStringName(this->string)));
seen_texts.push_back(this);
for (int i = 0; i < this->paramc; i++) {
Param *p = &this->param[i];
params.emplace_back(this->string, i, p);
if (!std::holds_alternative<ScriptTextRef>(*p)) continue;
std::get<ScriptTextRef>(*p)->_FillParamList(params, seen_texts);
for (int idx = 0; Param &p : this->param) {
params.emplace_back(this->string, idx, &p);
++idx;
if (!std::holds_alternative<ScriptTextRef>(p)) continue;
std::get<ScriptTextRef>(p)->_FillParamList(params, seen_texts);
}
seen_texts.pop_back();
/* Fill with dummy parameters to match FormatString() behaviour. */
if (seen_texts.empty()) {
static Param dummy = 0;
int nb_extra = SCRIPT_TEXT_MAX_PARAMETERS - (int)params.size();
for (int i = 0; i < nb_extra; i++)
params.emplace_back(StringIndexInTab(-1), i, &dummy);
/* Fill with dummy parameters to match old FormatString() compatibility behaviour. */
if (seen_texts.empty() && ScriptText::pad_parameter_count > 0) {
static Param dummy = {};
for (int idx = static_cast<int>(std::size(this->param)); idx < ScriptText::pad_parameter_count; ++idx) {
params.emplace_back(StringIndexInTab(-1), idx, &dummy);
}
}
}
@@ -201,6 +227,8 @@ void ScriptText::ParamCheck::Encode(StringBuilder &builder, std::string_view cmd
struct visitor {
StringBuilder &builder;
void operator()(const std::monostate &) { }
void operator()(std::string value)
{
this->builder.PutUtf8(SCC_ENCODED_STRING);
@@ -211,7 +239,8 @@ void ScriptText::ParamCheck::Encode(StringBuilder &builder, std::string_view cmd
void operator()(const SQInteger &value)
{
this->builder.PutUtf8(SCC_ENCODED_NUMERIC);
this->builder.PutIntegerBase(value, 16);
/* Sign-extend the value, then store as unsigned */
this->builder.PutIntegerBase<uint64_t>(static_cast<uint64_t>(static_cast<int64_t>(value)), 16);
}
void operator()(const ScriptTextRef &value)
+9 -5
View File
@@ -75,8 +75,6 @@ private:
*/
class ScriptText : public Text {
public:
static const int SCRIPT_TEXT_MAX_PARAMETERS = 20; ///< The maximum amount of parameters you can give to one object.
#ifndef DOXYGEN_API
/**
* The constructor wrapper from Squirrel.
@@ -128,10 +126,15 @@ public:
*/
EncodedString GetEncodedText() override;
/**
* @api -all
*/
static void SetPadParameterCount(HSQUIRRELVM vm);
private:
using ScriptTextRef = ScriptObjectRef<ScriptText>;
using ScriptTextList = std::vector<ScriptText *>;
using Param = std::variant<SQInteger, std::string, ScriptTextRef>;
using Param = std::variant<std::monostate, SQInteger, std::string, ScriptTextRef>;
struct ParamCheck {
StringIndexInTab owner;
@@ -149,8 +152,9 @@ private:
using ParamSpan = std::span<ParamCheck>;
StringIndexInTab string;
std::array<Param, SCRIPT_TEXT_MAX_PARAMETERS> param = {};
int paramc = 0;
std::vector<Param> param{};
static inline int pad_parameter_count = 0; ///< Pad parameters for relaxed string validation.
/**
* Internal function to recursively fill a list of parameters.
+2 -2
View File
@@ -415,7 +415,7 @@ public:
* Raise the given corners of the tile. The corners can be combined,
* for example: SLOPE_N | SLOPE_W (= SLOPE_NW) will raise the west and the north corner.
* @note The corners will be modified in the order west (first), south, east, north (last).
* Changing one corner might cause another corner to be changed too. So modifiing
* Changing one corner might cause another corner to be changed too. So modifying
* multiple corners may result in changing some corners by multiple steps.
* @param tile The tile to raise.
* @param slope Corners to raise (SLOPE_xxx).
@@ -432,7 +432,7 @@ public:
* Lower the given corners of the tile. The corners can be combined,
* for example: SLOPE_N | SLOPE_W (= SLOPE_NW) will lower the west and the north corner.
* @note The corners will be modified in the order west (first), south, east, north (last).
* Changing one corner might cause another corner to be changed too. So modifiing
* Changing one corner might cause another corner to be changed too. So modifying
* multiple corners may result in changing some corners by multiple steps.
* @param tile The tile to lower.
* @param slope Corners to lower (SLOPE_xxx).
+9 -2
View File
@@ -23,6 +23,13 @@ bool ScriptTileList::SaveObject(HSQUIRRELVM vm)
return true;
}
ScriptObject *ScriptTileList::CloneObject()
{
ScriptTileList *clone = new ScriptTileList();
clone->CopyList(this);
return clone;
}
void ScriptTileList::AddRectangle(TileIndex t1, TileIndex t2)
{
if (!::IsValidTile(t1)) return;
@@ -144,8 +151,8 @@ ScriptTileList_StationType::ScriptTileList_StationType(StationID station_id, Scr
if ((station_type & ScriptStation::STATION_TRAIN) != 0) station_types.Set(::StationType::Rail);
if ((station_type & ScriptStation::STATION_TRUCK_STOP) != 0) station_types.Set(::StationType::Truck);
if ((station_type & ScriptStation::STATION_BUS_STOP) != 0) station_types.Set(::StationType::Bus);
if ((station_type & ScriptStation::STATION_AIRPORT) != 0) station_types.Set(::StationType::Airport).Set(::StationType::Oilrig);
if ((station_type & ScriptStation::STATION_DOCK) != 0) station_types.Set(::StationType::Dock).Set(::StationType::Oilrig);
if ((station_type & ScriptStation::STATION_AIRPORT) != 0) station_types.Set({::StationType::Airport, ::StationType::Oilrig});
if ((station_type & ScriptStation::STATION_DOCK) != 0) station_types.Set({::StationType::Dock, ::StationType::Oilrig});
TileArea ta(::TileXY(rect->left, rect->top), rect->Width(), rect->Height());
for (TileIndex cur_tile : ta) {
+1
View File
@@ -22,6 +22,7 @@
class ScriptTileList : public ScriptList {
protected:
virtual bool SaveObject(HSQUIRRELVM) override;
virtual ScriptObject *CloneObject() override;
public:
/**
* Adds the rectangle between tile_from and tile_to to the to-be-evaluated tiles.
+11 -5
View File
@@ -37,7 +37,7 @@
{
if (!IsValidTown(town_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_TOWN_NAME, town_id));
return ::StrMakeValid(::GetString(STR_TOWN_NAME, town_id), {});
}
/* static */ bool ScriptTown::SetName(TownID town_id, Text *name)
@@ -93,7 +93,10 @@
const Town *t = ::Town::Get(town_id);
return t->supplied[cargo_type].old_max;
auto it = t->GetCargoSupplied(cargo_type);
if (it == std::end(t->supplied)) return 0;
return it->history[LAST_MONTH].production;
}
/* static */ SQInteger ScriptTown::GetLastMonthSupplied(TownID town_id, CargoType cargo_type)
@@ -103,7 +106,10 @@
const Town *t = ::Town::Get(town_id);
return t->supplied[cargo_type].old_act;
auto it = t->GetCargoSupplied(cargo_type);
if (it == std::end(t->supplied)) return 0;
return it->history[LAST_MONTH].transported;
}
/* static */ SQInteger ScriptTown::GetLastMonthTransportedPercentage(TownID town_id, CargoType cargo_type)
@@ -206,7 +212,7 @@
if (!IsValidTown(town_id)) return false;
const Town *t = ::Town::Get(town_id);
return ((uint32_t)GetDistanceSquareToTile(town_id, tile) <= t->cache.squared_town_zone_radius[HZB_TOWN_EDGE]);
return ((uint32_t)GetDistanceSquareToTile(town_id, tile) <= t->cache.squared_town_zone_radius[to_underlying(HouseZone::TownEdge)]);
}
/* static */ bool ScriptTown::HasStatue(TownID town_id)
@@ -278,7 +284,7 @@
houses = std::min<SQInteger>(houses, UINT32_MAX);
return ScriptObject::Command<CMD_EXPAND_TOWN>::Do(town_id, houses);
return ScriptObject::Command<CMD_EXPAND_TOWN>::Do(town_id, houses, {TownExpandMode::Buildings, TownExpandMode::Roads});
}
/* static */ bool ScriptTown::FoundTown(TileIndex tile, TownSize size, bool city, RoadLayout layout, Text *name)
+2 -2
View File
@@ -51,7 +51,7 @@
* Helper function to connect a just built tunnel to nearby roads.
* @param instance The script instance we have to built the road for.
*/
static void _DoCommandReturnBuildTunnel2(class ScriptInstance *instance)
static void _DoCommandReturnBuildTunnel2(class ScriptInstance &instance)
{
if (!ScriptTunnel::_BuildTunnelRoad2()) {
ScriptInstance::DoCommandReturn(instance);
@@ -67,7 +67,7 @@ static void _DoCommandReturnBuildTunnel2(class ScriptInstance *instance)
* Helper function to connect a just built tunnel to nearby roads.
* @param instance The script instance we have to built the road for.
*/
static void _DoCommandReturnBuildTunnel1(class ScriptInstance *instance)
static void _DoCommandReturnBuildTunnel1(class ScriptInstance &instance)
{
if (!ScriptTunnel::_BuildTunnelRoad1()) {
ScriptInstance::DoCommandReturn(instance);
+1 -1
View File
@@ -304,7 +304,7 @@
{
if (!IsPrimaryVehicle(vehicle_id)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_VEHICLE_NAME, vehicle_id));
return ::StrMakeValid(::GetString(STR_VEHICLE_NAME, vehicle_id), {});
}
/* static */ SQInteger ScriptVehicle::GetAge(VehicleID vehicle_id)
+2 -2
View File
@@ -370,7 +370,7 @@ public:
* is owned by you.
* @pre ScriptEngine::IsBuildable(engine_id).
* @pre ScriptCargo::IsValidCargo(cargo).
* @return The capacity the vehicle will have when refited.
* @return The capacity the vehicle will have when refitted.
*/
static SQInteger GetBuildWithRefitCapacity(TileIndex depot, EngineID engine_id, CargoType cargo);
@@ -431,7 +431,7 @@ public:
* @pre ScriptCargo::IsValidCargo(cargo).
* @pre You must own the vehicle.
* @pre The vehicle must be stopped in the depot.
* @return The capacity the vehicle will have when refited.
* @return The capacity the vehicle will have when refitted.
*/
static SQInteger GetRefitCapacity(VehicleID vehicle_id, CargoType cargo);
+38 -2
View File
@@ -12,6 +12,7 @@
#include "script_group.hpp"
#include "script_map.hpp"
#include "script_station.hpp"
#include "script_waypoint.hpp"
#include "../../depot_map.h"
#include "../../vehicle_base.h"
#include "../../vehiclelist_func.h"
@@ -33,17 +34,52 @@ ScriptVehicleList::ScriptVehicleList(HSQUIRRELVM vm)
);
}
ScriptVehicleList_Station::ScriptVehicleList_Station(StationID station_id)
ScriptVehicleList_Station::ScriptVehicleList_Station(HSQUIRRELVM vm)
{
EnforceDeityOrCompanyModeValid_Void();
int nparam = sq_gettop(vm) - 1;
if (nparam < 1 || nparam > 2) throw sq_throwerror(vm, "wrong number of parameters");
SQInteger sqstationid;
if (SQ_FAILED(sq_getinteger(vm, 2, &sqstationid))) {
throw sq_throwerror(vm, "parameter 1 must be an integer");
}
StationID station_id = static_cast<StationID>(sqstationid);
if (!ScriptBaseStation::IsValidBaseStation(station_id)) return;
bool is_deity = ScriptCompanyMode::IsDeity();
::CompanyID owner = ScriptObject::GetCompany();
::VehicleType type = VEH_INVALID;
if (nparam == 2) {
SQInteger sqtype;
if (SQ_FAILED(sq_getinteger(vm, 3, &sqtype))) {
throw sq_throwerror(vm, "parameter 2 must be an integer");
}
if (sqtype < ScriptVehicle::VT_RAIL || sqtype > ScriptVehicle::VT_AIR) return;
type = static_cast<::VehicleType>(sqtype);
}
FindVehiclesWithOrder(
[is_deity, owner, type](const Vehicle *v) { return (is_deity || v->owner == owner) && (type == VEH_INVALID || v->type == type); },
[station_id](const Order *order) { return (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station_id; },
[this](const Vehicle *v) { this->AddItem(v->index.base()); }
);
}
ScriptVehicleList_Waypoint::ScriptVehicleList_Waypoint(StationID waypoint_id)
{
EnforceDeityOrCompanyModeValid_Void();
if (!ScriptWaypoint::IsValidWaypoint(waypoint_id)) return;
bool is_deity = ScriptCompanyMode::IsDeity();
::CompanyID owner = ScriptObject::GetCompany();
FindVehiclesWithOrder(
[is_deity, owner](const Vehicle *v) { return is_deity || v->owner == owner; },
[station_id](const Order *order) { return (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station_id; },
[waypoint_id](const Order *order) { return order->IsType(OT_GOTO_WAYPOINT) && order->GetDestination() == waypoint_id; },
[this](const Vehicle *v) { this->AddItem(v->index.base()); }
);
}
+28
View File
@@ -58,11 +58,39 @@ public:
*/
class ScriptVehicleList_Station : public ScriptList {
public:
#ifdef DOXYGEN_API
/**
* @param station_id The station to get the list of vehicles from, which have orders to it.
* @pre ScriptBaseStation::IsValidBaseStation(station_id)
*/
ScriptVehicleList_Station(StationID station_id);
/**
* @param station_id The station to get the list of vehicles from, which have orders to it.
* @param vehicle_type The VehicleType to get the list of vehicles for.
* @pre ScriptBaseStation::IsValidBaseStation(station_id)
*/
ScriptVehicleList_Station(StationID station_id, ScriptVehicle::VehicleType vehicle_type);
#else
/**
* The constructor wrapper from Squirrel.
*/
ScriptVehicleList_Station(HSQUIRRELVM vm);
#endif /* DOXYGEN_API */
};
/**
* Creates a list of vehicles that have orders to a given waypoint.
* @api ai game
* @ingroup ScriptList
*/
class ScriptVehicleList_Waypoint : public ScriptList {
public:
/**
* @param waypoint_id The waypoint to get the list of vehicles from, which have orders to it.
* @pre ScriptWaypoint::IsValidWaypoint(waypoint_id)
*/
ScriptVehicleList_Waypoint(StationID waypoint_id);
};
/**
+2 -2
View File
@@ -34,7 +34,7 @@ ScriptWaypointList_Vehicle::ScriptWaypointList_Vehicle(VehicleID vehicle_id)
const Vehicle *v = ::Vehicle::Get(vehicle_id);
for (const Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
if (o->IsType(OT_GOTO_WAYPOINT)) this->AddItem(o->GetDestination().ToStationID().base());
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_WAYPOINT)) this->AddItem(o.GetDestination().ToStationID().base());
}
}
+5 -5
View File
@@ -20,10 +20,10 @@
#include "../safeguards.h"
void ScriptConfig::Change(std::optional<std::string> name, int version, bool force_exact_match)
void ScriptConfig::Change(std::optional<std::string_view> name, int version, bool force_exact_match)
{
if (name.has_value()) {
this->name = std::move(name.value());
this->name = name.value();
this->info = this->FindInfo(this->name, version, force_exact_match);
} else {
this->info = nullptr;
@@ -88,7 +88,7 @@ int ScriptConfig::GetSetting(const std::string &name) const
return (*it).second;
}
void ScriptConfig::SetSetting(const std::string_view name, int value)
void ScriptConfig::SetSetting(std::string_view name, int value)
{
/* You can only set Script specific settings if an Script is selected. */
if (this->info == nullptr) return;
@@ -140,7 +140,7 @@ int ScriptConfig::GetVersion() const
return this->version;
}
void ScriptConfig::StringToSettings(const std::string &value)
void ScriptConfig::StringToSettings(std::string_view value)
{
std::string_view to_process = value;
for (;;) {
@@ -168,7 +168,7 @@ std::string ScriptConfig::SettingsToString() const
std::string result;
for (const auto &item : this->settings) {
fmt::format_to(std::back_inserter(result), "{}={},", item.first, item.second);
format_append(result, "{}={},", item.first, item.second);
}
/* Remove the last ','. */
+4 -4
View File
@@ -19,7 +19,7 @@ static const int INT32_DIGITS_WITH_SIGN_AND_TERMINATION = 10 + 1 + 1;
/** Flags for Script settings. */
enum class ScriptConfigFlag : uint8_t {
// Unused flag 0x1.
/* Unused flag 0x1. */
Boolean = 1, ///< This value is a boolean (either 0 (false) or 1 (true) ).
InGame = 2, ///< This setting can be changed while the Script is running.
Developer = 3, ///< This setting will only be visible when the Script development tools are active.
@@ -78,7 +78,7 @@ public:
* @param force_exact_match If true try to find the exact same version
* as specified. If false any compatible version is ok.
*/
void Change(std::optional<std::string> name, int version = -1, bool force_exact_match = false);
void Change(std::optional<std::string_view> name, int version = -1, bool force_exact_match = false);
/**
* Get the ScriptInfo linked to this ScriptConfig.
@@ -122,7 +122,7 @@ public:
/**
* Set the value of a setting for this config.
*/
void SetSetting(const std::string_view name, int value);
void SetSetting(std::string_view name, int value);
/**
* Reset all settings to their default value.
@@ -154,7 +154,7 @@ public:
* Convert a string which is stored in the config file or savegames to
* custom settings of this Script.
*/
void StringToSettings(const std::string &value);
void StringToSettings(std::string_view value);
/**
* Convert the custom settings to a string that can be stored in the config
+48 -59
View File
@@ -25,6 +25,7 @@
#include "../strings_func.h"
#include "../timer/timer.h"
#include "../timer/timer_window.h"
#include "../core/string_consumer.hpp"
#include "script_gui.h"
#include "script_log.hpp"
@@ -72,7 +73,7 @@ struct ScriptListWindow : public Window {
ScriptListWindow(WindowDesc &desc, CompanyID slot, bool show_all) : Window(desc),
slot(slot), show_all(show_all)
{
if (slot == OWNER_DEITY) {
if (this->slot == OWNER_DEITY) {
this->info_list = this->show_all ? Game::GetInfoList() : Game::GetUniqueInfoList();
} else {
this->info_list = this->show_all ? AI::GetInfoList() : AI::GetUniqueInfoList();
@@ -85,8 +86,8 @@ struct ScriptListWindow : public Window {
this->vscroll->SetCount(this->info_list->size() + 1);
/* Try if we can find the currently selected AI */
if (GetConfig(slot)->HasScript()) {
ScriptInfo *info = GetConfig(slot)->GetInfo();
if (GetConfig(this->slot)->HasScript()) {
ScriptInfo *info = GetConfig(this->slot)->GetInfo();
int i = 0;
for (const auto &item : *this->info_list) {
if (item.second == info) {
@@ -113,7 +114,7 @@ struct ScriptListWindow : public Window {
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
resize.width = 1;
resize.height = this->line_height;
fill.height = resize.height = this->line_height;
size.height = 5 * this->line_height;
}
@@ -169,25 +170,25 @@ struct ScriptListWindow : public Window {
void ChangeScript()
{
if (this->selected == -1) {
GetConfig(slot)->Change(std::nullopt);
GetConfig(this->slot)->Change(std::nullopt);
} else {
ScriptInfoList::const_iterator it = this->info_list->cbegin();
std::advance(it, this->selected);
GetConfig(slot)->Change(it->second->GetName(), it->second->GetVersion());
GetConfig(this->slot)->Change(it->second->GetName(), it->second->GetVersion());
}
if (_game_mode == GM_EDITOR) {
if (slot == OWNER_DEITY) {
if (this->slot == OWNER_DEITY) {
if (Game::GetInstance() != nullptr) Game::ResetInstance();
Game::StartNew();
} else {
Company *c = Company::GetIfValid(slot);
Company *c = Company::GetIfValid(this->slot);
if (c != nullptr && c->ai_instance != nullptr) {
c->ai_instance.reset();
AI::StartNew(slot);
AI::StartNew(this->slot);
}
}
}
InvalidateWindowData(WC_GAME_OPTIONS, slot == OWNER_DEITY ? WN_GAME_OPTIONS_GS : WN_GAME_OPTIONS_AI);
InvalidateWindowData(WC_GAME_OPTIONS, this->slot == OWNER_DEITY ? WN_GAME_OPTIONS_GS : WN_GAME_OPTIONS_AI);
InvalidateWindowClassesData(WC_SCRIPT_SETTINGS);
InvalidateWindowClassesData(WC_SCRIPT_DEBUG, -1);
CloseWindowByClass(WC_QUERY_STRING);
@@ -199,7 +200,7 @@ struct ScriptListWindow : public Window {
switch (widget) {
case WID_SCRL_LIST: { // Select one of the Scripts
int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCRL_LIST) - 1;
if (sel < (int)this->info_list->size()) {
if (sel < static_cast<int>(this->info_list->size())) {
this->selected = sel;
this->SetDirty();
if (click_count > 1) {
@@ -215,10 +216,6 @@ struct ScriptListWindow : public Window {
this->Close();
break;
}
case WID_SCRL_CANCEL:
this->Close();
break;
}
}
@@ -262,10 +259,7 @@ static constexpr NWidgetPart _nested_script_list_widgets[] = {
NWidget(WWT_PANEL, COLOUR_MAUVE, WID_SCRL_INFO_BG), SetMinimalTextLines(8, WidgetDimensions::unscaled.framerect.Vertical() + WidgetDimensions::unscaled.vsep_normal * 3), SetResize(1, 0),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRL_ACCEPT), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_LIST_ACCEPT, STR_AI_LIST_ACCEPT_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRL_CANCEL), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_LIST_CANCEL, STR_AI_LIST_CANCEL_TOOLTIP),
EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRL_ACCEPT), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_LIST_ACCEPT, STR_AI_LIST_ACCEPT_TOOLTIP),
NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
EndContainer(),
};
@@ -315,7 +309,7 @@ struct ScriptSettingsWindow : public Window {
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_SCRS_SCROLLBAR);
this->FinishInitNested(slot); // Initializes 'this->line_height' as side effect.
this->FinishInitNested(this->slot); // Initializes 'this->line_height' as side effect.
this->OnInvalidateData();
}
@@ -327,12 +321,12 @@ struct ScriptSettingsWindow : public Window {
*/
void RebuildVisibleSettings()
{
visible_settings.clear();
this->visible_settings.clear();
for (const auto &item : *this->script_config->GetConfigList()) {
bool no_hide = !item.flags.Test(ScriptConfigFlag::Developer);
if (no_hide || _settings_client.gui.ai_developer_tools) {
visible_settings.push_back(&item);
this->visible_settings.push_back(&item);
}
}
@@ -353,7 +347,7 @@ struct ScriptSettingsWindow : public Window {
this->line_height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL)) + padding.height;
resize.width = 1;
resize.height = this->line_height;
fill.height = resize.height = this->line_height;
size.height = 5 * this->line_height;
}
@@ -361,7 +355,7 @@ struct ScriptSettingsWindow : public Window {
{
if (widget != WID_SCRS_BACKGROUND) return;
Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
bool rtl = _current_text_dir == TD_RTL;
Rect br = ir.WithWidth(SETTING_BUTTON_WIDTH, rtl);
Rect tr = ir.Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide, rtl);
@@ -377,11 +371,11 @@ struct ScriptSettingsWindow : public Window {
bool editable = this->IsEditableItem(config_item);
if (config_item.flags.Test(ScriptConfigFlag::Boolean)) {
DrawBoolButton(br.left, y + button_y_offset, current_value != 0, editable);
DrawBoolButton(br.left, y + button_y_offset, COLOUR_YELLOW, COLOUR_MAUVE, current_value != 0, editable);
} else {
int i = static_cast<int>(std::distance(std::begin(this->visible_settings), it));
if (config_item.complete_labels) {
DrawDropDownButton(br.left, y + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && clicked_dropdown, editable);
DrawDropDownButton(br.left, y + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && this->clicked_dropdown, editable);
} else {
DrawArrowButtons(br.left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, editable && current_value > config_item.min_value, editable && current_value < config_item.max_value);
}
@@ -421,7 +415,7 @@ struct ScriptSettingsWindow : public Window {
bool bool_item = config_item.flags.Test(ScriptConfigFlag::Boolean);
Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
int x = pt.x - r.left;
if (_current_text_dir == TD_RTL) x = r.Width() - 1 - x;
@@ -484,10 +478,6 @@ struct ScriptSettingsWindow : public Window {
break;
}
case WID_SCRS_ACCEPT:
this->Close();
break;
case WID_SCRS_RESET:
this->script_config->ResetEditableSettings(_game_mode == GM_MENU || ((this->slot != OWNER_DEITY) && !Company::IsValidID(this->slot)));
this->SetDirty();
@@ -497,20 +487,20 @@ struct ScriptSettingsWindow : public Window {
void OnQueryTextFinished(std::optional<std::string> str) override
{
if (!str.has_value() || str->empty()) return;
int32_t value = atoi(str->c_str());
SetValue(value);
if (!str.has_value()) return;
auto value = ParseInteger<int32_t>(*str, 10, true);
if (!value.has_value()) return;
this->SetValue(*value);
}
void OnDropdownSelect(WidgetID widget, int index) override
void OnDropdownSelect(WidgetID widget, int index, int) override
{
if (widget != WID_SCRS_SETTING_DROPDOWN) return;
assert(this->clicked_dropdown);
SetValue(index);
this->SetValue(index);
}
void OnDropdownClose(Point, WidgetID widget, int, bool) override
void OnDropdownClose(Point, WidgetID widget, int, int, bool) override
{
if (widget != WID_SCRS_SETTING_DROPDOWN) return;
/* We cannot raise the dropdown button just yet. OnClick needs some hint, whether
@@ -578,9 +568,8 @@ static constexpr NWidgetPart _nested_script_settings_widgets[] = {
NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_SCRS_SCROLLBAR),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRS_ACCEPT), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_SETTINGS_CLOSE),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRS_RESET), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_SETTINGS_RESET),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCRS_RESET), SetStringTip(STR_AI_SETTINGS_RESET),
NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0),
EndContainer(),
NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
EndContainer(),
@@ -610,7 +599,7 @@ void ShowScriptSettingsWindow(CompanyID slot)
struct ScriptTextfileWindow : public TextfileWindow {
CompanyID slot{}; ///< View the textfile of this CompanyID slot.
ScriptTextfileWindow(TextfileType file_type, CompanyID slot) : TextfileWindow(file_type), slot(slot)
ScriptTextfileWindow(Window *parent, TextfileType file_type, CompanyID slot) : TextfileWindow(parent, file_type), slot(slot)
{
this->ConstructWindow();
this->OnInvalidateData();
@@ -619,7 +608,7 @@ struct ScriptTextfileWindow : public TextfileWindow {
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
{
if (widget == WID_TF_CAPTION) {
return GetString(stringid, (slot == OWNER_DEITY) ? STR_CONTENT_TYPE_GAME_SCRIPT : STR_CONTENT_TYPE_AI, GetConfig(slot)->GetInfo()->GetName());
return GetString(stringid, (this->slot == OWNER_DEITY) ? STR_CONTENT_TYPE_GAME_SCRIPT : STR_CONTENT_TYPE_AI, GetConfig(this->slot)->GetInfo()->GetName());
}
return this->Window::GetWidgetString(widget, stringid);
@@ -627,11 +616,11 @@ struct ScriptTextfileWindow : public TextfileWindow {
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
{
auto textfile = GetConfig(slot)->GetTextfile(file_type, slot);
auto textfile = GetConfig(this->slot)->GetTextfile(file_type, this->slot);
if (!textfile.has_value()) {
this->Close();
} else {
this->LoadTextfile(textfile.value(), (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR);
this->LoadTextfile(textfile.value(), (this->slot == OWNER_DEITY) ? GAME_DIR : AI_DIR);
}
}
};
@@ -641,10 +630,10 @@ struct ScriptTextfileWindow : public TextfileWindow {
* @param file_type The type of textfile to display.
* @param slot The slot the Script is using.
*/
void ShowScriptTextfileWindow(TextfileType file_type, CompanyID slot)
void ShowScriptTextfileWindow(Window *parent, TextfileType file_type, CompanyID slot)
{
CloseWindowById(WC_TEXTFILE, file_type);
new ScriptTextfileWindow(file_type, slot);
parent->CloseChildWindowById(WC_TEXTFILE, file_type);
new ScriptTextfileWindow(parent, file_type, slot);
}
@@ -745,13 +734,13 @@ struct ScriptDebugWindow : public Window {
for (const Company *c : Company::Iterate()) {
if (c->is_ai) {
ChangeToScript(c->index);
this->ChangeToScript(c->index);
return;
}
}
/* If no AI is available, see if there is a game script. */
if (Game::GetInstance() != nullptr) ChangeToScript(OWNER_DEITY);
if (Game::GetInstance() != nullptr) this->ChangeToScript(OWNER_DEITY);
}
/**
@@ -802,7 +791,7 @@ struct ScriptDebugWindow : public Window {
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (widget == WID_SCRD_LOG_PANEL) {
resize.height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
fill.height = resize.height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
size.height = 14 * resize.height + WidgetDimensions::scaled.framerect.Vertical();
}
}
@@ -857,9 +846,9 @@ struct ScriptDebugWindow : public Window {
void DrawWidgetCompanyButton(const Rect &r, WidgetID widget, int start) const
{
if (this->IsWidgetDisabled(widget)) return;
CompanyID cid = (CompanyID)(widget - start);
CompanyID cid = static_cast<CompanyID>(widget - start);
Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
DrawCompanyIcon(cid, CenterBounds(r.left, r.right, sprite_size.width), CenterBounds(r.top, r.bottom, sprite_size.height));
DrawCompanyIcon(cid, CentreBounds(r.left, r.right, sprite_size.width), CentreBounds(r.top, r.bottom, sprite_size.height));
}
/**
@@ -922,7 +911,7 @@ struct ScriptDebugWindow : public Window {
ScriptLogTypes::LogData &log = this->GetLogData();
int scroll_count = (int)log.size();
int scroll_count = static_cast<int>(log.size());
if (this->vscroll->GetCount() != scroll_count) {
this->vscroll->SetCount(scroll_count);
@@ -934,10 +923,10 @@ struct ScriptDebugWindow : public Window {
/* Detect when the user scrolls the window. Enable autoscroll when the bottom-most line becomes visible. */
if (this->last_vscroll_pos != this->vscroll->GetPosition()) {
this->autoscroll = this->vscroll->GetPosition() + this->vscroll->GetCapacity() >= (int)log.size();
this->autoscroll = this->vscroll->GetPosition() + this->vscroll->GetCapacity() >= static_cast<int>(log.size());
}
if (this->autoscroll && this->vscroll->SetPosition((int)log.size())) {
if (this->autoscroll && this->vscroll->SetPosition(static_cast<int>(log.size()))) {
/* We need a repaint */
this->SetWidgetDirty(WID_SCRD_VSCROLLBAR);
this->SetWidgetDirty(WID_SCRD_LOG_PANEL);
@@ -1016,12 +1005,12 @@ struct ScriptDebugWindow : public Window {
/* Check which button is clicked */
if (IsInsideMM(widget, WID_SCRD_COMPANY_BUTTON_START, WID_SCRD_COMPANY_BUTTON_END + 1)) {
ChangeToScript((CompanyID)(widget - WID_SCRD_COMPANY_BUTTON_START), citymania::_fn_mod);
this->ChangeToScript(static_cast<CompanyID>(widget - WID_SCRD_COMPANY_BUTTON_START), citymania::_fn_mod);
}
switch (widget) {
case WID_SCRD_SCRIPT_GAME:
ChangeToScript(OWNER_DEITY, citymania::_fn_mod);
this->ChangeToScript(OWNER_DEITY, citymania::_fn_mod);
break;
case WID_SCRD_RELOAD_TOGGLE:
@@ -1124,7 +1113,7 @@ struct ScriptDebugWindow : public Window {
}
/* Highlight row that matched */
this->highlight_row = (int)(log.size() - 1);
this->highlight_row = static_cast<int>(log.size() - 1);
}
}
}
+3 -1
View File
@@ -13,10 +13,12 @@
#include "../company_type.h"
#include "../textfile_type.h"
struct Window;
void ShowScriptListWindow(CompanyID slot, bool show_all);
Window *ShowScriptDebugWindow(CompanyID show_company = CompanyID::Invalid(), bool new_window = false);
void ShowScriptSettingsWindow(CompanyID slot);
void ShowScriptTextfileWindow(TextfileType file_type, CompanyID slot);
void ShowScriptTextfileWindow(Window *parent, TextfileType file_type, CompanyID slot);
void ShowScriptDebugWindowIfScriptError();
void InitializeScriptGui();
+75 -64
View File
@@ -14,11 +14,12 @@
#include "script_info.hpp"
#include "script_scanner.hpp"
#include "../core/string_consumer.hpp"
#include "../3rdparty/fmt/format.h"
#include "../safeguards.h"
bool ScriptInfo::CheckMethod(const char *name) const
bool ScriptInfo::CheckMethod(std::string_view name) const
{
if (!this->engine->MethodExists(this->SQ_instance, name)) {
this->engine->ThrowError(fmt::format("your info.nut/library.nut doesn't have the method '{}'", name));
@@ -27,18 +28,18 @@ bool ScriptInfo::CheckMethod(const char *name) const
return true;
}
/* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo *info)
/* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo &info)
{
/* Set some basic info from the parent */
Squirrel::GetInstance(vm, &info->SQ_instance, 2);
Squirrel::GetInstance(vm, &info.SQ_instance, 2);
/* Make sure the instance stays alive over time */
sq_addref(vm, &info->SQ_instance);
sq_addref(vm, &info.SQ_instance);
info->scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
info->engine = info->scanner->GetEngine();
info.scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
info.engine = info.scanner->GetEngine();
/* Ensure the mandatory functions exist */
static const char * const required_functions[] = {
static const std::string_view required_functions[] = {
"GetAuthor",
"GetName",
"GetShortName",
@@ -48,30 +49,31 @@ bool ScriptInfo::CheckMethod(const char *name) const
"CreateInstance",
};
for (const auto &required_function : required_functions) {
if (!info->CheckMethod(required_function)) return SQ_ERROR;
if (!info.CheckMethod(required_function)) return SQ_ERROR;
}
/* Get location information of the scanner */
info->main_script = info->scanner->GetMainScript();
info->tar_file = info->scanner->GetTarFile();
info.main_script = info.scanner->GetMainScript();
info.tar_file = info.scanner->GetTarFile();
/* Cache the data the info file gives us. */
if (!info->engine->CallStringMethod(info->SQ_instance, "GetAuthor", &info->author, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "GetName", &info->name, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "GetShortName", &info->short_name, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "GetDescription", &info->description, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "GetDate", &info->date, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallIntegerMethod(info->SQ_instance, "GetVersion", &info->version, MAX_GET_OPS)) return SQ_ERROR;
if (!info->engine->CallStringMethod(info->SQ_instance, "CreateInstance", &info->instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "GetAuthor", &info.author, MAX_GET_OPS)) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "GetName", &info.name, MAX_GET_OPS)) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "GetShortName", &info.short_name, MAX_GET_OPS)) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "GetDescription", &info.description, MAX_GET_OPS)) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "GetDate", &info.date, MAX_GET_OPS)) return SQ_ERROR;
if (!info.engine->CallIntegerMethod(info.SQ_instance, "GetVersion", &info.version, MAX_GET_OPS)) return SQ_ERROR;
if (info.version < 0) return SQ_ERROR;
if (!info.engine->CallStringMethod(info.SQ_instance, "CreateInstance", &info.instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR;
/* The GetURL function is optional. */
if (info->engine->MethodExists(info->SQ_instance, "GetURL")) {
if (!info->engine->CallStringMethod(info->SQ_instance, "GetURL", &info->url, MAX_GET_OPS)) return SQ_ERROR;
if (info.engine->MethodExists(info.SQ_instance, "GetURL")) {
if (!info.engine->CallStringMethod(info.SQ_instance, "GetURL", &info.url, MAX_GET_OPS)) return SQ_ERROR;
}
/* Check if we have settings */
if (info->engine->MethodExists(info->SQ_instance, "GetSettings")) {
if (!info->GetSettings()) return SQ_ERROR;
if (info.engine->MethodExists(info.SQ_instance, "GetSettings")) {
if (!info.GetSettings()) return SQ_ERROR;
}
return 0;
@@ -82,65 +84,74 @@ bool ScriptInfo::GetSettings()
return this->engine->CallMethod(this->SQ_instance, "GetSettings", nullptr, MAX_GET_SETTING_OPS);
}
enum class ScriptConfigItemKey : uint8_t {
Name,
Description,
MinValue,
MaxValue,
MediumValue,
DefaultValue,
Flags,
};
using ScriptConfigItemKeys = EnumBitSet<ScriptConfigItemKey, uint8_t>;
SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
{
ScriptConfigItem config;
uint items = 0;
ScriptConfigItemKeys present{};
int medium_value = INT32_MIN;
/* Read the table, and find all properties we care about */
sq_pushnull(vm);
while (SQ_SUCCEEDED(sq_next(vm, -2))) {
const SQChar *key_string;
if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
std::string_view key_string;
if (SQ_FAILED(sq_getstring(vm, -2, key_string))) return SQ_ERROR;
std::string key = StrMakeValid(key_string);
if (key == "name") {
const SQChar *sqvalue;
if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
std::string_view sqvalue;
if (SQ_FAILED(sq_getstring(vm, -1, sqvalue))) return SQ_ERROR;
/* Don't allow '=' and ',' in configure setting names, as we need those
* 2 chars to nicely store the settings as a string. */
auto replace_with_underscore = [](auto c) { return c == '=' || c == ','; };
config.name = StrMakeValid(sqvalue);
std::replace_if(config.name.begin(), config.name.end(), replace_with_underscore, '_');
items |= 0x001;
present.Set(ScriptConfigItemKey::Name);
} else if (key == "description") {
const SQChar *sqdescription;
if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
std::string_view sqdescription;
if (SQ_FAILED(sq_getstring(vm, -1, sqdescription))) return SQ_ERROR;
config.description = StrMakeValid(sqdescription);
items |= 0x002;
present.Set(ScriptConfigItemKey::Description);
} else if (key == "min_value") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
config.min_value = ClampTo<int32_t>(res);
items |= 0x004;
present.Set(ScriptConfigItemKey::MinValue);
} else if (key == "max_value") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
config.max_value = ClampTo<int32_t>(res);
items |= 0x008;
present.Set(ScriptConfigItemKey::MaxValue);
} else if (key == "easy_value") {
// No longer parsed.
items |= 0x010;
/* No longer parsed. */
} else if (key == "medium_value") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
medium_value = ClampTo<int32_t>(res);
items |= 0x020;
present.Set(ScriptConfigItemKey::MediumValue);
} else if (key == "hard_value") {
// No longer parsed.
items |= 0x040;
/* No longer parsed. */
} else if (key == "custom_value") {
// No longer parsed.
/* No longer parsed. */
} else if (key == "default_value") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
config.default_value = ClampTo<int32_t>(res);
items |= 0x080;
present.Set(ScriptConfigItemKey::DefaultValue);
} else if (key == "random_deviation") {
// No longer parsed.
/* No longer parsed. */
} else if (key == "step_size") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
@@ -148,8 +159,8 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
} else if (key == "flags") {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
config.flags = (ScriptConfigFlags)res;
items |= 0x100;
config.flags = static_cast<ScriptConfigFlags>(res);
present.Set(ScriptConfigItemKey::Flags);
} else {
this->engine->ThrowError(fmt::format("unknown setting property '{}'", key));
return SQ_ERROR;
@@ -162,23 +173,22 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
/* Check if default_value is set. Although required, this was changed with
* 14.0, and as such, older AIs don't use it yet. So we convert the older
* values into a default_value. */
if ((items & 0x080) == 0) {
if (!present.Test(ScriptConfigItemKey::DefaultValue)) {
/* Easy/medium/hard should all three be defined. */
if ((items & 0x010) == 0 || (items & 0x020) == 0 || (items & 0x040) == 0) {
if (!present.Test(ScriptConfigItemKey::MediumValue)) {
this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
return SQ_ERROR;
}
config.default_value = medium_value;
items |= 0x080;
} else {
/* For compatibility, also act like the default sets the easy/medium/hard. */
items |= 0x010 | 0x020 | 0x040;
present.Set(ScriptConfigItemKey::DefaultValue);
}
/* Make sure all properties are defined */
uint mask = config.flags.Test(ScriptConfigFlag::Boolean) ? 0x1F3 : 0x1FF;
if (items != mask) {
/* Make sure all required properties are defined */
ScriptConfigItemKeys required = {ScriptConfigItemKey::Name, ScriptConfigItemKey::Description, ScriptConfigItemKey::DefaultValue, ScriptConfigItemKey::Flags};
if (!config.flags.Test(ScriptConfigFlag::Boolean)) required.Set({ScriptConfigItemKey::MinValue, ScriptConfigItemKey::MaxValue});
if (!present.All(required)) {
this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
return SQ_ERROR;
}
@@ -189,9 +199,9 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
{
const SQChar *setting_name_str;
if (SQ_FAILED(sq_getstring(vm, -2, &setting_name_str))) return SQ_ERROR;
std::string setting_name = StrMakeValid(setting_name_str);
std::string_view setting_name_view;
if (SQ_FAILED(sq_getstring(vm, -2, setting_name_view))) return SQ_ERROR;
std::string setting_name = StrMakeValid(setting_name_view);
ScriptConfigItem *config = nullptr;
for (auto &item : this->config_list) {
@@ -207,21 +217,22 @@ SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
/* Read the table and find all labels */
sq_pushnull(vm);
while (SQ_SUCCEEDED(sq_next(vm, -2))) {
const SQChar *key_string;
const SQChar *label;
if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
if (SQ_FAILED(sq_getstring(vm, -1, &label))) return SQ_ERROR;
std::string_view key_string;
std::string_view label;
if (SQ_FAILED(sq_getstring(vm, -2, key_string))) return SQ_ERROR;
if (SQ_FAILED(sq_getstring(vm, -1, label))) return SQ_ERROR;
/* Because squirrel doesn't support identifiers starting with a digit,
* we skip the first character. */
key_string++;
key_string.remove_prefix(1);
int sign = 1;
if (*key_string == '_') {
if (key_string.starts_with('_')) {
/* When the second character is '_', it indicates the value is negative. */
sign = -1;
key_string++;
key_string.remove_prefix(1);
}
int key = atoi(key_string) * sign;
config->labels[key] = StrMakeValid(label);
auto key = ParseInteger<int>(key_string);
if (!key.has_value()) return SQ_ERROR;
config->labels[*key * sign] = StrMakeValid(label);
sq_pop(vm, 2);
}
@@ -244,7 +255,7 @@ const ScriptConfigItemList *ScriptInfo::GetConfigList() const
return &this->config_list;
}
const ScriptConfigItem *ScriptInfo::GetConfigItem(const std::string_view name) const
const ScriptConfigItem *ScriptInfo::GetConfigItem(std::string_view name) const
{
for (const auto &item : this->config_list) {
if (item.name == name) return &item;
+5 -5
View File
@@ -82,12 +82,12 @@ public:
/**
* Check if a given method exists.
*/
bool CheckMethod(const char *name) const;
bool CheckMethod(std::string_view name) const;
/**
* Process the creation of a FileInfo object.
*/
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo *info);
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo &info);
/**
* Get the scanner which has found this ScriptInfo.
@@ -107,7 +107,7 @@ public:
/**
* Get the description of a certain Script config option.
*/
const ScriptConfigItem *GetConfigItem(const std::string_view name) const;
const ScriptConfigItem *GetConfigItem(std::string_view name) const;
/**
* Set a setting.
@@ -149,7 +149,7 @@ private:
class ScriptScanner *scanner = nullptr; ///< ScriptScanner object that was used to scan this script info.
};
void Script_CreateDummyInfo(HSQUIRRELVM vm, const char *type, const char *dir);
void Script_CreateDummy(HSQUIRRELVM vm, StringID string, const char *type);
void Script_CreateDummyInfo(HSQUIRRELVM vm, std::string_view type, std::string_view dir);
void Script_CreateDummy(HSQUIRRELVM vm, StringID string, std::string_view type);
#endif /* SCRIPT_INFO_HPP */
+7 -8
View File
@@ -12,7 +12,7 @@
#include "../string_func.h"
#include "../strings_func.h"
#include "../3rdparty/fmt/format.h"
#include "../core/format.hpp"
#include "../safeguards.h"
@@ -26,7 +26,7 @@
*/
/** Run the dummy info.nut. */
void Script_CreateDummyInfo(HSQUIRRELVM vm, const char *type, const char *dir)
void Script_CreateDummyInfo(HSQUIRRELVM vm, std::string_view type, std::string_view dir)
{
std::string dummy_script = fmt::format(
"class Dummy{0} extends {0}Info {{\n"
@@ -42,7 +42,7 @@ void Script_CreateDummyInfo(HSQUIRRELVM vm, const char *type, const char *dir)
sq_pushroottable(vm);
/* Load and run the script */
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script.c_str(), dummy_script.size(), "dummy", SQTrue))) {
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script, "dummy", SQTrue))) {
sq_push(vm, -2);
if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) {
sq_pop(vm, 1);
@@ -78,7 +78,7 @@ static std::vector<std::string> EscapeQuotesAndSlashesAndSplitOnNewLines(const s
}
/** Run the dummy AI and let it generate an error message. */
void Script_CreateDummy(HSQUIRRELVM vm, StringID string, const char *type)
void Script_CreateDummy(HSQUIRRELVM vm, StringID string, std::string_view type)
{
/* We want to translate the error message.
* We do this in three steps:
@@ -90,19 +90,18 @@ void Script_CreateDummy(HSQUIRRELVM vm, StringID string, const char *type)
/* 2) We construct the AI's code. This is done by merging a header, body and footer */
std::string dummy_script;
auto back_inserter = std::back_inserter(dummy_script);
/* Just a rough ballpark estimate. */
dummy_script.reserve(error_message.size() + 128 + 64 * messages.size());
fmt::format_to(back_inserter, "class Dummy{0} extends {0}Controller {{\n function Start()\n {{\n", type);
format_append(dummy_script, "class Dummy{0} extends {0}Controller {{\n function Start()\n {{\n", type);
for (std::string &message : messages) {
fmt::format_to(back_inserter, " {}Log.Error(\"{}\");\n", type, message);
format_append(dummy_script, " {}Log.Error(\"{}\");\n", type, message);
}
dummy_script += " }\n}\n";
/* 3) Finally we load and run the script */
sq_pushroottable(vm);
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script.c_str(), dummy_script.size(), "dummy", SQTrue))) {
if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script, "dummy", SQTrue))) {
sq_push(vm, -2);
if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) {
sq_pop(vm, 1);
+92 -86
View File
@@ -24,46 +24,45 @@
#include "api/script_event.hpp"
#include "api/script_log.hpp"
#include "../company_base.h"
#include "../company_func.h"
#include "../company_type.h"
#include "../fileio_func.h"
#include "../goal_type.h"
#include "../league_type.h"
#include "../signs_type.h"
#include "../story_type.h"
#include "../misc/endian_buffer.hpp"
#include "../safeguards.h"
ScriptStorage::~ScriptStorage()
{
/* Free our pointers */
if (event_data != nullptr) ScriptEventController::FreeEventPointer();
}
ScriptStorage::ScriptStorage() = default;
ScriptStorage::~ScriptStorage() = default;
/**
* Callback called by squirrel when a script uses "print" and for error messages.
* @param error_msg Is this an error message?
* @param message The actual message text.
*/
static void PrintFunc(bool error_msg, const std::string &message)
static void PrintFunc(bool error_msg, std::string_view message)
{
/* Convert to OpenTTD internal capable string */
ScriptController::Print(error_msg, message);
ScriptController::Print(error_msg, std::string{message});
}
ScriptInstance::ScriptInstance(const char *APIName)
ScriptInstance::ScriptInstance(std::string_view api_name)
{
this->storage = new ScriptStorage();
this->engine = new Squirrel(APIName);
this->storage = std::make_unique<ScriptStorage>();
this->engine = std::make_unique<Squirrel>(api_name);
this->engine->SetPrintFunction(&PrintFunc);
}
void ScriptInstance::Initialize(const std::string &main_script, const std::string &instance_name, CompanyID company)
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
this->controller = new ScriptController(company);
this->controller = std::make_unique<ScriptController>(company);
/* Register the API functions and classes */
this->engine->SetGlobalPointer(this->engine);
this->engine->SetGlobalPointer(this->engine.get());
this->RegisterAPI();
if (this->IsDead()) {
/* Failed to register API; a message has already been logged. */
@@ -71,7 +70,7 @@ void ScriptInstance::Initialize(const std::string &main_script, const std::strin
}
try {
ScriptObject::SetAllowDoCommand(false);
ScriptObject::DisableDoCommandScope disabler{};
/* Load and execute the script for this script */
if (main_script == "%_dummy") {
this->LoadDummyScript();
@@ -82,16 +81,14 @@ void ScriptInstance::Initialize(const std::string &main_script, const std::strin
}
/* Create the main-class */
this->instance = new SQObject();
if (!this->engine->CreateClassInstance(instance_name, this->controller, this->instance)) {
this->instance = std::make_unique<SQObject>();
if (!this->engine->CreateClassInstance(instance_name, this->controller.get(), this->instance.get())) {
/* If CreateClassInstance has returned false instance has not been
* registered with squirrel, so avoid trying to Release it by clearing it now */
delete this->instance;
this->instance = nullptr;
this->instance.reset();
this->Died();
return;
}
ScriptObject::SetAllowDoCommand(true);
} catch (Script_FatalError &e) {
this->is_dead = true;
this->engine->ThrowError(e.GetErrorMessage());
@@ -102,7 +99,7 @@ void ScriptInstance::Initialize(const std::string &main_script, const std::strin
void ScriptInstance::RegisterAPI()
{
squirrel_register_std(this->engine);
squirrel_register_std(*this->engine);
}
bool ScriptInstance::LoadCompatibilityScript(std::string_view api_version, Subdirectory dir)
@@ -132,6 +129,13 @@ bool ScriptInstance::LoadCompatibilityScripts(Subdirectory dir, std::span<const
ScriptLog::Info(fmt::format("Downgrading API to be compatible with version {}", this->api_version));
HSQUIRRELVM vm = this->engine->GetVM();
sq_pushroottable(vm);
sq_pushstring(vm, "CompatScriptRootTable");
sq_pushroottable(vm);
sq_newslot(vm, -3, SQFalse);
sq_pop(vm, 1);
/* 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) {
@@ -140,19 +144,23 @@ bool ScriptInstance::LoadCompatibilityScripts(Subdirectory dir, std::span<const
if (*it == this->api_version) break;
}
sq_pushroottable(vm);
sq_pushstring(vm, "CompatScriptRootTable");
sq_deleteslot(vm, -2, SQFalse);
sq_pop(vm, 1);
return true;
}
ScriptInstance::~ScriptInstance()
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
this->in_shutdown = true;
if (instance != nullptr) this->engine->ReleaseObject(this->instance);
if (engine != nullptr) delete this->engine;
delete this->storage;
delete this->controller;
delete this->instance;
if (instance != nullptr) this->engine->ReleaseObject(this->instance.get());
/* Engine must be reset explicitly in scope of the active instance. */
this->engine.reset();
}
void ScriptInstance::Continue()
@@ -169,16 +177,14 @@ void ScriptInstance::Died()
this->last_allocated_memory = this->GetAllocatedMemory(); // Update cache
if (this->instance != nullptr) this->engine->ReleaseObject(this->instance);
delete this->instance;
delete this->engine;
this->instance = nullptr;
this->engine = nullptr;
if (this->instance != nullptr) this->engine->ReleaseObject(this->instance.get());
this->engine.reset();
this->instance.reset();
}
void ScriptInstance::GameLoop()
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
if (this->IsDead()) return;
if (this->engine->HasScriptCrashed()) {
@@ -202,7 +208,7 @@ void ScriptInstance::GameLoop()
this->is_save_data_on_stack = false;
}
try {
this->callback(this);
this->callback(*this);
} catch (Script_Suspend &e) {
this->suspend = e.GetSuspendTime();
this->callback = e.GetSuspendCallback();
@@ -216,21 +222,22 @@ void ScriptInstance::GameLoop()
if (!this->is_started) {
try {
ScriptObject::SetAllowDoCommand(false);
/* Run the constructor if it exists. Don't allow any DoCommands in it. */
if (this->engine->MethodExists(*this->instance, "constructor")) {
if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
{
ScriptObject::DisableDoCommandScope disabler{};
/* Run the constructor if it exists. Don't allow any DoCommands in it. */
if (this->engine->MethodExists(*this->instance, "constructor")) {
if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
this->Died();
return;
}
}
if (!this->CallLoad() || this->engine->IsSuspended()) {
if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
this->Died();
return;
}
}
if (!this->CallLoad() || this->engine->IsSuspended()) {
if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
this->Died();
return;
}
ScriptObject::SetAllowDoCommand(true);
/* Start the script by calling Start() */
if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
} catch (Script_Suspend &e) {
@@ -268,65 +275,66 @@ void ScriptInstance::GameLoop()
void ScriptInstance::CollectGarbage()
{
if (this->is_started && !this->IsDead()) {
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
this->engine->CollectGarbage();
}
}
/* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturn(ScriptInstance &instance)
{
instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
instance.engine->InsertResult(ScriptObject::GetLastCommandRes());
}
/* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<VehicleID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<VehicleID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<SignID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<SignID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<GroupID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<GroupID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<GoalID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<GoalID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnStoryPageID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnStoryPageID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<StoryPageID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<StoryPageID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnStoryPageElementID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnStoryPageElementID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<StoryPageElementID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<StoryPageElementID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnLeagueTableElementID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnLeagueTableElementID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<LeagueTableElementID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<LeagueTableElementID>(ScriptObject::GetLastCommandResData()));
}
/* static */ void ScriptInstance::DoCommandReturnLeagueTableID(ScriptInstance *instance)
/* static */ void ScriptInstance::DoCommandReturnLeagueTableID(ScriptInstance &instance)
{
instance->engine->InsertResult(EndianBufferReader::ToValue<LeagueTableID>(ScriptObject::GetLastCommandResData()));
instance.engine->InsertResult(EndianBufferReader::ToValue<LeagueTableID>(ScriptObject::GetLastCommandResData()));
}
ScriptStorage *ScriptInstance::GetStorage()
ScriptStorage &ScriptInstance::GetStorage()
{
return this->storage;
assert(this->storage != nullptr);
return *this->storage;
}
ScriptLogTypes::LogData &ScriptInstance::GetLogData()
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
return ScriptObject::GetLogData();
}
@@ -386,9 +394,9 @@ static const SaveLoad _script_byte[] = {
_script_sl_byte = SQSL_STRING;
SlObject(nullptr, _script_byte);
}
const SQChar *buf;
sq_getstring(vm, index, &buf);
size_t len = strlen(buf) + 1;
std::string_view view;
sq_getstring(vm, index, view);
size_t len = view.size() + 1;
if (len >= 255) {
ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
return false;
@@ -396,7 +404,7 @@ static const SaveLoad _script_byte[] = {
if (!test) {
_script_sl_byte = (uint8_t)len;
SlObject(nullptr, _script_byte);
SlCopy(const_cast<char *>(buf), len, SLE_CHAR);
SlCopy(const_cast<char *>(view.data()), len, SLE_CHAR);
}
return true;
}
@@ -504,7 +512,7 @@ static const SaveLoad _script_byte[] = {
void ScriptInstance::Save()
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
/* Don't save data if the script didn't start yet or if it crashed. */
if (this->engine == nullptr || this->engine->HasScriptCrashed()) {
@@ -523,10 +531,9 @@ void ScriptInstance::Save()
return;
} else if (this->engine->MethodExists(*this->instance, "Save")) {
HSQOBJECT savedata;
/* We don't want to be interrupted during the save function. */
bool backup_allow = ScriptObject::GetAllowDoCommand();
ScriptObject::SetAllowDoCommand(false);
try {
/* We don't want to be interrupted during the save function. */
ScriptObject::DisableDoCommandScope disabler{};
if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
/* The script crashed in the Save function. We can't kill
* it here, but do so in the next script tick. */
@@ -547,10 +554,9 @@ void ScriptInstance::Save()
this->engine->CrashOccurred();
return;
}
ScriptObject::SetAllowDoCommand(backup_allow);
if (!sq_istable(savedata)) {
ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
ScriptLog::Error(this->GetOpsTillSuspend() <= 0 ? "This script took too long to Save." : "Save function should return a table.");
SaveEmpty();
this->engine->CrashOccurred();
return;
@@ -652,7 +658,7 @@ bool ScriptInstance::IsPaused()
ScriptData *data;
bool operator()(const SQInteger &value) { sq_pushinteger(this->vm, value); return true; }
bool operator()(const std::string &value) { sq_pushstring(this->vm, value, -1); return true; }
bool operator()(const std::string &value) { sq_pushstring(this->vm, value); return true; }
bool operator()(const SQBool &value) { sq_pushbool(this->vm, value); return true; }
bool operator()(const SQSaveLoadType &type)
{
@@ -681,10 +687,10 @@ bool ScriptInstance::IsPaused()
case SQSL_INSTANCE: {
SQInteger top = sq_gettop(this->vm);
LoadObjects(this->vm, this->data);
const SQChar *buf;
sq_getstring(this->vm, -1, &buf);
std::string_view view;
sq_getstring(this->vm, -1, view);
Squirrel *engine = static_cast<Squirrel *>(sq_getforeignptr(this->vm));
std::string class_name = fmt::format("{}{}", engine->GetAPIName(), buf);
std::string class_name = fmt::format("{}{}", engine->GetAPIName(), view);
sq_pushroottable(this->vm);
sq_pushstring(this->vm, class_name);
if (SQ_FAILED(sq_get(this->vm, -2))) throw Script_FatalError(fmt::format("'{}' doesn't exist", class_name));
@@ -742,7 +748,7 @@ bool ScriptInstance::IsPaused()
void ScriptInstance::LoadOnStack(ScriptData *data)
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
if (this->IsDead() || data == nullptr) return;
@@ -781,7 +787,7 @@ bool ScriptInstance::CallLoad()
/* Go to the instance-root */
sq_pushobject(vm, *this->instance);
/* Find the function-name inside the script */
sq_pushstring(vm, "Load", -1);
sq_pushstring(vm, "Load");
/* Change the "Load" string in a function pointer */
sq_get(vm, -2);
/* Push the main instance as "this" object */
@@ -806,7 +812,7 @@ SQInteger ScriptInstance::GetOpsTillSuspend()
bool ScriptInstance::DoCommandCallback(const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd)
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
if (!ScriptObject::CheckLastCommand(data, cmd)) {
Debug(script, 1, "DoCommandCallback terminating a script, last command does not match expected command");
@@ -830,7 +836,7 @@ bool ScriptInstance::DoCommandCallback(const CommandCost &result, const CommandD
void ScriptInstance::InsertEvent(class ScriptEvent *event)
{
ScriptObject::ActiveInstance active(this);
ScriptObject::ActiveInstance active(*this);
ScriptEventController::InsertEvent(event);
}
+20 -16
View File
@@ -46,7 +46,7 @@ public:
/**
* Create a new script.
*/
ScriptInstance(const char *APIName);
ScriptInstance(std::string_view api_name);
virtual ~ScriptInstance();
/**
@@ -91,7 +91,7 @@ public:
/**
* Get the storage of this script.
*/
class ScriptStorage *GetStorage();
class ScriptStorage &GetStorage();
/**
* Get the log pointer of this script.
@@ -101,52 +101,56 @@ public:
/**
* Return a true/false reply for a DoCommand.
*/
static void DoCommandReturn(ScriptInstance *instance);
static void DoCommandReturn(ScriptInstance &instance);
/**
* Return a VehicleID reply for a DoCommand.
*/
static void DoCommandReturnVehicleID(ScriptInstance *instance);
static void DoCommandReturnVehicleID(ScriptInstance &instance);
/**
* Return a SignID reply for a DoCommand.
*/
static void DoCommandReturnSignID(ScriptInstance *instance);
static void DoCommandReturnSignID(ScriptInstance &instance);
/**
* Return a GroupID reply for a DoCommand.
*/
static void DoCommandReturnGroupID(ScriptInstance *instance);
static void DoCommandReturnGroupID(ScriptInstance &instance);
/**
* Return a GoalID reply for a DoCommand.
*/
static void DoCommandReturnGoalID(ScriptInstance *instance);
static void DoCommandReturnGoalID(ScriptInstance &instance);
/**
* Return a StoryPageID reply for a DoCommand.
*/
static void DoCommandReturnStoryPageID(ScriptInstance *instance);
static void DoCommandReturnStoryPageID(ScriptInstance &instance);
/**
* Return a StoryPageElementID reply for a DoCommand.
*/
static void DoCommandReturnStoryPageElementID(ScriptInstance *instance);
static void DoCommandReturnStoryPageElementID(ScriptInstance &instance);
/**
* Return a LeagueTableID reply for a DoCommand.
*/
static void DoCommandReturnLeagueTableID(ScriptInstance *instance);
static void DoCommandReturnLeagueTableID(ScriptInstance &instance);
/**
* Return a LeagueTableElementID reply for a DoCommand.
*/
static void DoCommandReturnLeagueTableElementID(ScriptInstance *instance);
static void DoCommandReturnLeagueTableElementID(ScriptInstance &instance);
/**
* Get the controller attached to the instance.
*/
class ScriptController *GetController() { return controller; }
class ScriptController &GetController()
{
assert(this->controller != nullptr);
return *this->controller;
}
/**
* Return the "this script died" value
@@ -252,7 +256,7 @@ public:
void ReleaseSQObject(HSQOBJECT *obj);
protected:
class Squirrel *engine = nullptr; ///< A wrapper around the squirrel vm.
std::unique_ptr<class Squirrel> engine; ///< A wrapper around the squirrel vm.
std::string api_version{}; ///< Current API used by this script.
/**
@@ -284,9 +288,9 @@ protected:
virtual void LoadDummyScript() = 0;
private:
class ScriptController *controller = nullptr; ///< The script main class.
class ScriptStorage *storage = nullptr; ///< Some global information for each running script.
SQObject *instance = nullptr; ///< Squirrel-pointer to the script main class.
std::unique_ptr<class ScriptStorage> storage; ///< Some global information for each running script.
std::unique_ptr<class ScriptController> controller; ///< The script main class.
std::unique_ptr<SQObject> instance; ///< Squirrel-pointer to the script main class.
bool is_started = false; ///< Is the scripts constructor executed?
bool is_dead = false; ///< True if the script has been stopped.
+28 -38
View File
@@ -44,21 +44,18 @@ bool ScriptScanner::AddFile(const std::string &filename, size_t, const std::stri
return true;
}
ScriptScanner::ScriptScanner() :
engine(nullptr)
{
}
ScriptScanner::ScriptScanner() = default;
void ScriptScanner::ResetEngine()
{
this->engine->Reset();
this->engine->SetGlobalPointer(this);
this->RegisterAPI(this->engine);
this->RegisterAPI(*this->engine);
}
void ScriptScanner::Initialize(const char *name)
void ScriptScanner::Initialize(std::string_view name)
{
this->engine = new Squirrel(name);
this->engine = std::make_unique<Squirrel>(name);
this->RescanDir();
@@ -68,8 +65,6 @@ void ScriptScanner::Initialize(const char *name)
ScriptScanner::~ScriptScanner()
{
this->Reset();
delete this->engine;
}
void ScriptScanner::RescanDir()
@@ -83,57 +78,52 @@ void ScriptScanner::RescanDir()
void ScriptScanner::Reset()
{
for (const auto &item : this->info_list) {
delete item.second;
}
this->info_list.clear();
this->info_single_list.clear();
this->info_vector.clear();
}
void ScriptScanner::RegisterScript(ScriptInfo *info)
void ScriptScanner::RegisterScript(std::unique_ptr<ScriptInfo> &&info)
{
std::string script_original_name = this->GetScriptName(info);
std::string script_original_name = this->GetScriptName(*info);
std::string script_name = fmt::format("{}.{}", script_original_name, info->GetVersion());
/* Check if GetShortName follows the rules */
if (info->GetShortName().size() != 4) {
Debug(script, 0, "The script '{}' returned a string from GetShortName() which is not four characters. Unable to load the script.", info->GetName());
delete info;
return;
}
if (this->info_list.find(script_name) != this->info_list.end()) {
if (auto it = this->info_list.find(script_name); it != this->info_list.end()) {
/* This script was already registered */
#ifdef _WIN32
/* Windows doesn't care about the case */
if (StrEqualsIgnoreCase(this->info_list[script_name]->GetMainScript(), info->GetMainScript())) {
if (StrEqualsIgnoreCase(it->second->GetMainScript(), info->GetMainScript())) {
#else
if (this->info_list[script_name]->GetMainScript() == info->GetMainScript()) {
if (it->second->GetMainScript() == info->GetMainScript()) {
#endif
delete info;
return;
}
Debug(script, 1, "Registering two scripts with the same name and version");
Debug(script, 1, " 1: {}", this->info_list[script_name]->GetMainScript());
Debug(script, 1, " 1: {}", it->second->GetMainScript());
Debug(script, 1, " 2: {}", info->GetMainScript());
Debug(script, 1, "The first is taking precedence.");
delete info;
return;
}
this->info_list[script_name] = info;
ScriptInfo *script_info = this->info_vector.emplace_back(std::move(info)).get();
this->info_list[script_name] = script_info;
if (!info->IsDeveloperOnly() || _settings_client.gui.ai_developer_tools) {
if (!script_info->IsDeveloperOnly() || _settings_client.gui.ai_developer_tools) {
/* Add the script to the 'unique' script list, where only the highest version
* of the script is registered. */
auto it = this->info_single_list.find(script_original_name);
if (it == this->info_single_list.end()) {
this->info_single_list[script_original_name] = info;
} else if (it->second->GetVersion() < info->GetVersion()) {
it->second = info;
this->info_single_list[script_original_name] = script_info;
} else if (it->second->GetVersion() < script_info->GetVersion()) {
it->second = script_info;
}
}
}
@@ -195,17 +185,17 @@ struct ScriptFileChecksumCreator : FileScanner {
* @param info The script to get the shortname and md5 sum from.
* @return True iff they're the same.
*/
static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, Subdirectory dir)
static bool IsSameScript(const ContentInfo &ci, bool md5sum, const ScriptInfo &info, Subdirectory dir)
{
uint32_t id = 0;
const char *str = info->GetShortName().c_str();
for (int j = 0; j < 4 && *str != '\0'; j++, str++) id |= *str << (8 * j);
auto str = std::string_view{info.GetShortName()}.substr(0, 4);
for (size_t j = 0; j < str.size(); j++) id |= static_cast<uint8_t>(str[j]) << (8 * j);
if (id != ci.unique_id) return false;
if (!md5sum) return true;
ScriptFileChecksumCreator checksum(dir);
const auto &tar_filename = info->GetTarFile();
const auto &tar_filename = info.GetTarFile();
TarList::iterator iter;
if (!tar_filename.empty() && (iter = _tar_list[dir].find(tar_filename)) != _tar_list[dir].end()) {
/* The main script is in a tar file, so find all files that
@@ -215,8 +205,8 @@ static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, S
if (tar.second.tar_filename != iter->first) continue;
/* Check the extension. */
const char *ext = strrchr(tar.first.c_str(), '.');
if (ext == nullptr || !StrEqualsIgnoreCase(ext, ".nut")) continue;
auto ext = tar.first.rfind('.');
if (ext == std::string_view::npos || !StrEqualsIgnoreCase(tar.first.substr(ext), ".nut")) continue;
checksum.AddFile(tar.first, 0, tar_filename);
}
@@ -224,7 +214,7 @@ static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, S
/* There'll always be at least 1 path separator character in a script
* main script name as the search algorithm requires the main script to
* be in a subdirectory of the script directory; so <dir>/<path>/main.nut. */
const std::string &main_script = info->GetMainScript();
const std::string &main_script = info.GetMainScript();
std::string path = main_script.substr(0, main_script.find_last_of(PATHSEPCHAR));
checksum.Scan(".nut", path);
}
@@ -235,15 +225,15 @@ static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, S
bool ScriptScanner::HasScript(const ContentInfo &ci, bool md5sum)
{
for (const auto &item : this->info_list) {
if (IsSameScript(ci, md5sum, item.second, this->GetDirectory())) return true;
if (IsSameScript(ci, md5sum, *item.second, this->GetDirectory())) return true;
}
return false;
}
const char *ScriptScanner::FindMainScript(const ContentInfo &ci, bool md5sum)
std::optional<std::string_view> ScriptScanner::FindMainScript(const ContentInfo &ci, bool md5sum)
{
for (const auto &item : this->info_list) {
if (IsSameScript(ci, md5sum, item.second, this->GetDirectory())) return item.second->GetMainScript().c_str();
if (IsSameScript(ci, md5sum, *item.second, this->GetDirectory())) return item.second->GetMainScript();
}
return nullptr;
return std::nullopt;
}
+13 -11
View File
@@ -13,7 +13,7 @@
#include "../fileio_func.h"
#include "../string_func.h"
typedef std::map<std::string, class ScriptInfo *, CaseInsensitiveComparator> ScriptInfoList; ///< Type for the list of scripts.
using ScriptInfoList = std::map<std::string, class ScriptInfo *, CaseInsensitiveComparator>; ///< Type for the list of scripts.
/** Scanner to help finding scripts. */
class ScriptScanner : public FileScanner {
@@ -26,7 +26,7 @@ public:
/**
* Get the engine of the main squirrel handler (it indexes all available scripts).
*/
class Squirrel *GetEngine() { return this->engine; }
class Squirrel *GetEngine() { return this->engine.get(); }
/**
* Get the current main script the ScanDir is currently tracking.
@@ -51,7 +51,7 @@ public:
/**
* Register a ScriptInfo to the scanner.
*/
void RegisterScript(class ScriptInfo *info);
void RegisterScript(std::unique_ptr<class ScriptInfo> &&info);
/**
* Get the list of registered scripts to print on the console.
@@ -74,7 +74,7 @@ public:
* @param md5sum Whether to check the MD5 checksum.
* @return A filename of a file of the content, else \c nullptr.
*/
const char *FindMainScript(const ContentInfo &ci, bool md5sum);
std::optional<std::string_view> FindMainScript(const ContentInfo &ci, bool md5sum);
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override;
@@ -84,28 +84,30 @@ public:
void RescanDir();
protected:
class Squirrel *engine; ///< The engine we're scanning with.
std::unique_ptr<class Squirrel> engine; ///< The engine we're scanning with.
std::string main_script; ///< The full path of the script.
std::string tar_file; ///< If, which tar file the script was in.
ScriptInfoList info_list; ///< The list of all script.
std::vector<std::unique_ptr<ScriptInfo>> info_vector;
ScriptInfoList info_list; ///< The list of all script.
ScriptInfoList info_single_list; ///< The list of all unique script. The best script (highest version) is shown.
/**
* Initialize the scanner.
* @param name The name of the scanner ("AIScanner", "GSScanner", ..).
*/
void Initialize(const char *name);
void Initialize(std::string_view name);
/**
* Get the script name how to store the script in memory.
*/
virtual std::string GetScriptName(ScriptInfo *info) = 0;
virtual std::string GetScriptName(ScriptInfo &info) = 0;
/**
* Get the filename to scan for this type of script.
*/
virtual const char *GetFileName() const = 0;
virtual std::string_view GetFileName() const = 0;
/**
* Get the directory to scan in.
@@ -115,12 +117,12 @@ protected:
/**
* Register the API for this ScriptInfo.
*/
virtual void RegisterAPI(class Squirrel *engine) = 0;
virtual void RegisterAPI(class Squirrel &engine) = 0;
/**
* Get the type of the script, in plural.
*/
virtual const char *GetScannerName() const = 0;
virtual std::string_view GetScannerName() const = 0;
/**
* Reset all allocated lists.
+31 -42
View File
@@ -10,15 +10,22 @@
#ifndef SCRIPT_STORAGE_HPP
#define SCRIPT_STORAGE_HPP
#include "../signs_func.h"
#include "../vehicle_func.h"
#include <queue>
#include "../command_type.h"
#include "../company_type.h"
#include "../rail_type.h"
#include "../road_type.h"
#include "../group.h"
#include "../goal_type.h"
#include "../story_type.h"
#include "script_types.hpp"
#include "script_log_types.hpp"
#include "script_object.hpp"
class ScriptEvent;
/* This is a "struct", so we can forward declare it, and use as incomplete type. */
struct ScriptEventQueue : std::queue<ScriptObjectRef<ScriptEvent>> {
};
/**
* The callback function for Mode-classes.
@@ -36,53 +43,35 @@ typedef bool (ScriptAsyncModeProc)();
class ScriptStorage {
friend class ScriptObject;
private:
ScriptModeProc *mode; ///< The current build mode we are int.
class ScriptObject *mode_instance; ///< The instance belonging to the current build mode.
ScriptAsyncModeProc *async_mode; ///< The current command async mode we are in.
class ScriptObject *async_mode_instance; ///< The instance belonging to the current command async mode.
CompanyID root_company; ///< The root company, the company that the script really belongs to.
CompanyID company; ///< The current company.
ScriptModeProc *mode = nullptr; ///< The current build mode we are int.
class ScriptObject *mode_instance = nullptr; ///< The instance belonging to the current build mode.
ScriptAsyncModeProc *async_mode = nullptr; ///< The current command async mode we are in.
class ScriptObject *async_mode_instance = nullptr; ///< The instance belonging to the current command async mode.
CompanyID root_company = INVALID_OWNER; ///< The root company, the company that the script really belongs to.
CompanyID company = INVALID_OWNER; ///< The current company.
uint delay; ///< The ticks of delay each DoCommand has.
bool allow_do_command; ///< Is the usage of DoCommands restricted?
uint delay = 1; ///< The ticks of delay each DoCommand has.
bool allow_do_command = true; ///< Is the usage of DoCommands restricted?
CommandCost costs; ///< The costs the script is tracking.
Money last_cost; ///< The last cost of the command.
CommandCost costs; ///< The costs the script is tracking.
Money last_cost = 0; ///< The last cost of the command.
ScriptErrorType last_error{}; ///< The last error of the command.
bool last_command_res; ///< The last result of the command.
bool last_command_res = true; ///< The last result of the command.
CommandDataBuffer last_data; ///< The last data passed to a command.
Commands last_cmd; ///< The last cmd passed to a command.
CommandDataBuffer last_cmd_ret; ///< The extra data returned by the last command.
CommandDataBuffer last_data; ///< The last data passed to a command.
Commands last_cmd = CMD_END; ///< The last cmd passed to a command.
CommandDataBuffer last_cmd_ret; ///< The extra data returned by the last command.
std::vector<int> callback_value; ///< The values which need to survive a callback.
RoadType road_type; ///< The current roadtype we build.
RailType rail_type; ///< The current railtype we build.
RoadType road_type = INVALID_ROADTYPE; ///< The current roadtype we build.
RailType rail_type = INVALID_RAILTYPE; ///< The current railtype we build.
void *event_data; ///< Pointer to the event data storage.
ScriptLogTypes::LogData log_data;///< Log data storage.
ScriptEventQueue event_queue; ///< Event queue for this script.
ScriptLogTypes::LogData log_data; ///< Log data storage.
public:
ScriptStorage() :
mode (nullptr),
mode_instance (nullptr),
async_mode (nullptr),
async_mode_instance (nullptr),
root_company (INVALID_OWNER),
company (INVALID_OWNER),
delay (1),
allow_do_command (true),
/* costs (can't be set) */
last_cost (0),
last_command_res (true),
last_cmd (CMD_END),
/* calback_value (can't be set) */
road_type (INVALID_ROADTYPE),
rail_type (INVALID_RAILTYPE),
event_data (nullptr)
{ }
ScriptStorage();
~ScriptStorage();
};
+1 -1
View File
@@ -13,7 +13,7 @@
/**
* The callback function when a script suspends.
*/
typedef void (Script_SuspendCallbackProc)(class ScriptInstance *instance);
typedef void (Script_SuspendCallbackProc)(class ScriptInstance &instance);
/**
* A throw-class that is given when the script wants to suspend.
+93 -68
View File
@@ -18,6 +18,8 @@
#include <sqstdaux.h>
#include <../squirrel/sqpcheader.h>
#include <../squirrel/sqvm.h>
#include "../core/math_func.hpp"
#include "../core/string_consumer.hpp"
#include "../safeguards.h"
@@ -132,7 +134,7 @@ public:
this->CheckAllocationAllowed(size - oldsize);
void *new_p = this->DoAlloc(size);
memcpy(new_p, p, std::min(oldsize, size));
std::copy_n(static_cast<std::byte *>(p), std::min(oldsize, size), static_cast<std::byte *>(new_p));
this->Free(p, oldsize);
return new_p;
@@ -177,7 +179,7 @@ size_t Squirrel::GetAllocatedMemory() const noexcept
}
void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
void Squirrel::CompileError(HSQUIRRELVM vm, std::string_view desc, std::string_view source, SQInteger line, SQInteger column)
{
std::string msg = fmt::format("Error {}:{}/{}: {}", source, line, column, desc);
@@ -192,7 +194,7 @@ void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *so
}
}
void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const std::string &s)
void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, std::string_view s)
{
/* Check if we have a custom print function */
SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func;
@@ -203,7 +205,7 @@ void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const std::string &s)
}
}
void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error)
void Squirrel::RunError(HSQUIRRELVM vm, std::string_view error)
{
/* Set the print function to something that prints to stderr */
SQPRINTFUNCTION pf = sq_getprintfunc(vm);
@@ -227,11 +229,11 @@ void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error)
SQInteger Squirrel::_RunError(HSQUIRRELVM vm)
{
const SQChar *sErr = nullptr;
std::string_view view;
if (sq_gettop(vm) >= 1) {
if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
Squirrel::RunError(vm, sErr);
if (SQ_SUCCEEDED(sq_getstring(vm, -1, view))) {
Squirrel::RunError(vm, view);
return 0;
}
}
@@ -240,7 +242,7 @@ SQInteger Squirrel::_RunError(HSQUIRRELVM vm)
return 0;
}
void Squirrel::PrintFunc(HSQUIRRELVM vm, const std::string &s)
void Squirrel::PrintFunc(HSQUIRRELVM vm, std::string_view s)
{
/* Check if we have a custom print function */
SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func;
@@ -251,57 +253,57 @@ void Squirrel::PrintFunc(HSQUIRRELVM vm, const std::string &s)
}
}
void Squirrel::AddMethod(const char *method_name, SQFUNCTION proc, uint nparam, const char *params, void *userdata, int size)
void Squirrel::AddMethod(std::string_view method_name, SQFUNCTION proc, std::string_view params, void *userdata, int size)
{
ScriptAllocatorScope alloc_scope(this);
sq_pushstring(this->vm, method_name, -1);
sq_pushstring(this->vm, method_name);
if (size != 0) {
void *ptr = sq_newuserdata(vm, size);
memcpy(ptr, userdata, size);
std::copy_n(static_cast<std::byte *>(userdata), size, static_cast<std::byte *>(ptr));
}
sq_newclosure(this->vm, proc, size != 0 ? 1 : 0);
if (nparam != 0) sq_setparamscheck(this->vm, nparam, params);
if (!params.empty()) sq_setparamscheck(this->vm, params.size(), params);
sq_setnativeclosurename(this->vm, -1, method_name);
sq_newslot(this->vm, -3, SQFalse);
}
void Squirrel::AddConst(const char *var_name, int value)
void Squirrel::AddConst(std::string_view var_name, int value)
{
ScriptAllocatorScope alloc_scope(this);
sq_pushstring(this->vm, var_name, -1);
sq_pushstring(this->vm, var_name);
sq_pushinteger(this->vm, value);
sq_newslot(this->vm, -3, SQTrue);
}
void Squirrel::AddConst(const char *var_name, bool value)
void Squirrel::AddConst(std::string_view var_name, bool value)
{
ScriptAllocatorScope alloc_scope(this);
sq_pushstring(this->vm, var_name, -1);
sq_pushstring(this->vm, var_name);
sq_pushbool(this->vm, value);
sq_newslot(this->vm, -3, SQTrue);
}
void Squirrel::AddClassBegin(const char *class_name)
void Squirrel::AddClassBegin(std::string_view class_name)
{
ScriptAllocatorScope alloc_scope(this);
sq_pushroottable(this->vm);
sq_pushstring(this->vm, class_name, -1);
sq_pushstring(this->vm, class_name);
sq_newclass(this->vm, SQFalse);
}
void Squirrel::AddClassBegin(const char *class_name, const char *parent_class)
void Squirrel::AddClassBegin(std::string_view class_name, std::string_view parent_class)
{
ScriptAllocatorScope alloc_scope(this);
sq_pushroottable(this->vm);
sq_pushstring(this->vm, class_name, -1);
sq_pushstring(this->vm, parent_class, -1);
sq_pushstring(this->vm, class_name);
sq_pushstring(this->vm, parent_class);
if (SQ_FAILED(sq_get(this->vm, -3))) {
Debug(misc, 0, "[squirrel] Failed to initialize class '{}' based on parent class '{}'", class_name, parent_class);
Debug(misc, 0, "[squirrel] Make sure that '{}' exists before trying to define '{}'", parent_class, class_name);
@@ -318,7 +320,7 @@ void Squirrel::AddClassEnd()
sq_pop(vm, 1);
}
bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
bool Squirrel::MethodExists(HSQOBJECT instance, std::string_view method_name)
{
assert(!this->crashed);
ScriptAllocatorScope alloc_scope(this);
@@ -327,7 +329,7 @@ bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
/* Go to the instance-root */
sq_pushobject(this->vm, instance);
/* Find the function-name inside the script */
sq_pushstring(this->vm, method_name, -1);
sq_pushstring(this->vm, method_name);
if (SQ_FAILED(sq_get(this->vm, -2))) {
sq_settop(this->vm, top);
return false;
@@ -371,7 +373,7 @@ void Squirrel::CollectGarbage()
sq_collectgarbage(this->vm);
}
bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
bool Squirrel::CallMethod(HSQOBJECT instance, std::string_view method_name, HSQOBJECT *ret, int suspend)
{
assert(!this->crashed);
ScriptAllocatorScope alloc_scope(this);
@@ -386,7 +388,7 @@ bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT
/* Go to the instance-root */
sq_pushobject(this->vm, instance);
/* Find the function-name inside the script */
sq_pushstring(this->vm, method_name, -1);
sq_pushstring(this->vm, method_name);
if (SQ_FAILED(sq_get(this->vm, -2))) {
Debug(misc, 0, "[squirrel] Could not find '{}' in the class", method_name);
sq_settop(this->vm, top);
@@ -405,16 +407,19 @@ bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT
return true;
}
bool Squirrel::CallStringMethod(HSQOBJECT instance, const char *method_name, std::string *res, int suspend)
bool Squirrel::CallStringMethod(HSQOBJECT instance, std::string_view method_name, std::string *res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
if (ret._type != OT_STRING) return false;
*res = StrMakeValid(ObjectToString(&ret));
auto str = ObjectToString(&ret);
if (!str.has_value()) return false;
*res = StrMakeValid(*str);
return true;
}
bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend)
bool Squirrel::CallIntegerMethod(HSQOBJECT instance, std::string_view method_name, int *res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
@@ -423,7 +428,7 @@ bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, in
return true;
}
bool Squirrel::CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend)
bool Squirrel::CallBoolMethod(HSQOBJECT instance, std::string_view method_name, bool *res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
@@ -442,11 +447,10 @@ bool Squirrel::CallBoolMethod(HSQOBJECT instance, const char *method_name, bool
sq_pushroottable(vm);
if (prepend_API_name) {
std::string prepended_class_name = engine->GetAPIName();
prepended_class_name += class_name;
sq_pushstring(vm, prepended_class_name, -1);
std::string prepended_class_name = fmt::format("{}{}", engine->GetAPIName(), class_name);
sq_pushstring(vm, prepended_class_name);
} else {
sq_pushstring(vm, class_name, -1);
sq_pushstring(vm, class_name);
}
if (SQ_FAILED(sq_get(vm, -2))) {
@@ -486,7 +490,7 @@ bool Squirrel::CreateClassInstance(const std::string &class_name, void *real_ins
return Squirrel::CreateClassInstanceVM(this->vm, class_name, real_instance, instance, nullptr);
}
/* static */ SQUserPointer Squirrel::GetRealInstance(HSQUIRRELVM vm, int index, const char *tag)
/* static */ SQUserPointer Squirrel::GetRealInstance(HSQUIRRELVM vm, int index, std::string_view tag)
{
if (index < 0) index += sq_gettop(vm) + 1;
Squirrel *engine = static_cast<Squirrel *>(sq_getforeignptr(vm));
@@ -503,8 +507,8 @@ bool Squirrel::CreateClassInstance(const std::string &class_name, void *real_ins
throw sq_throwerror(vm, fmt::format("parameter {} has an invalid type ; expected: '{}'", index - 1, class_name));
}
Squirrel::Squirrel(const char *APIName) :
APIName(APIName), allocator(new ScriptAllocator())
Squirrel::Squirrel(std::string_view api_name) :
api_name(api_name), allocator(std::make_unique<ScriptAllocator>())
{
this->Initialize();
}
@@ -532,7 +536,7 @@ void Squirrel::Initialize()
sq_setforeignptr(this->vm, this);
sq_pushroottable(this->vm);
squirrel_register_global_std(this);
squirrel_register_global_std(*this);
/* Set consts table as delegate of root table, so consts/enums defined via require() are accessible */
sq_pushconsttable(this->vm);
@@ -544,52 +548,73 @@ private:
FileHandle file;
size_t size;
size_t pos;
std::string buffer;
StringConsumer consumer;
size_t ReadInternal(std::span<char> buf)
{
size_t count = buf.size();
if (this->pos + count > this->size) {
count = this->size - this->pos;
}
if (count > 0) count = fread(buf.data(), 1, count, this->file);
this->pos += count;
return count;
}
public:
SQFile(FileHandle file, size_t size) : file(std::move(file)), size(size), pos(0) {}
SQFile(FileHandle file, size_t size) : file(std::move(file)), size(size), pos(0), consumer(buffer) {}
size_t Read(void *buf, size_t elemsize, size_t count)
StringConsumer &GetConsumer(size_t min_size = 64)
{
assert(elemsize != 0);
if (this->pos + (elemsize * count) > this->size) {
count = (this->size - this->pos) / elemsize;
if (this->consumer.GetBytesLeft() < min_size && this->pos < this->size) {
this->buffer.erase(0, this->consumer.GetBytesRead());
size_t buffer_size = this->buffer.size();
size_t read_size = Align(min_size - buffer_size, 4096); // read pages of 4096 bytes
/* TODO C++23: use std::string::resize_and_overwrite() */
this->buffer.resize(buffer_size + read_size);
auto dest = std::span(this->buffer.data(), this->buffer.size()).subspan(buffer_size);
buffer_size += this->ReadInternal(dest);
this->buffer.resize(buffer_size);
this->consumer = StringConsumer(this->buffer);
}
if (count == 0) return 0;
size_t ret = fread(buf, elemsize, count, this->file);
this->pos += ret * elemsize;
return ret;
return this->consumer;
}
size_t Read(void *buf, size_t max_size)
{
std::span<char> dest(reinterpret_cast<char *>(buf), max_size);
auto view = this->consumer.Read(max_size);
std::copy(view.data(), view.data() + view.size(), dest.data());
size_t result_size = view.size();
if (result_size < max_size) {
assert(!this->consumer.AnyBytesLeft());
result_size += this->ReadInternal(dest.subspan(result_size));
}
return result_size;
}
};
static char32_t _io_file_lexfeed_ASCII(SQUserPointer file)
{
unsigned char c;
if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) return c;
return 0;
StringConsumer &consumer = reinterpret_cast<SQFile *>(file)->GetConsumer();
return consumer.TryReadUint8().value_or(0); // read as unsigned, otherwise integer promotion breaks it
}
static char32_t _io_file_lexfeed_UTF8(SQUserPointer file)
{
char buffer[5];
/* Read the first character, and get the length based on UTF-8 specs. If invalid, bail out. */
if (((SQFile *)file)->Read(buffer, sizeof(buffer[0]), 1) != 1) return 0;
uint len = Utf8EncodedCharLen(buffer[0]);
if (len == 0) return -1;
/* Read the remaining bits. */
if (len > 1 && ((SQFile *)file)->Read(buffer + 1, sizeof(buffer[0]), len - 1) != len - 1) return 0;
/* Convert the character, and when definitely invalid, bail out as well. */
char32_t c;
if (Utf8Decode(&c, buffer) != len) return -1;
return c;
StringConsumer &consumer = reinterpret_cast<SQFile *>(file)->GetConsumer();
return consumer.AnyBytesLeft() ? consumer.ReadUtf8(-1) : 0;
}
static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger size)
{
SQInteger ret = ((SQFile *)file)->Read(buf, 1, size);
SQInteger ret = reinterpret_cast<SQFile *>(file)->Read(buf, size);
if (ret == 0) return -1;
return ret;
}
@@ -600,10 +625,10 @@ SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const std::string &filename, SQBool
std::optional<FileHandle> file = std::nullopt;
size_t size;
if (strncmp(this->GetAPIName(), "AI", 2) == 0) {
if (this->GetAPIName().starts_with("AI")) {
file = FioFOpenFile(filename, "rb", AI_DIR, &size);
if (!file.has_value()) file = FioFOpenFile(filename, "rb", AI_LIBRARY_DIR, &size);
} else if (strncmp(this->GetAPIName(), "GS", 2) == 0) {
} else if (this->GetAPIName().starts_with("GS")) {
file = FioFOpenFile(filename, "rb", GAME_DIR, &size);
if (!file.has_value()) file = FioFOpenFile(filename, "rb", GAME_LIBRARY_DIR, &size);
} else {
+24 -24
View File
@@ -26,14 +26,14 @@ class Squirrel {
friend class ScriptInstance;
private:
typedef void (SQPrintFunc)(bool error_msg, const std::string &message);
using SQPrintFunc = void (bool error_msg, std::string_view message);
HSQUIRRELVM vm; ///< The VirtualMachine instance for squirrel
void *global_pointer; ///< Can be set by who ever initializes Squirrel
SQPrintFunc *print_func; ///< Points to either nullptr, or a custom print handler
bool crashed; ///< True if the squirrel script made an error.
int overdrawn_ops; ///< The amount of operations we have overdrawn.
const char *APIName; ///< Name of the API used for this squirrel.
std::string_view api_name; ///< Name of the API used for this squirrel.
std::unique_ptr<ScriptAllocator> allocator; ///< Allocator object used by this script.
/**
@@ -44,7 +44,7 @@ private:
/**
* Get the API name.
*/
const char *GetAPIName() { return this->APIName; }
std::string_view GetAPIName() { return this->api_name; }
/** Perform all initialization steps to create the engine. */
void Initialize();
@@ -55,25 +55,25 @@ protected:
/**
* The CompileError handler.
*/
static void CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column);
static void CompileError(HSQUIRRELVM vm, std::string_view desc, std::string_view source, SQInteger line, SQInteger column);
/**
* The RunError handler.
*/
static void RunError(HSQUIRRELVM vm, const SQChar *error);
static void RunError(HSQUIRRELVM vm, std::string_view error);
/**
* If a user runs 'print' inside a script, this function gets the params.
*/
static void PrintFunc(HSQUIRRELVM vm, const std::string &s);
static void PrintFunc(HSQUIRRELVM vm, std::string_view s);
/**
* If an error has to be print, this function is called.
*/
static void ErrorPrintFunc(HSQUIRRELVM vm, const std::string &s);
static void ErrorPrintFunc(HSQUIRRELVM vm, std::string_view s);
public:
Squirrel(const char *APIName);
Squirrel(std::string_view api_name);
~Squirrel();
/**
@@ -98,39 +98,39 @@ public:
* Adds a function to the stack. Depending on the current state this means
* either a method or a global function.
*/
void AddMethod(const char *method_name, SQFUNCTION proc, uint nparam = 0, const char *params = nullptr, void *userdata = nullptr, int size = 0);
void AddMethod(std::string_view method_name, SQFUNCTION proc, std::string_view params = {}, void *userdata = nullptr, int size = 0);
/**
* Adds a const to the stack. Depending on the current state this means
* either a const to a class or to the global space.
*/
void AddConst(const char *var_name, int value);
void AddConst(std::string_view var_name, int value);
/**
* Adds a const to the stack. Depending on the current state this means
* either a const to a class or to the global space.
*/
void AddConst(const char *var_name, uint value) { this->AddConst(var_name, (int)value); }
void AddConst(std::string_view var_name, uint value) { this->AddConst(var_name, (int)value); }
void AddConst(const char *var_name, const ConvertibleThroughBase auto &value) { this->AddConst(var_name, static_cast<int>(value.base())); }
void AddConst(std::string_view var_name, const ConvertibleThroughBase auto &value) { this->AddConst(var_name, static_cast<int>(value.base())); }
/**
* Adds a const to the stack. Depending on the current state this means
* either a const to a class or to the global space.
*/
void AddConst(const char *var_name, bool value);
void AddConst(std::string_view var_name, bool value);
/**
* Adds a class to the global scope. Make sure to call AddClassEnd when you
* are done adding methods.
*/
void AddClassBegin(const char *class_name);
void AddClassBegin(std::string_view class_name);
/**
* Adds a class to the global scope, extending 'parent_class'.
* Make sure to call AddClassEnd when you are done adding methods.
*/
void AddClassBegin(const char *class_name, const char *parent_class);
void AddClassBegin(std::string_view class_name, std::string_view parent_class);
/**
* Finishes adding a class to the global scope. If this isn't called, no
@@ -162,16 +162,16 @@ public:
* Call a method of an instance, in various flavors.
* @return False if the script crashed or returned a wrong type.
*/
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend);
bool CallMethod(HSQOBJECT instance, const char *method_name, int suspend) { return this->CallMethod(instance, method_name, nullptr, suspend); }
bool CallStringMethod(HSQOBJECT instance, const char *method_name, std::string *res, int suspend);
bool CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend);
bool CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend);
bool CallMethod(HSQOBJECT instance, std::string_view method_name, HSQOBJECT *ret, int suspend);
bool CallMethod(HSQOBJECT instance, std::string_view method_name, int suspend) { return this->CallMethod(instance, method_name, nullptr, suspend); }
bool CallStringMethod(HSQOBJECT instance, std::string_view method_name, std::string *res, int suspend);
bool CallIntegerMethod(HSQOBJECT instance, std::string_view method_name, int *res, int suspend);
bool CallBoolMethod(HSQOBJECT instance, std::string_view method_name, bool *res, int suspend);
/**
* Check if a method exists in an instance.
*/
bool MethodExists(HSQOBJECT instance, const char *method_name);
bool MethodExists(HSQOBJECT instance, std::string_view method_name);
/**
* Creates a class instance.
@@ -195,7 +195,7 @@ public:
* @note This will only work just after a function-call from within Squirrel
* to your C++ function.
*/
static SQUserPointer GetRealInstance(HSQUIRRELVM vm, int index, const char *tag);
static SQUserPointer GetRealInstance(HSQUIRRELVM vm, int index, std::string_view tag);
/**
* Get the Squirrel-instance pointer.
@@ -207,7 +207,7 @@ public:
/**
* Convert a Squirrel-object to a string.
*/
static const char *ObjectToString(HSQOBJECT *ptr) { return sq_objtostring(ptr); }
static std::optional<std::string_view> ObjectToString(HSQOBJECT *ptr) { return sq_objtostring(ptr); }
/**
* Convert a Squirrel-object to an integer.
@@ -238,7 +238,7 @@ public:
/**
* Throw a Squirrel error that will be nicely displayed to the user.
*/
void ThrowError(const std::string_view error) { sq_throwerror(this->vm, error); }
void ThrowError(std::string_view error) { sq_throwerror(this->vm, error); }
/**
* Release a SQ object.
+29 -49
View File
@@ -19,111 +19,91 @@
template <class CL, ScriptType ST>
class DefSQClass {
private:
const char *classname;
std::string_view classname;
public:
DefSQClass(const char *_classname) :
DefSQClass(std::string_view _classname) :
classname(_classname)
{}
/**
* This defines a method inside a class for Squirrel.
* This defines a method inside a class for Squirrel with defined params.
* @note If you define params, make sure that the first param is always 'x',
* which is the 'this' inside the function. This is hidden from the rest
* of the code, but without it calling your function will fail!
*/
template <typename Func>
void DefSQMethod(Squirrel *engine, Func function_proc, const char *function_name)
void DefSQMethod(Squirrel &engine, Func function_proc, std::string_view function_name, std::string_view params = {})
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQNonStaticCallback<CL, Func, ST>, 0, nullptr, &function_proc, sizeof(function_proc));
engine.AddMethod(function_name, DefSQNonStaticCallback<CL, Func, ST>, params, &function_proc, sizeof(function_proc));
}
/**
* This defines a method inside a class for Squirrel, which has access to the 'engine' (experts only!).
*/
template <typename Func>
void DefSQAdvancedMethod(Squirrel *engine, Func function_proc, const char *function_name)
void DefSQAdvancedMethod(Squirrel &engine, Func function_proc, std::string_view function_name)
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQAdvancedNonStaticCallback<CL, Func, ST>, 0, nullptr, &function_proc, sizeof(function_proc));
engine.AddMethod(function_name, DefSQAdvancedNonStaticCallback<CL, Func, ST>, {}, &function_proc, sizeof(function_proc));
}
/**
* This defines a method inside a class for Squirrel with defined params.
* @note If you define nparam, make sure that the first param is always 'x',
* This defines a static method inside a class for Squirrel with defined params.
* @note If you define params, make sure that the first param is always 'x',
* which is the 'this' inside the function. This is hidden from the rest
* of the code, but without it calling your function will fail!
*/
template <typename Func>
void DefSQMethod(Squirrel *engine, Func function_proc, const char *function_name, int nparam, const char *params)
void DefSQStaticMethod(Squirrel &engine, Func function_proc, std::string_view function_name, std::string_view params = {})
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQNonStaticCallback<CL, Func, ST>, nparam, params, &function_proc, sizeof(function_proc));
}
/**
* This defines a static method inside a class for Squirrel.
*/
template <typename Func>
void DefSQStaticMethod(Squirrel *engine, Func function_proc, const char *function_name)
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQStaticCallback<CL, Func>, 0, nullptr, &function_proc, sizeof(function_proc));
engine.AddMethod(function_name, DefSQStaticCallback<CL, Func>, params, &function_proc, sizeof(function_proc));
}
/**
* This defines a static method inside a class for Squirrel, which has access to the 'engine' (experts only!).
*/
template <typename Func>
void DefSQAdvancedStaticMethod(Squirrel *engine, Func function_proc, const char *function_name)
void DefSQAdvancedStaticMethod(Squirrel &engine, Func function_proc, std::string_view function_name)
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQAdvancedStaticCallback<CL, Func>, 0, nullptr, &function_proc, sizeof(function_proc));
}
/**
* This defines a static method inside a class for Squirrel with defined params.
* @note If you define nparam, make sure that the first param is always 'x',
* which is the 'this' inside the function. This is hidden from the rest
* of the code, but without it calling your function will fail!
*/
template <typename Func>
void DefSQStaticMethod(Squirrel *engine, Func function_proc, const char *function_name, int nparam, const char *params)
{
using namespace SQConvert;
engine->AddMethod(function_name, DefSQStaticCallback<CL, Func>, nparam, params, &function_proc, sizeof(function_proc));
engine.AddMethod(function_name, DefSQAdvancedStaticCallback<CL, Func>, {}, &function_proc, sizeof(function_proc));
}
template <typename Var>
void DefSQConst(Squirrel *engine, Var value, const char *var_name)
void DefSQConst(Squirrel &engine, Var value, std::string_view var_name)
{
engine->AddConst(var_name, value);
engine.AddConst(var_name, value);
}
void PreRegister(Squirrel *engine)
void PreRegister(Squirrel &engine)
{
engine->AddClassBegin(this->classname);
engine.AddClassBegin(this->classname);
}
void PreRegister(Squirrel *engine, const char *parent_class)
void PreRegister(Squirrel &engine, std::string_view parent_class)
{
engine->AddClassBegin(this->classname, parent_class);
engine.AddClassBegin(this->classname, parent_class);
}
template <typename Func, int Tnparam>
void AddConstructor(Squirrel *engine, const char *params)
template <typename Func>
void AddConstructor(Squirrel &engine, std::string_view params)
{
using namespace SQConvert;
engine->AddMethod("constructor", DefSQConstructorCallback<CL, Func, Tnparam>, Tnparam, params);
engine.AddMethod("constructor", DefSQConstructorCallback<CL, Func>, params);
}
void AddSQAdvancedConstructor(Squirrel *engine)
void AddSQAdvancedConstructor(Squirrel &engine)
{
using namespace SQConvert;
engine->AddMethod("constructor", DefSQAdvancedConstructorCallback<CL>, 0, nullptr);
engine.AddMethod("constructor", DefSQAdvancedConstructorCallback<CL>);
}
void PostRegister(Squirrel *engine)
void PostRegister(Squirrel &engine)
{
engine->AddClassEnd();
engine.AddClassEnd();
}
};
+14 -11
View File
@@ -61,7 +61,7 @@ namespace SQConvert {
static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
{
if (res.has_value()) {
sq_pushstring(vm, res.value(), -1);
sq_pushstring(vm, res.value());
} else {
sq_pushnull(vm);
}
@@ -110,9 +110,9 @@ namespace SQConvert {
/* Convert what-ever there is as parameter to a string */
sq_tostring(vm, index);
const SQChar *tmp;
sq_getstring(vm, -1, &tmp);
std::string result = StrMakeValid(tmp);
std::string_view view;
sq_getstring(vm, -1, view);
std::string result = StrMakeValid(view);
sq_poptop(vm);
return result;
}
@@ -187,9 +187,9 @@ namespace SQConvert {
return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
}
static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
static Tcls *SQConstruct(HSQUIRRELVM vm)
{
return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
return SQConstruct(vm, std::index_sequence_for<Targs...>{});
}
private:
@@ -210,7 +210,7 @@ namespace SQConvert {
}
template <size_t... i>
static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
static Tcls *SQConstruct([[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
{
Tcls *inst = new Tcls(
Param<Targs>::Get(vm, 2 + i)...
@@ -376,14 +376,17 @@ namespace SQConvert {
* params. It creates the instance in C++, and it sets all the needed
* settings in SQ to register the instance.
*/
template <typename Tcls, typename Tmethod, int Tnparam>
template <typename Tcls, typename Tmethod>
inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
{
try {
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
/* Create the real instance */
Tcls *instance = HelperT<Tmethod>::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
sq_setinstanceup(vm, -Tnparam, instance);
sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback<Tcls>);
Tcls *instance = HelperT<Tmethod>::SQConstruct(vm);
sq_setinstanceup(vm, -nparam, instance);
sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
instance->AddRef();
return 0;
} catch (SQInteger &e) {
+10 -14
View File
@@ -41,20 +41,16 @@ SQInteger SquirrelStd::max(HSQUIRRELVM vm)
SQInteger SquirrelStd::require(HSQUIRRELVM vm)
{
SQInteger top = sq_gettop(vm);
const SQChar *filename;
std::string_view filename;
sq_getstring(vm, 2, &filename);
sq_getstring(vm, 2, filename);
/* Get the script-name of the current file, so we can work relative from it */
SQStackInfos si;
sq_stackinfos(vm, 1, &si);
if (si.source == nullptr) {
Debug(misc, 0, "[squirrel] Couldn't detect the script-name of the 'require'-caller; this should never happen!");
return SQ_ERROR;
}
/* Keep the dir, remove the rest */
std::string path = si.source;
std::string path{si.source};
auto p = path.find_last_of(PATHSEPCHAR);
/* Keep the PATHSEPCHAR there, remove the rest */
if (p != std::string::npos) path.erase(p + 1);
@@ -86,20 +82,20 @@ SQInteger SquirrelStd::notifyallexceptions(HSQUIRRELVM vm)
return SQ_ERROR;
}
void squirrel_register_global_std(Squirrel *engine)
void squirrel_register_global_std(Squirrel &engine)
{
/* We don't use squirrel_helper here, as we want to register to the global
* scope and not to a class. */
engine->AddMethod("require", &SquirrelStd::require, 2, ".s");
engine->AddMethod("notifyallexceptions", &SquirrelStd::notifyallexceptions, 2, ".b");
engine.AddMethod("require", &SquirrelStd::require, ".s");
engine.AddMethod("notifyallexceptions", &SquirrelStd::notifyallexceptions, ".b");
}
void squirrel_register_std(Squirrel *engine)
void squirrel_register_std(Squirrel &engine)
{
/* We don't use squirrel_helper here, as we want to register to the global
* scope and not to a class. */
engine->AddMethod("min", &SquirrelStd::min, 3, ".ii");
engine->AddMethod("max", &SquirrelStd::max, 3, ".ii");
engine.AddMethod("min", &SquirrelStd::min, ".ii");
engine.AddMethod("max", &SquirrelStd::max, ".ii");
sqstd_register_mathlib(engine->GetVM());
sqstd_register_mathlib(engine.GetVM());
}
+2 -2
View File
@@ -52,12 +52,12 @@ public:
/**
* Register all standard functions we want to give to a script.
*/
void squirrel_register_std(Squirrel *engine);
void squirrel_register_std(Squirrel &engine);
/**
* Register all standard functions that are available on first startup.
* @note this set is very limited, and is only meant to load other scripts and things like that.
*/
void squirrel_register_global_std(Squirrel *engine);
void squirrel_register_global_std(Squirrel &engine);
#endif /* SQUIRREL_STD_HPP */