RigsofRods
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
37 namespace RoR {
38 
41 
42 template <typename T>
43 AngelScript::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 
59 template <typename T, typename U>
60 AngelScript::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 
78 template <typename ItorT>
79 AngelScript::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 
93 template <typename ItorT>
94 AngelScript::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 
108 template<typename T>
109 bool 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
151 template<typename T>
153 {
154 public:
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. */ } }
224 private:
225  const std::map<std::string, T>& m_map;
227 };
228 
229 template <typename T>
231 {
232 public:
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. */ } }
263 private:
264  const std::vector<T>& m_vec;
266 };
267 
268 // Example opCast behaviour
269 template<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
286 template<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
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
RoR::CReadonlyScriptArrayView::m_refcount
int m_refcount
Definition: ScriptUtils.h:265
RoR::VectorToScriptArray
AngelScript::CScriptArray * VectorToScriptArray(const std::vector< T > &vec, const std::string &decl)
Definition: ScriptUtils.h:43
RoR::CReadonlyScriptArrayView::IsEmpty
bool IsEmpty() const
Definition: ScriptUtils.h:236
RoR::CReadonlyScriptArrayView::Release
void Release()
Definition: ScriptUtils.h:262
RoR::CReadonlyScriptArrayView::AddRef
void AddRef()
Definition: ScriptUtils.h:261
RoR::CReadonlyScriptDictView::Exists
bool Exists(const std::string &key) const
Definition: ScriptUtils.h:158
format
Truck file format(technical spec)
RoR::CReadonlyScriptDictView
Definition: ScriptUtils.h:152
RoR::CReadonlyScriptDictView::Release
void Release()
Definition: ScriptUtils.h:223
RoR::ScriptEngine::getEngine
AngelScript::asIScriptEngine * getEngine()
Definition: ScriptEngine.h:291
RoR::GetValueFromScriptDict
bool GetValueFromScriptDict(const std::string &log_msg, AngelScript::CScriptDictionary *dict, bool required, std::string const &key, const char *decl, T &out_value)
Definition: ScriptUtils.h:109
RoR::CReadonlyScriptDictView::RegisterReadonlyScriptDictView
static void RegisterReadonlyScriptDictView(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
Definition: ScriptUtils.h:199
RoR::CReadonlyScriptDictView::m_map
const std::map< std::string, T > & m_map
Definition: ScriptUtils.h:225
RoR::IterableMapToScriptArray
AngelScript::CScriptArray * IterableMapToScriptArray(ItorT begin, ItorT end, const std::string &decl)
Definition: ScriptUtils.h:79
RoR::App::GetScriptEngine
ScriptEngine * GetScriptEngine()
Definition: Application.cpp:283
RoR::CReadonlyScriptArrayView::m_vec
const std::vector< T > & m_vec
Definition: ScriptUtils.h:264
RoR::IterableListToScriptArray
AngelScript::CScriptArray * IterableListToScriptArray(ItorT begin, ItorT end, const std::string &decl)
Definition: ScriptUtils.h:94
RoR::CReadonlyScriptDictView::IsEmpty
bool IsEmpty() const
Definition: ScriptUtils.h:163
RoR::ScriptRefCastNoCount
B * ScriptRefCastNoCount(A *a)
Definition: ScriptUtils.h:287
RoR::CReadonlyScriptArrayView::OpIndex
T OpIndex(unsigned pos)
Definition: ScriptUtils.h:238
RoR::MapToScriptArray
AngelScript::CScriptArray * MapToScriptArray(std::map< T, U > &map, const std::string &decl)
Definition: ScriptUtils.h:60
RoR::CReadonlyScriptDictView::GetSize
AngelScript::asUINT GetSize() const
Definition: ScriptUtils.h:171
ScriptEngine.h
RoR::ScriptRefCast
B * ScriptRefCast(A *a)
Definition: ScriptUtils.h:270
RoR::CReadonlyScriptDictView::OpIndex
T OpIndex(const std::string &key) const
Definition: ScriptUtils.h:176
RoR::CReadonlyScriptArrayView::RegisterReadonlyScriptArrayView
static void RegisterReadonlyScriptArrayView(AngelScript::asIScriptEngine *engine, const char *decl, const char *value_decl)
Definition: ScriptUtils.h:240
RoR::CReadonlyScriptDictView::AddRef
void AddRef()
Definition: ScriptUtils.h:222
RoR::CReadonlyScriptArrayView::GetSize
unsigned GetSize() const
Definition: ScriptUtils.h:237
RoR::CReadonlyScriptArrayView::CReadonlyScriptArrayView
CReadonlyScriptArrayView(const std::vector< T > &vec)
Definition: ScriptUtils.h:234
RoR::CReadonlyScriptDictView::m_refcount
int m_refcount
Definition: ScriptUtils.h:226
RoR::CReadonlyScriptDictView::CReadonlyScriptDictView
CReadonlyScriptDictView(const std::map< std::string, T > &map)
Definition: ScriptUtils.h:156
RoR::CReadonlyScriptArrayView
Definition: ScriptUtils.h:230
RoR
Definition: AppContext.h:36
RoR::CReadonlyScriptDictView::GetKeys
AngelScript::CScriptArray * GetKeys() const
Definition: ScriptUtils.h:185