Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
ScriptUtils.h
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2013-2023 Petr Ohlidal
4
5 For more information, see http://www.rigsofrods.org/
6
7 Rigs of Rods is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 3, as
9 published by the Free Software Foundation.
10
11 Rigs of Rods is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
18*/
19
21
22#pragma once
23
24#ifdef USE_ANGELSCRIPT
25
26#include <angelscript.h>
27#include "ScriptEngine.h"
28#include "scriptdictionary/scriptdictionary.h"
29#include "scriptarray/scriptarray.h"
30#include "scriptbuilder/scriptbuilder.h"
32
33#include <list>
34#include <map>
35#include <string>
36#include <vector>
37
38namespace RoR {
39
42
43template <typename T>
44AngelScript::CScriptArray* VectorToScriptArray(const std::vector<T>& vec, const std::string& decl)
45{
46 std::string arraydecl = fmt::format("array<{}>", decl);
47 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
48 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo, vec.size());
49
50 for (AngelScript::asUINT i = 0; i < arr->GetSize(); i++)
51 {
52 // Set the value of each element
53 T tempval = vec[i];
54 arr->SetValue(i, &tempval);
55 }
56
57 return arr;
58}
59
60template <typename T, typename U>
61AngelScript::CScriptArray* MapToScriptArray(std::map<T, U>& map, const std::string& decl)
62{
63 std::string arraydecl = fmt::format("array<{}>", decl);
64 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
65 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo, map.size());
66
67 for (auto itor = map.begin(); itor != map.end(); itor++)
68 {
69 // Set the value of each element
70 AngelScript::asUINT pos = static_cast<AngelScript::asUINT>(std::distance(map.begin(), itor));
71 arr->SetValue(pos, &itor->second);
72 }
73
74 return arr;
75}
76
77// Iterables:
78
79template <typename ItorT>
80AngelScript::CScriptArray* IterableMapToScriptArray(ItorT begin, ItorT end, const std::string& decl)
81{
82 std::string arraydecl = fmt::format("array<{}>", decl);
83 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
84 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo);
85
86 for (auto itor = begin; itor != end; itor++)
87 {
88 arr->InsertLast(&itor->second);
89 }
90
91 return arr;
92}
93
94template <typename ItorT>
95AngelScript::CScriptArray* IterableListToScriptArray(ItorT begin, ItorT end, const std::string& decl)
96{
97 std::string arraydecl = fmt::format("array<{}>", decl);
98 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
99 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo);
100
101 for (auto itor = begin; itor != end; itor++)
102 {
103 arr->InsertLast(&itor);
104 }
105
106 return arr;
107}
108
109template<typename T>
110bool GetValueFromScriptDict(const std::string& log_msg, AngelScript::CScriptDictionary* dict, bool required, std::string const& key, const char* decl, T & out_value)
111{
112 if (!dict)
113 {
114 // Dict is NULL
115 if (required)
116 {
117 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, no parameters; '{}' is required.", log_msg, key));
118 }
119 return false;
120 }
121 auto itor = dict->find(key);
122 if (itor == dict->end())
123 {
124 // Key not found
125 if (required)
126 {
127 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, required parameter '{}' not found.", log_msg, key));
128 }
129 return false;
130 }
131
132 const int expected_typeid = App::GetScriptEngine()->getEngine()->GetTypeIdByDecl(decl);
133 const int actual_typeid = itor.GetTypeId();
134 if (actual_typeid != expected_typeid)
135 {
136 // Wrong type
137 if (required)
138 {
139 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, required parameter '{}' must be a {}, instead got {}.",
140 log_msg, key, decl, App::GetScriptEngine()->getEngine()->GetTypeDeclaration(actual_typeid)));
141 }
142 return false;
143 }
144
145 return itor.GetValue(&out_value, actual_typeid); // Error will be logged to Angelscript.log
146}
147
148// Proof of concept: a read-only Dictionary view of a `std::map`-like container.
149// Assumed to always use `std::string` for key.
150// Not using RefCountingObject because this is only for use in the script, not C++
151// Adopted from 'scriptdictionary.cpp' bundled with AngelScript SDK
152template<typename T>
154{
155public:
156
157 CReadonlyScriptDictView(const std::map<std::string, T>& map) : m_map(map), m_refcount(1) {}
158
159 bool Exists(const std::string& key) const
160 {
161 return (m_map.find(key) != m_map.end());
162 }
163
164 bool IsEmpty() const
165 {
166 if (m_map.size() == 0)
167 return true;
168
169 return false;
170 }
171
172 AngelScript::asUINT GetSize() const
173 {
174 return AngelScript::asUINT(m_map.size());
175 }
176
177 T OpIndex(const std::string& key) const
178 {
179 auto it = m_map.find(key);
180 if (it != m_map.end())
181 return it->second;
182 else
183 return T{};
184 }
185
186 AngelScript::CScriptArray* GetKeys() const
187 {
188 // Create the array object
189 AngelScript::CScriptArray* array = AngelScript::CScriptArray::Create(AngelScript::asGetActiveContext()->GetEngine()->GetTypeInfoByDecl("array<string>"), AngelScript::asUINT(m_map.size()));
190 long current = -1;
191 for (auto it = m_map.begin(); it != m_map.end(); it++)
192 {
193 current++;
194 *(std::string*)array->At(current) = it->first;
195 }
196
197 return array;
198 }
199
200 static void RegisterReadonlyScriptDictView(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
201 {
202 using namespace AngelScript;
203
204 int r;
205
206 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptDictView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
207 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", asMETHOD(CReadonlyScriptDictView<T>, AddRef), asCALL_THISCALL); ROR_ASSERT(r >= 0);
208 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", asMETHOD(CReadonlyScriptDictView <T>, Release), asCALL_THISCALL); ROR_ASSERT(r >= 0);
209
210 r = engine->RegisterObjectMethod(decl, "bool exists(const string &in) const", asMETHOD(CReadonlyScriptDictView<T>, Exists), asCALL_THISCALL); ROR_ASSERT(r >= 0);
211 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", asMETHOD(CReadonlyScriptDictView<T>, IsEmpty), asCALL_THISCALL); ROR_ASSERT(r >= 0);
212 r = engine->RegisterObjectMethod(decl, "uint getSize() const", asMETHOD(CReadonlyScriptDictView<T>, GetSize), asCALL_THISCALL); ROR_ASSERT(r >= 0);
213 r = engine->RegisterObjectMethod(decl, "array<string> @getKeys() const", asMETHOD(CReadonlyScriptDictView<T>, GetKeys), asCALL_THISCALL); assert(r >= 0);
214
215 std::string opIndexDecl = fmt::format("{}@ opIndex(const string &in)", value_decl);
216 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), asMETHOD(CReadonlyScriptDictView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
217 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(const string &in) const", value_decl);
218 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), asMETHOD(CReadonlyScriptDictView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
219
220 }
221
222 static void RegisterReadonlyScriptDictViewGeneric(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
223 {
224 using namespace AngelScript;
225
226 int r;
227
228 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptDictView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
229 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", WRAP_MFN(CReadonlyScriptDictView<T>, AddRef), asCALL_GENERIC); ROR_ASSERT(r >= 0);
230 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", WRAP_MFN(CReadonlyScriptDictView<T>, Release), asCALL_GENERIC); ROR_ASSERT(r >= 0);
231
232 r = engine->RegisterObjectMethod(decl, "bool exists(const string &in) const", WRAP_MFN(CReadonlyScriptDictView<T>, Exists), asCALL_GENERIC); ROR_ASSERT(r >= 0);
233 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", WRAP_MFN(CReadonlyScriptDictView<T>, IsEmpty), asCALL_GENERIC); ROR_ASSERT(r >= 0);
234 r = engine->RegisterObjectMethod(decl, "uint getSize() const", WRAP_MFN(CReadonlyScriptDictView<T>, GetSize), asCALL_GENERIC); ROR_ASSERT(r >= 0);
235 r = engine->RegisterObjectMethod(decl, "array<string> @getKeys() const", WRAP_MFN(CReadonlyScriptDictView<T>, GetKeys), asCALL_GENERIC); ROR_ASSERT(r >= 0);
236
237 std::string opIndexDecl = fmt::format("{}@ opIndex(const string &in)", value_decl);
238 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), WRAP_MFN(CReadonlyScriptDictView<T>, OpIndex), asCALL_GENERIC); ROR_ASSERT(r >= 0);
239 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(const string &in) const", value_decl);
240 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), WRAP_MFN(CReadonlyScriptDictView<T>, OpIndex), asCALL_GENERIC); ROR_ASSERT(r >= 0);
241 }
242
243 // Angelscript refcounting
244 void AddRef() { m_refcount++; }
245 void Release() { m_refcount--; if (m_refcount == 0) { delete this; /* Comit suicide! This is legit in C++ and AngelScript relies on it. */ } }
246private:
247 const std::map<std::string, T>& m_map;
249};
250
251template <typename T>
253{
254public:
255
256 CReadonlyScriptArrayView(const std::vector<T>& vec) : m_vec(vec), m_refcount(1) {}
257
258 bool IsEmpty() const { return m_vec.empty(); }
259 unsigned GetSize() const { return m_vec.size(); }
260 T OpIndex(unsigned pos) { return m_vec.at(pos); }
261
262 static void RegisterReadonlyScriptArrayView(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
263 {
264 using namespace AngelScript;
265
266 int r;
267
268 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptArrayView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
269 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", asMETHOD(CReadonlyScriptArrayView<T>, AddRef), asCALL_THISCALL); ROR_ASSERT(r >= 0);
270 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", asMETHOD(CReadonlyScriptArrayView <T>, Release), asCALL_THISCALL); ROR_ASSERT(r >= 0);
271
272 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", asMETHOD(CReadonlyScriptArrayView<T>, IsEmpty), asCALL_THISCALL); ROR_ASSERT(r >= 0);
273 r = engine->RegisterObjectMethod(decl, "uint length() const", asMETHOD(CReadonlyScriptArrayView<T>, GetSize), asCALL_THISCALL); ROR_ASSERT(r >= 0);
274
275 std::string opIndexDecl = fmt::format("{}@ opIndex(uint pos)", value_decl);
276 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), asMETHOD(CReadonlyScriptArrayView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
277 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(uint pos) const", value_decl);
278 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), asMETHOD(CReadonlyScriptArrayView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
279
280 }
281
282 static void RegisterReadonlyScriptArrayViewGeneric(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
283 {
284 using namespace AngelScript;
285
286 int r;
287
288 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptArrayView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
289 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", WRAP_MFN(CReadonlyScriptArrayView<T>, AddRef), asCALL_GENERIC); ROR_ASSERT(r >= 0);
290 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", WRAP_MFN(CReadonlyScriptArrayView<T>, Release), asCALL_GENERIC); ROR_ASSERT(r >= 0);
291
292 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", WRAP_MFN(CReadonlyScriptArrayView<T>, IsEmpty), asCALL_GENERIC); ROR_ASSERT(r >= 0);
293 r = engine->RegisterObjectMethod(decl, "uint length() const", WRAP_MFN(CReadonlyScriptArrayView<T>, GetSize), asCALL_GENERIC); ROR_ASSERT(r >= 0);
294
295 std::string opIndexDecl = fmt::format("{}@ opIndex(uint pos)", value_decl);
296 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), WRAP_MFN(CReadonlyScriptArrayView<T>, OpIndex), asCALL_GENERIC); ROR_ASSERT(r >= 0);
297 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(uint pos) const", value_decl);
298 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), WRAP_MFN(CReadonlyScriptArrayView<T>, OpIndex), asCALL_GENERIC); ROR_ASSERT(r >= 0);
299 }
300
301 // Angelscript refcounting
302 void AddRef() { m_refcount++; }
303 void Release() { m_refcount--; if (m_refcount == 0) { delete this; /* Comit suicide! This is legit in C++ and AngelScript relies on it. */ } }
304private:
305 const std::vector<T>& m_vec;
307};
308
309// Example opCast behaviour
310template<class A, class B>
312{
313 // If the handle already is a null handle, then just return the null handle
314 if (!a) return 0;
315
316 // Now try to dynamically cast the pointer to the wanted type
317 B* b = dynamic_cast<B*>(a);
318 if (b != 0)
319 {
320 // Since the cast was made, we need to increase the ref counter for the returned handle
321 b->AddRef();
322 }
323 return b;
324}
325
326// Example opCast behaviour - for objects with asOBJ_NOCOUNT
327template<class A, class B>
329{
330 // If the handle already is a null handle, then just return the null handle
331 if (!a) return 0;
332
333 // Now try to dynamically cast the pointer to the wanted type
334 return dynamic_cast<B*>(a);
335}
336
338
339} // namespace RoR
340
341#endif // USE_ANGELSCRIPT
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define WRAP_MFN(ClassType, name)
static void RegisterReadonlyScriptArrayViewGeneric(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
CReadonlyScriptArrayView(const std::vector< T > &vec)
const std::vector< T > & m_vec
static void RegisterReadonlyScriptArrayView(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
T OpIndex(const std::string &key) const
static void RegisterReadonlyScriptDictView(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
AngelScript::asUINT GetSize() const
const std::map< std::string, T > & m_map
bool Exists(const std::string &key) const
CReadonlyScriptDictView(const std::map< std::string, T > &map)
AngelScript::CScriptArray * GetKeys() const
static void RegisterReadonlyScriptDictViewGeneric(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
void SLOG(const char *msg)
Replacement of macro.
AngelScript::asIScriptEngine * getEngine()
AngelScript::CScriptArray * VectorToScriptArray(const std::vector< T > &vec, const std::string &decl)
Definition ScriptUtils.h:44
AngelScript::CScriptArray * MapToScriptArray(std::map< T, U > &map, const std::string &decl)
Definition ScriptUtils.h:61
B * ScriptRefCast(A *a)
B * ScriptRefCastNoCount(A *a)
bool GetValueFromScriptDict(const std::string &log_msg, AngelScript::CScriptDictionary *dict, bool required, std::string const &key, const char *decl, T &out_value)
AngelScript::CScriptArray * IterableMapToScriptArray(ItorT begin, ItorT end, const std::string &decl)
Definition ScriptUtils.h:80
AngelScript::CScriptArray * IterableListToScriptArray(ItorT begin, ItorT end, const std::string &decl)
Definition ScriptUtils.h:95
ScriptEngine * GetScriptEngine()