OpenTTD Source: src/script/script_info.cpp Source File

OpenTTD Source 15.3

script_info.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "../settings_type.h"
12
13#include "squirrel_helper.hpp"
14
15#include "script_info.hpp"
16#include "script_scanner.hpp"
17#include "../core/string_consumer.hpp"
18#include "../3rdparty/fmt/format.h"
19
20#include "../safeguards.h"
21
22 bool ScriptInfo::CheckMethod(std::string_view name) const
23{
24 if (!this->engine->MethodExists(this->SQ_instance, name)) {
25 this->engine->ThrowError(fmt::format("your info.nut/library.nut doesn't have the method '{}'", name));
26 return false;
27 }
28 return true;
29}
30
31/* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo &info)
32{
33 /* Set some basic info from the parent */
34 Squirrel::GetInstance(vm, &info.SQ_instance, 2);
35 /* Make sure the instance stays alive over time */
36 sq_addref(vm, &info.SQ_instance);
37
38 info.scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
39 info.engine = info.scanner->GetEngine();
40
41 /* Ensure the mandatory functions exist */
42 static const std::string_view required_functions[] = {
43 "GetAuthor",
44 "GetName",
45 "GetShortName",
46 "GetDescription",
47 "GetVersion",
48 "GetDate",
49 "CreateInstance",
50 };
51 for (const auto &required_function : required_functions) {
52 if (!info.CheckMethod(required_function)) return SQ_ERROR;
53 }
54
55 /* Get location information of the scanner */
56 info.main_script = info.scanner->GetMainScript();
57 info.tar_file = info.scanner->GetTarFile();
58
59 /* Cache the data the info file gives us. */
60 if (!info.engine->CallStringMethod(info.SQ_instance, "GetAuthor", &info.author, MAX_GET_OPS)) return SQ_ERROR;
61 if (!info.engine->CallStringMethod(info.SQ_instance, "GetName", &info.name, MAX_GET_OPS)) return SQ_ERROR;
62 if (!info.engine->CallStringMethod(info.SQ_instance, "GetShortName", &info.short_name, MAX_GET_OPS)) return SQ_ERROR;
63 if (!info.engine->CallStringMethod(info.SQ_instance, "GetDescription", &info.description, MAX_GET_OPS)) return SQ_ERROR;
64 if (!info.engine->CallStringMethod(info.SQ_instance, "GetDate", &info.date, MAX_GET_OPS)) return SQ_ERROR;
65 if (!info.engine->CallIntegerMethod(info.SQ_instance, "GetVersion", &info.version, MAX_GET_OPS)) return SQ_ERROR;
66 if (info.version < 0) return SQ_ERROR;
67 if (!info.engine->CallStringMethod(info.SQ_instance, "CreateInstance", &info.instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR;
68
69 /* The GetURL function is optional. */
70 if (info.engine->MethodExists(info.SQ_instance, "GetURL")) {
71 if (!info.engine->CallStringMethod(info.SQ_instance, "GetURL", &info.url, MAX_GET_OPS)) return SQ_ERROR;
72 }
73
74 /* Check if we have settings */
75 if (info.engine->MethodExists(info.SQ_instance, "GetSettings")) {
76 if (!info.GetSettings()) return SQ_ERROR;
77 }
78
79 return 0;
80}
81
82 bool ScriptInfo::GetSettings()
83{
84 return this->engine->CallMethod(this->SQ_instance, "GetSettings", nullptr, MAX_GET_SETTING_OPS);
85}
86
87enum class ScriptConfigItemKey : uint8_t {
88 Name,
89 Description,
90 MinValue,
91 MaxValue,
92 MediumValue,
93 DefaultValue,
94 Flags,
95};
96using ScriptConfigItemKeys = EnumBitSet<ScriptConfigItemKey, uint8_t>;
97
98SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
99{
100 ScriptConfigItem config;
101 ScriptConfigItemKeys present{};
102
103 int medium_value = INT32_MIN;
104
105 /* Read the table, and find all properties we care about */
106 sq_pushnull(vm);
107 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
108 std::string_view key_string;
109 if (SQ_FAILED(sq_getstring(vm, -2, key_string))) return SQ_ERROR;
110 std::string key = StrMakeValid(key_string);
111
112 if (key == "name") {
113 std::string_view sqvalue;
114 if (SQ_FAILED(sq_getstring(vm, -1, sqvalue))) return SQ_ERROR;
115
116 /* Don't allow '=' and ',' in configure setting names, as we need those
117 * 2 chars to nicely store the settings as a string. */
118 auto replace_with_underscore = [](auto c) { return c == '=' || c == ','; };
119 config.name = StrMakeValid(sqvalue);
120 std::replace_if(config.name.begin(), config.name.end(), replace_with_underscore, '_');
121 present.Set(ScriptConfigItemKey::Name);
122 } else if (key == "description") {
123 std::string_view sqdescription;
124 if (SQ_FAILED(sq_getstring(vm, -1, sqdescription))) return SQ_ERROR;
125 config.description = StrMakeValid(sqdescription);
126 present.Set(ScriptConfigItemKey::Description);
127 } else if (key == "min_value") {
128 SQInteger res;
129 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
130 config.min_value = ClampTo<int32_t>(res);
131 present.Set(ScriptConfigItemKey::MinValue);
132 } else if (key == "max_value") {
133 SQInteger res;
134 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
135 config.max_value = ClampTo<int32_t>(res);
136 present.Set(ScriptConfigItemKey::MaxValue);
137 } else if (key == "easy_value") {
138 /* No longer parsed. */
139 } else if (key == "medium_value") {
140 SQInteger res;
141 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
142 medium_value = ClampTo<int32_t>(res);
143 present.Set(ScriptConfigItemKey::MediumValue);
144 } else if (key == "hard_value") {
145 /* No longer parsed. */
146 } else if (key == "custom_value") {
147 /* No longer parsed. */
148 } else if (key == "default_value") {
149 SQInteger res;
150 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
151 config.default_value = ClampTo<int32_t>(res);
152 present.Set(ScriptConfigItemKey::DefaultValue);
153 } else if (key == "random_deviation") {
154 /* No longer parsed. */
155 } else if (key == "step_size") {
156 SQInteger res;
157 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
158 config.step_size = ClampTo<int32_t>(res);
159 } else if (key == "flags") {
160 SQInteger res;
161 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
162 config.flags = static_cast<ScriptConfigFlags>(res);
163 present.Set(ScriptConfigItemKey::Flags);
164 } else {
165 this->engine->ThrowError(fmt::format("unknown setting property '{}'", key));
166 return SQ_ERROR;
167 }
168
169 sq_pop(vm, 2);
170 }
171 sq_pop(vm, 1);
172
173 /* Check if default_value is set. Although required, this was changed with
174 * 14.0, and as such, older AIs don't use it yet. So we convert the older
175 * values into a default_value. */
176 if (!present.Test(ScriptConfigItemKey::DefaultValue)) {
177 /* Easy/medium/hard should all three be defined. */
178 if (!present.Test(ScriptConfigItemKey::MediumValue)) {
179 this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
180 return SQ_ERROR;
181 }
182
183 config.default_value = medium_value;
184 present.Set(ScriptConfigItemKey::DefaultValue);
185 }
186
187 /* Make sure all required properties are defined */
188 ScriptConfigItemKeys required = {ScriptConfigItemKey::Name, ScriptConfigItemKey::Description, ScriptConfigItemKey::DefaultValue, ScriptConfigItemKey::Flags};
189 if (!config.flags.Test(ScriptConfigFlag::Boolean)) required.Set({ScriptConfigItemKey::MinValue, ScriptConfigItemKey::MaxValue});
190
191 if (!present.All(required)) {
192 this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
193 return SQ_ERROR;
194 }
195
196 this->config_list.emplace_back(config);
197 return 0;
198}
199
200SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
201{
202 std::string_view setting_name_view;
203 if (SQ_FAILED(sq_getstring(vm, -2, setting_name_view))) return SQ_ERROR;
204 std::string setting_name = StrMakeValid(setting_name_view);
205
206 ScriptConfigItem *config = nullptr;
207 for (auto &item : this->config_list) {
208 if (item.name == setting_name) config = &item;
209 }
210
211 if (config == nullptr) {
212 this->engine->ThrowError(fmt::format("Trying to add labels for non-defined setting '{}'", setting_name));
213 return SQ_ERROR;
214 }
215 if (!config->labels.empty()) return SQ_ERROR;
216
217 /* Read the table and find all labels */
218 sq_pushnull(vm);
219 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
220 std::string_view key_string;
221 std::string_view label;
222 if (SQ_FAILED(sq_getstring(vm, -2, key_string))) return SQ_ERROR;
223 if (SQ_FAILED(sq_getstring(vm, -1, label))) return SQ_ERROR;
224 /* Because squirrel doesn't support identifiers starting with a digit,
225 * we skip the first character. */
226 key_string.remove_prefix(1);
227 int sign = 1;
228 if (key_string.starts_with('_')) {
229 /* When the second character is '_', it indicates the value is negative. */
230 sign = -1;
231 key_string.remove_prefix(1);
232 }
233 auto key = ParseInteger<int>(key_string);
234 if (!key.has_value()) return SQ_ERROR;
235 config->labels[*key * sign] = StrMakeValid(label);
236
237 sq_pop(vm, 2);
238 }
239 sq_pop(vm, 1);
240
241 /* Check labels for completeness */
242 config->complete_labels = true;
243 for (int value = config->min_value; value <= config->max_value; value++) {
244 if (config->labels.find(value) == config->labels.end()) {
245 config->complete_labels = false;
246 break;
247 }
248 }
249
250 return 0;
251}
252
253 const ScriptConfigItemList *ScriptInfo::GetConfigList() const
254{
255 return &this->config_list;
256}
257
258 const ScriptConfigItem *ScriptInfo::GetConfigItem(std::string_view name) const
259{
260 for (const auto &item : this->config_list) {
261 if (item.name == name) return &item;
262 }
263 return nullptr;
264}
265
266 int ScriptInfo::GetSettingDefaultValue(const std::string &name) const
267{
268 for (const auto &item : this->config_list) {
269 if (item.name != name) continue;
270 return item.default_value;
271 }
272
273 /* There is no such setting */
274 return -1;
275}
BaseBitSet::Test
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Definition base_bitset_type.hpp:141
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
EnumBitSet
Enum-as-bit-set wrapper.
Definition enum_type.hpp:177
ScriptInfo
All static information from an Script like name, version, etc.
Definition script_info.hpp:30
ScriptInfo::CheckMethod
bool CheckMethod(std::string_view name) const
Check if a given method exists.
Definition script_info.cpp:22
ScriptInfo::short_name
std::string short_name
Short name (4 chars) which uniquely identifies the script.
Definition script_info.hpp:142
ScriptInfo::version
int version
Version of the script.
Definition script_info.hpp:146
ScriptInfo::scanner
class ScriptScanner * scanner
ScriptScanner object that was used to scan this script info.
Definition script_info.hpp:149
ScriptInfo::description
std::string description
Small description of the script.
Definition script_info.hpp:143
ScriptInfo::config_list
ScriptConfigItemList config_list
List of settings from this Script.
Definition script_info.hpp:135
ScriptInfo::GetConfigList
const ScriptConfigItemList * GetConfigList() const
Get the config list for this Script.
Definition script_info.cpp:253
ScriptInfo::engine
class Squirrel * engine
Engine used to register for Squirrel.
Definition script_info.hpp:133
ScriptInfo::name
std::string name
Full name of the script.
Definition script_info.hpp:141
ScriptInfo::Constructor
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo &info)
Process the creation of a FileInfo object.
Definition script_info.cpp:31
ScriptInfo::AddSetting
SQInteger AddSetting(HSQUIRRELVM vm)
Set a setting.
Definition script_info.cpp:98
ScriptInfo::GetConfigItem
const ScriptConfigItem * GetConfigItem(std::string_view name) const
Get the description of a certain Script config option.
Definition script_info.cpp:258
ScriptInfo::main_script
std::string main_script
The full path of the script.
Definition script_info.hpp:138
ScriptInfo::AddLabels
SQInteger AddLabels(HSQUIRRELVM vm)
Add labels for a setting.
Definition script_info.cpp:200
ScriptInfo::instance_name
std::string instance_name
Name of the main class in the script.
Definition script_info.hpp:145
ScriptInfo::GetSettings
bool GetSettings()
Get the settings of the Script.
Definition script_info.cpp:82
ScriptInfo::SQ_instance
HSQOBJECT SQ_instance
The Squirrel instance created for this info.
Definition script_info.hpp:134
ScriptInfo::author
std::string author
Author of the script.
Definition script_info.hpp:140
ScriptInfo::GetSettingDefaultValue
int GetSettingDefaultValue(const std::string &name) const
Get the default value for a setting.
Definition script_info.cpp:266
ScriptInfo::tar_file
std::string tar_file
If, which tar file the script was in.
Definition script_info.hpp:139
ScriptInfo::url
std::string url
URL of the script.
Definition script_info.hpp:147
ScriptInfo::date
std::string date
The date the script was written at.
Definition script_info.hpp:144
ScriptScanner
Scanner to help finding scripts.
Definition script_scanner.hpp:19
ScriptScanner::GetTarFile
std::string GetTarFile()
Get the current tar file the ScanDir is currently tracking.
Definition script_scanner.hpp:39
ScriptScanner::GetEngine
class Squirrel * GetEngine()
Get the engine of the main squirrel handler (it indexes all available scripts).
Definition script_scanner.hpp:29
ScriptScanner::GetMainScript
std::string GetMainScript()
Get the current main script the ScanDir is currently tracking.
Definition script_scanner.hpp:34
Squirrel::GetGlobalPointer
static void * GetGlobalPointer(HSQUIRRELVM vm)
Get the pointer as set by SetGlobalPointer.
Definition squirrel.hpp:237
Squirrel::ThrowError
void ThrowError(std::string_view error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition squirrel.hpp:247
Squirrel::GetInstance
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition squirrel.hpp:211
Squirrel::CallMethod
bool CallMethod(HSQOBJECT instance, std::string_view method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
Definition squirrel.cpp:376
Squirrel::MethodExists
bool MethodExists(HSQOBJECT instance, std::string_view method_name)
Check if a method exists in an instance.
Definition squirrel.cpp:323
VehicleCallbackMask::Name
@ Name
Engine name.
ScriptConfigItemList
std::vector< ScriptConfigItem > ScriptConfigItemList
List of ScriptConfig items.
Definition script_config.hpp:48
ScriptConfigFlag::Boolean
@ Boolean
This value is a boolean (either 0 (false) or 1 (true) ).
script_info.hpp
ScriptInfo keeps track of all information of a script, like Author, Description, ....
MAX_CREATEINSTANCE_OPS
static const int MAX_CREATEINSTANCE_OPS
Number of operations to create an instance of a script.
Definition script_info.hpp:23
MAX_GET_SETTING_OPS
static const int MAX_GET_SETTING_OPS
Maximum number of operations allowed for getting a particular setting.
Definition script_info.hpp:27
MAX_GET_OPS
static const int MAX_GET_OPS
Number of operations to get the author and similar information.
Definition script_info.hpp:25
script_scanner.hpp
Declarations of the class for the script scanner.
squirrel_helper.hpp
declarations and parts of the implementation of the class for convert code
StrMakeValid
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Definition string.cpp:119
ScriptConfigItem
Info about a single Script setting.
Definition script_config.hpp:33
ScriptConfigItem::flags
ScriptConfigFlags flags
Flags for the configuration setting.
Definition script_config.hpp:40
ScriptConfigItem::labels
LabelMapping labels
Text labels for the integer values.
Definition script_config.hpp:41
ScriptConfigItem::name
std::string name
The name of the configuration setting.
Definition script_config.hpp:34
ScriptConfigItem::default_value
int default_value
The default value of this configuration setting.
Definition script_config.hpp:38
ScriptConfigItem::min_value
int min_value
The minimal value this configuration setting can have.
Definition script_config.hpp:36
ScriptConfigItem::max_value
int max_value
The maximal value this configuration setting can have.
Definition script_config.hpp:37
ScriptConfigItem::step_size
int step_size
The step size in the gui.
Definition script_config.hpp:39
ScriptConfigItem::description
std::string description
The description of the configuration setting.
Definition script_config.hpp:35
ScriptConfigItem::complete_labels
bool complete_labels
True if all values have a label.
Definition script_config.hpp:42