Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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"
31
32#include <list>
33#include <map>
34#include <string>
35#include <vector>
36
37namespace RoR {
38
41
42template <typename T>
43AngelScript::CScriptArray* VectorToScriptArray(const std::vector<T>& vec, const std::string& decl)
44{
45 std::string arraydecl = fmt::format("array<{}>", decl);
46 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
47 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo, vec.size());
48
49 for (AngelScript::asUINT i = 0; i < arr->GetSize(); i++)
50 {
51 // Set the value of each element
52 T tempval = vec[i];
53 arr->SetValue(i, &tempval);
54 }
55
56 return arr;
57}
58
59template <typename T, typename U>
60AngelScript::CScriptArray* MapToScriptArray(std::map<T, U>& map, const std::string& decl)
61{
62 std::string arraydecl = fmt::format("array<{}>", decl);
63 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
64 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo, map.size());
65
66 for (auto itor = map.begin(); itor != map.end(); itor++)
67 {
68 // Set the value of each element
69 AngelScript::asUINT pos = static_cast<AngelScript::asUINT>(std::distance(map.begin(), itor));
70 arr->SetValue(pos, &itor->second);
71 }
72
73 return arr;
74}
75
76// Iterables:
77
78template <typename ItorT>
79AngelScript::CScriptArray* IterableMapToScriptArray(ItorT begin, ItorT end, const std::string& decl)
80{
81 std::string arraydecl = fmt::format("array<{}>", decl);
82 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
83 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo);
84
85 for (auto itor = begin; itor != end; itor++)
86 {
87 arr->InsertLast(&itor->second);
88 }
89
90 return arr;
91}
92
93template <typename ItorT>
94AngelScript::CScriptArray* IterableListToScriptArray(ItorT begin, ItorT end, const std::string& decl)
95{
96 std::string arraydecl = fmt::format("array<{}>", decl);
97 AngelScript::asITypeInfo* typeinfo = App::GetScriptEngine()->getEngine()->GetTypeInfoByDecl(arraydecl.c_str());
98 AngelScript::CScriptArray* arr = AngelScript::CScriptArray::Create(typeinfo);
99
100 for (auto itor = begin; itor != end; itor++)
101 {
102 arr->InsertLast(&itor);
103 }
104
105 return arr;
106}
107
108template<typename T>
109bool GetValueFromScriptDict(const std::string& log_msg, AngelScript::CScriptDictionary* dict, bool required, std::string const& key, const char* decl, T & out_value)
110{
111 if (!dict)
112 {
113 // Dict is NULL
114 if (required)
115 {
116 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, no parameters; '{}' is required.", log_msg, key));
117 }
118 return false;
119 }
120 auto itor = dict->find(key);
121 if (itor == dict->end())
122 {
123 // Key not found
124 if (required)
125 {
126 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, required parameter '{}' not found.", log_msg, key));
127 }
128 return false;
129 }
130
131 const int expected_typeid = App::GetScriptEngine()->getEngine()->GetTypeIdByDecl(decl);
132 const int actual_typeid = itor.GetTypeId();
133 if (actual_typeid != expected_typeid)
134 {
135 // Wrong type
136 if (required)
137 {
138 App::GetScriptEngine()->SLOG(fmt::format("{}: ERROR, required parameter '{}' must be a {}, instead got {}.",
139 log_msg, key, decl, App::GetScriptEngine()->getEngine()->GetTypeDeclaration(actual_typeid)));
140 }
141 return false;
142 }
143
144 return itor.GetValue(&out_value, actual_typeid); // Error will be logged to Angelscript.log
145}
146
147// Proof of concept: a read-only Dictionary view of a `std::map`-like container.
148// Assumed to always use `std::string` for key.
149// Not using RefCountingObject because this is only for use in the script, not C++
150// Adopted from 'scriptdictionary.cpp' bundled with AngelScript SDK
151template<typename T>
153{
154public:
155
156 CReadonlyScriptDictView(const std::map<std::string, T>& map) : m_map(map), m_refcount(1) {}
157
158 bool Exists(const std::string& key) const
159 {
160 return (m_map.find(key) != m_map.end());
161 }
162
163 bool IsEmpty() const
164 {
165 if (m_map.size() == 0)
166 return true;
167
168 return false;
169 }
170
171 AngelScript::asUINT GetSize() const
172 {
173 return AngelScript::asUINT(m_map.size());
174 }
175
176 T OpIndex(const std::string& key) const
177 {
178 auto it = m_map.find(key);
179 if (it != m_map.end())
180 return it->second;
181 else
182 return T{};
183 }
184
185 AngelScript::CScriptArray* GetKeys() const
186 {
187 // Create the array object
188 AngelScript::CScriptArray* array = AngelScript::CScriptArray::Create(AngelScript::asGetActiveContext()->GetEngine()->GetTypeInfoByDecl("array<string>"), AngelScript::asUINT(m_map.size()));
189 long current = -1;
190 for (auto it = m_map.begin(); it != m_map.end(); it++)
191 {
192 current++;
193 *(std::string*)array->At(current) = it->first;
194 }
195
196 return array;
197 }
198
199 static void RegisterReadonlyScriptDictView(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
200 {
201 using namespace AngelScript;
202
203 int r;
204
205 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptDictView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
206 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", asMETHOD(CReadonlyScriptDictView<T>, AddRef), asCALL_THISCALL); ROR_ASSERT(r >= 0);
207 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", asMETHOD(CReadonlyScriptDictView <T>, Release), asCALL_THISCALL); ROR_ASSERT(r >= 0);
208
209 r = engine->RegisterObjectMethod(decl, "bool exists(const string &in) const", asMETHOD(CReadonlyScriptDictView<T>, Exists), asCALL_THISCALL); ROR_ASSERT(r >= 0);
210 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", asMETHOD(CReadonlyScriptDictView<T>, IsEmpty), asCALL_THISCALL); ROR_ASSERT(r >= 0);
211 r = engine->RegisterObjectMethod(decl, "uint getSize() const", asMETHOD(CReadonlyScriptDictView<T>, GetSize), asCALL_THISCALL); ROR_ASSERT(r >= 0);
212 r = engine->RegisterObjectMethod(decl, "array<string> @getKeys() const", asMETHOD(CReadonlyScriptDictView<T>, GetKeys), asCALL_THISCALL); assert(r >= 0);
213
214 std::string opIndexDecl = fmt::format("{}@ opIndex(const string &in)", value_decl);
215 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), asMETHOD(CReadonlyScriptDictView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
216 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(const string &in) const", value_decl);
217 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), asMETHOD(CReadonlyScriptDictView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
218
219 }
220
221 // Angelscript refcounting
222 void AddRef() { m_refcount++; }
223 void Release() { m_refcount--; if (m_refcount == 0) { delete this; /* Comit suicide! This is legit in C++ and AngelScript relies on it. */ } }
224private:
225 const std::map<std::string, T>& m_map;
227};
228
229template <typename T>
231{
232public:
233
234 CReadonlyScriptArrayView(const std::vector<T>& vec) : m_vec(vec), m_refcount(1) {}
235
236 bool IsEmpty() const { return m_vec.empty(); }
237 unsigned GetSize() const { return m_vec.size(); }
238 T OpIndex(unsigned pos) { return m_vec.at(pos); }
239
240 static void RegisterReadonlyScriptArrayView(AngelScript::asIScriptEngine* engine, const char* decl, const char* value_decl)
241 {
242 using namespace AngelScript;
243
244 int r;
245
246 r = engine->RegisterObjectType(decl, sizeof(CReadonlyScriptArrayView<T>), asOBJ_REF); ROR_ASSERT(r >= 0);
247 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_ADDREF, "void f()", asMETHOD(CReadonlyScriptArrayView<T>, AddRef), asCALL_THISCALL); ROR_ASSERT(r >= 0);
248 r = engine->RegisterObjectBehaviour(decl, asBEHAVE_RELEASE, "void f()", asMETHOD(CReadonlyScriptArrayView <T>, Release), asCALL_THISCALL); ROR_ASSERT(r >= 0);
249
250 r = engine->RegisterObjectMethod(decl, "bool isEmpty() const", asMETHOD(CReadonlyScriptArrayView<T>, IsEmpty), asCALL_THISCALL); ROR_ASSERT(r >= 0);
251 r = engine->RegisterObjectMethod(decl, "uint length() const", asMETHOD(CReadonlyScriptArrayView<T>, GetSize), asCALL_THISCALL); ROR_ASSERT(r >= 0);
252
253 std::string opIndexDecl = fmt::format("{}@ opIndex(uint pos)", value_decl);
254 r = engine->RegisterObjectMethod(decl, opIndexDecl.c_str(), asMETHOD(CReadonlyScriptArrayView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
255 std::string opIndexConstDecl = fmt::format("const {}@ opIndex(uint pos) const", value_decl);
256 r = engine->RegisterObjectMethod(decl, opIndexConstDecl.c_str(), asMETHOD(CReadonlyScriptArrayView<T>, OpIndex), asCALL_THISCALL); ROR_ASSERT(r >= 0);
257
258 }
259
260 // Angelscript refcounting
261 void AddRef() { m_refcount++; }
262 void Release() { m_refcount--; if (m_refcount == 0) { delete this; /* Comit suicide! This is legit in C++ and AngelScript relies on it. */ } }
263private:
264 const std::vector<T>& m_vec;
266};
267
268// Example opCast behaviour
269template<class A, class B>
271{
272 // If the handle already is a null handle, then just return the null handle
273 if (!a) return 0;
274
275 // Now try to dynamically cast the pointer to the wanted type
276 B* b = dynamic_cast<B*>(a);
277 if (b != 0)
278 {
279 // Since the cast was made, we need to increase the ref counter for the returned handle
280 b->AddRef();
281 }
282 return b;
283}
284
285// Example opCast behaviour - for objects with asOBJ_NOCOUNT
286template<class A, class B>
288{
289 // If the handle already is a null handle, then just return the null handle
290 if (!a) return 0;
291
292 // Now try to dynamically cast the pointer to the wanted type
293 return dynamic_cast<B*>(a);
294}
295
297
298} // namespace RoR
299
300#endif // USE_ANGELSCRIPT
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
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
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:43
AngelScript::CScriptArray * MapToScriptArray(std::map< T, U > &map, const std::string &decl)
Definition ScriptUtils.h:60
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:79
AngelScript::CScriptArray * IterableListToScriptArray(ItorT begin, ItorT end, const std::string &decl)
Definition ScriptUtils.h:94
ScriptEngine * GetScriptEngine()