SimpleAI
 All Classes Namespaces Files Functions Variables Typedefs Macros Groups Pages
LUAAIRegistry.h
1 /***
2  * @file LUAAIRegistry.h
3  * @ingroup LUA
4  */
5 #pragma once
6 
7 #include "AIRegistry.h"
8 #include "LUAFunctions.h"
9 #include "tree/LUATreeNode.h"
11 #include "filter/LUAFilter.h"
12 #include "movement/LUASteering.h"
13 
14 namespace ai {
15 
69 class LUAAIRegistry : public AIRegistry {
70 protected:
71  lua_State* _s = nullptr;
72 
74  typedef std::shared_ptr<LuaNodeFactory> LUATreeNodeFactoryPtr;
75  typedef std::map<std::string, LUATreeNodeFactoryPtr> TreeNodeFactoryMap;
76 
78  typedef std::shared_ptr<LuaConditionFactory> LUAConditionFactoryPtr;
79  typedef std::map<std::string, LUAConditionFactoryPtr> ConditionFactoryMap;
80 
82  typedef std::shared_ptr<LuaFilterFactory> LUAFilterFactoryPtr;
83  typedef std::map<std::string, LUAFilterFactoryPtr> FilterFactoryMap;
84 
86  typedef std::shared_ptr<LuaSteeringFactory> LUASteeringFactoryPtr;
87  typedef std::map<std::string, LUASteeringFactoryPtr> SteeringFactoryMap;
88 
89  ReadWriteLock _lock{"luaregistry"};
90  TreeNodeFactoryMap _treeNodeFactories;
91  ConditionFactoryMap _conditionFactories;
92  FilterFactoryMap _filterFactories;
93  SteeringFactoryMap _steeringFactories;
94 
95  /***
96  * Gives you access the the light userdata for the LUAAIRegistry.
97  * @return the registry userdata
98  */
99  static LUAAIRegistry* luaAI_toregistry(lua_State * s) {
100  return luaAI_getlightuserdata<LUAAIRegistry>(s, luaAI_metaregistry());
101  }
102 
103  /***
104  * Gives you access the the userdata for the LuaNodeFactory instance you are operating on.
105  * @return the node factory userdata
106  */
107  static LuaNodeFactory* luaAI_tonodefactory(lua_State * s, int n) {
108  return *(LuaNodeFactory **) lua_touserdata(s, n);
109  }
110 
111  /***
112  * Gives you access the the userdata for the LuaConditionFactory instance you are operating on.
113  * @return the condition factory userdata
114  */
115  static LuaConditionFactory* luaAI_toconditionfactory(lua_State * s, int n) {
116  return *(LuaConditionFactory **) lua_touserdata(s, n);
117  }
118 
119  /***
120  * Gives you access the the userdata for the LuaFilterFactory instance you are operating on.
121  * @return the filter factory userdata
122  */
123  static LuaFilterFactory* luaGetFilterFactoryContext(lua_State * s, int n) {
124  return *(LuaFilterFactory **) lua_touserdata(s, n);
125  }
126 
127  /***
128  * Gives you access the the userdata for the LuaSteeringFactory instance you are operating on.
129  * @return the steering factory userdata
130  */
131  static LuaSteeringFactory* luaAI_tosteeringfactory(lua_State * s, int n) {
132  return *(LuaSteeringFactory **) lua_touserdata(s, n);
133  }
134 
135  /***
136  * Empty (default) execute() function that just throws an error.
137  * @param ai The ai to execute the tree node on
138  * @param deltaMillis Milliseconds since last execution
139  * @return throws error because execute() function wasn't overridden
140  */
141  static int luaAI_nodeemptyexecute(lua_State* s) {
142  const LuaNodeFactory* factory = luaAI_tonodefactory(s, 1);
143  return luaL_error(s, "There is no execute function set for node: %s", factory->type().c_str());
144  }
145 
146  static int luaAI_nodetostring(lua_State* s) {
147  const LuaNodeFactory* factory = luaAI_tonodefactory(s, 1);
148  lua_pushfstring(s, "node: %s", factory->type().c_str());
149  return 1;
150  }
151 
152  /***
153  * @brief Create a new lua @ai{TreeNode}
154  *
155  * @par lua parameters: #1 name of the node
156  * @note you have to specify an @c execute method that accepts two parameters in your lua code. E.g. do it like this:
157  * @code
158  * local luatest = REGISTRY.createNode(\"LuaTest\")
159  " function luatest:execute(ai, deltaMillis)
160  " return FAILED\n"
161  " end
162  * @endcode
163  */
164  static int luaAI_createnode(lua_State* s) {
165  LUAAIRegistry* r = luaAI_toregistry(s);
166  const std::string type = luaL_checkstring(s, -1);
167  const LUATreeNodeFactoryPtr& factory = std::make_shared<LuaNodeFactory>(s, type);
168  const bool inserted = r->registerNodeFactory(type, *factory);
169  if (!inserted) {
170  return luaL_error(s, "tree node %s is already registered", type.c_str());
171  }
172 
173  luaAI_newuserdata<LuaNodeFactory*>(s, factory.get());
174  const luaL_Reg nodes[] = {
175  {"execute", luaAI_nodeemptyexecute},
176  {"__tostring", luaAI_nodetostring},
177  {"__newindex", luaAI_newindex},
178  {nullptr, nullptr}
179  };
180  luaAI_setupmetatable(s, type, nodes, "node");
181  ScopedWriteLock scopedLock(r->_lock);
182  r->_treeNodeFactories.emplace(type, factory);
183  return 1;
184  }
185 
186  /***
187  * Empty (default) evaluate() function that just throws an error.
188  * @param ai The ai to execute the condition node on
189  * @return throws error because evaluate() function wasn't overridden
190  */
191  static int luaAI_conditionemptyevaluate(lua_State* s) {
192  const LuaConditionFactory* factory = luaAI_toconditionfactory(s, 1);
193  return luaL_error(s, "There is no evaluate function set for condition: %s", factory->type().c_str());
194  }
195 
196  static int luaAI_conditiontostring(lua_State* s) {
197  const LuaConditionFactory* factory = luaAI_toconditionfactory(s, 1);
198  lua_pushfstring(s, "condition: %s", factory->type().c_str());
199  return 1;
200  }
201 
202  /***
203  * @param type The string that identifies the name that is used to register the condition under
204  * @return userdata with a metatable for conditions
205  */
206  static int luaAI_createcondition(lua_State* s) {
207  LUAAIRegistry* r = luaAI_toregistry(s);
208  const std::string type = luaL_checkstring(s, -1);
209  const LUAConditionFactoryPtr& factory = std::make_shared<LuaConditionFactory>(s, type);
210  const bool inserted = r->registerConditionFactory(type, *factory);
211  if (!inserted) {
212  return luaL_error(s, "condition %s is already registered", type.c_str());
213  }
214 
215  luaAI_newuserdata<LuaConditionFactory*>(s, factory.get());
216  const luaL_Reg nodes[] = {
217  {"evaluate", luaAI_conditionemptyevaluate},
218  {"__tostring", luaAI_conditiontostring},
219  {"__newindex", luaAI_newindex},
220  {nullptr, nullptr}
221  };
222  luaAI_setupmetatable(s, type, nodes, "condition");
223  ScopedWriteLock scopedLock(r->_lock);
224  r->_conditionFactories.emplace(type, factory);
225  return 1;
226  }
227 
228  /***
229  * Empty (default) filter() function that just throws an error.
230  * @param ai The ai to execute the filter for
231  * @return throws error because filter() function wasn't overridden
232  */
233  static int luaAI_filteremptyfilter(lua_State* s) {
234  const LuaFilterFactory* factory = luaGetFilterFactoryContext(s, 1);
235  return luaL_error(s, "There is no filter function set for filter: %s", factory->type().c_str());
236  }
237 
238  static int luaAI_filtertostring(lua_State* s) {
239  const LuaFilterFactory* factory = luaGetFilterFactoryContext(s, 1);
240  lua_pushfstring(s, "filter: %s", factory->type().c_str());
241  return 1;
242  }
243 
244  static int luaAI_createfilter(lua_State* s) {
245  LUAAIRegistry* r = luaAI_toregistry(s);
246  const std::string type = luaL_checkstring(s, -1);
247  const LUAFilterFactoryPtr& factory = std::make_shared<LuaFilterFactory>(s, type);
248  const bool inserted = r->registerFilterFactory(type, *factory);
249  if (!inserted) {
250  return luaL_error(s, "filter %s is already registered", type.c_str());
251  }
252 
253  luaAI_newuserdata<LuaFilterFactory*>(s, factory.get());
254  const luaL_Reg nodes[] = {
255  {"filter", luaAI_filteremptyfilter},
256  {"__tostring", luaAI_filtertostring},
257  {"__newindex", luaAI_newindex},
258  {nullptr, nullptr}
259  };
260  luaAI_setupmetatable(s, type, nodes, "filter");
261 
262  ScopedWriteLock scopedLock(r->_lock);
263  r->_filterFactories.emplace(type, factory);
264  return 1;
265  }
266 
267  static int luaAI_steeringemptyexecute(lua_State* s) {
268  const LuaSteeringFactory* factory = luaAI_tosteeringfactory(s, 1);
269  return luaL_error(s, "There is no execute() function set for steering: %s", factory->type().c_str());
270  }
271 
272  static int luaAI_steeringtostring(lua_State* s) {
273  const LuaSteeringFactory* factory = luaAI_tosteeringfactory(s, 1);
274  lua_pushfstring(s, "steering: %s", factory->type().c_str());
275  return 1;
276  }
277 
278  static int luaAI_createsteering(lua_State* s) {
279  LUAAIRegistry* r = luaAI_toregistry(s);
280  const std::string type = luaL_checkstring(s, -1);
281  const LUASteeringFactoryPtr& factory = std::make_shared<LuaSteeringFactory>(s, type);
282  const bool inserted = r->registerSteeringFactory(type, *factory);
283  if (!inserted) {
284  return luaL_error(s, "steering %s is already registered", type.c_str());
285  }
286 
287  luaAI_newuserdata<LuaSteeringFactory*>(s, factory.get());
288  const luaL_Reg nodes[] = {
289  {"filter", luaAI_steeringemptyexecute},
290  {"__tostring", luaAI_steeringtostring},
291  {"__newindex", luaAI_newindex},
292  {nullptr, nullptr}
293  };
294  luaAI_setupmetatable(s, type, nodes, "steering");
295 
296  ScopedWriteLock scopedLock(r->_lock);
297  r->_steeringFactories.emplace(type, factory);
298  return 1;
299  }
300 
301 public:
302  LUAAIRegistry() {
303  init();
304  }
305 
306  std::vector<luaL_Reg> aiFuncs = {
307  {"id", luaAI_aiid},
308  {"time", luaAI_aitime},
309  {"hasZone", luaAI_aihaszone},
310  {"zone", luaAI_aigetzone},
311  {"filteredEntities", luaAI_aifilteredentities},
312  {"setFilteredEntities", luaAI_aisetfilteredentities},
313  {"addFilteredEntity", luaAI_aiaddfilteredentity},
314  {"character", luaAI_aigetcharacter},
315  {"aggroMgr", luaAI_aigetaggromgr},
316  {"__tostring", luaAI_aitostring},
317  {"__gc", luaAI_aigc},
318  {"__eq", luaAI_aieq},
319  {nullptr, nullptr}
320  };
321  std::vector<luaL_Reg> vecFuncs = {
322  {"__add", luaAI_vecadd},
323  {"__sub", luaAI_vecsub},
324  {"__mul", luaAI_vecdot},
325  {"__div", luaAI_vecdiv},
326  {"__unm", luaAI_vecnegate},
327  {"__len", luaAI_veclen},
328  {"__eq", luaAI_veceq},
329  {"__tostring", luaAI_vectostring},
330  {"__index", luaAI_vecindex},
331  {"__newindex", luaAI_vecnewindex},
332  {"dot", luaAI_vecdot},
333  {nullptr, nullptr}
334  };
335  std::vector<luaL_Reg> zoneFuncs = {
336  {"size", luaAI_zonesize},
337  {"name", luaAI_zonename},
338  {"ai", luaAI_zoneai},
339  {"execute", luaAI_zoneexecute},
340  {"groupMgr", luaAI_zonegroupmgr},
341  {"__tostring", luaAI_zonetostring},
342  {nullptr, nullptr}
343  };
344  std::vector<luaL_Reg> characterFuncs = {
345  {"id", luaAI_characterid},
346  {"position", luaAI_characterposition},
347  {"setPosition", luaAI_charactersetposition},
348  {"speed", luaAI_characterspeed},
349  {"setSpeed", luaAI_charactersetspeed},
350  {"orientation", luaAI_characterorientation},
351  {"setOrientation", luaAI_charactersetorientation},
352  {"setAttribute", luaAI_charactersetattribute},
353  {"attributes", luaAI_characterattributes},
354  {"__eq", luaAI_charactereq},
355  {"__gc", luaAI_charactergc},
356  {"__tostring", luaAI_charactertostring},
357  {nullptr, nullptr}
358  };
359  std::vector<luaL_Reg> aggroMgrFuncs = {
360  {"setReduceByRatio", luaAI_aggromgrsetreducebyratio},
361  {"setReduceByValue", luaAI_aggromgrsetreducebyvalue},
362  {"resetReduceValue", luaAI_aggromgrresetreducevalue},
363  {"addAggro", luaAI_aggromgraddaggro},
364  {"highestEntry", luaAI_aggromgrhighestentry},
365  {"entries", luaAI_aggromgrentries},
366  {"__tostring", luaAI_aggromgrtostring},
367  {nullptr, nullptr}
368  };
369  std::vector<luaL_Reg> groupMgrFuncs = {
370  {"add", luaAI_groupmgradd},
371  {"remove", luaAI_groupmgrremove},
372  {"isLeader", luaAI_groupmgrisleader},
373  {"isInGroup", luaAI_groupmgrisingroup},
374  {"isInAnyGroup", luaAI_groupmgrisinanygroup},
375  {"size", luaAI_groupmgrsize},
376  {"position", luaAI_groupmgrposition},
377  {"leader", luaAI_groupmgrleader},
378  {"__tostring", luaAI_groupmgrtostring},
379  {nullptr, nullptr}
380  };
381  std::vector<luaL_Reg> registryFuncs = {
382  {"createNode", luaAI_createnode},
383  {"createCondition", luaAI_createcondition},
384  {"createFilter", luaAI_createfilter},
385  {"createSteering", luaAI_createsteering},
386  {nullptr, nullptr}
387  };
388 
389  static int luaAI_aisetfilteredentities(lua_State* s) {
390  luaAI_AI* ai = luaAI_toai(s, 1);
391  luaL_checktype(s, 2, LUA_TTABLE);
392 
393  const int n = lua_rawlen(s, 2);
394  FilteredEntities v(n);
395  for (int i = 1; i <= n; ++i) {
396  lua_rawgeti(s, 2, i);
397  const int top = lua_gettop(s);
398  const CharacterId id = (CharacterId)luaL_checknumber(s, top);
399  v[i - 1] = id;
400  lua_pop(s, 1);
401  }
402  ai->ai->setFilteredEntities(v);
403  return 0;
404  }
405 
406  static int luaAI_aiaddfilteredentity(lua_State* s) {
407  luaAI_AI* ai = luaAI_toai(s, 1);
408  const CharacterId id = (CharacterId)luaL_checkinteger(s, 2);
409  ai->ai->addFilteredEntity(id);
410  return 0;
411  }
412 
417  lua_State* getLuaState() {
418  return _s;
419  }
420 
429  ai_assert(_s != nullptr, "LUA state is not yet initialized");
430  return luaL_getmetatable(_s, luaAI_metaai());
431  }
432 
438  ai_assert(_s != nullptr, "LUA state is not yet initialized");
439  return luaL_getmetatable(_s, luaAI_metacharacter());
440  }
441 
445  bool init() {
446  if (_s != nullptr) {
447  return true;
448  }
449  _s = luaL_newstate();
450 
451  lua_atpanic(_s, [] (lua_State* L) {
452  ai_log_error("Lua panic. Error message: %s", (lua_isnil(L, -1) ? "" : lua_tostring(L, -1)));
453  return 0;
454  });
455  lua_gc(_s, LUA_GCSTOP, 0);
456  luaL_openlibs(_s);
457 
458  luaAI_registerfuncs(_s, &registryFuncs.front(), "META_REGISTRY");
459  lua_setglobal(_s, "REGISTRY");
460 
461  // TODO: random
462 
463  luaAI_globalpointer(_s, this, luaAI_metaregistry());
464 
465  luaAI_registerfuncs(_s, &aiFuncs.front(), luaAI_metaai());
466  luaAI_registerfuncs(_s, &vecFuncs.front(), luaAI_metavec());
467  luaAI_registerfuncs(_s, &zoneFuncs.front(), luaAI_metazone());
468  luaAI_registerfuncs(_s, &characterFuncs.front(), luaAI_metacharacter());
469  luaAI_registerfuncs(_s, &aggroMgrFuncs.front(), luaAI_metaaggromgr());
470  luaAI_registerfuncs(_s, &groupMgrFuncs.front(), luaAI_metagroupmgr());
471 
472  const char* script = ""
473  "UNKNOWN, CANNOTEXECUTE, RUNNING, FINISHED, FAILED, EXCEPTION = 0, 1, 2, 3, 4, 5\n";
474 
475  if (luaL_loadbufferx(_s, script, strlen(script), "", nullptr) || lua_pcall(_s, 0, 0, 0)) {
476  ai_log_error("%s", lua_tostring(_s, -1));
477  lua_pop(_s, 1);
478  return false;
479  }
480  return true;
481  }
482 
486  void shutdown() {
487  {
488  ScopedWriteLock scopedLock(_lock);
489  _treeNodeFactories.clear();
490  _conditionFactories.clear();
491  _filterFactories.clear();
492  _steeringFactories.clear();
493  }
494  if (_s != nullptr) {
495  lua_close(_s);
496  _s = nullptr;
497  }
498  }
499 
500  ~LUAAIRegistry() {
501  shutdown();
502  }
503 
504  inline bool evaluate(const std::string& str) {
505  return evaluate(str.c_str(), str.length());
506  }
507 
514  bool evaluate(const char* luaBuffer, size_t size) {
515  if (_s == nullptr) {
516  ai_log_debug("LUA state is not yet initialized");
517  return false;
518  }
519  if (luaL_loadbufferx(_s, luaBuffer, size, "", nullptr) || lua_pcall(_s, 0, 0, 0)) {
520  ai_log_error("%s", lua_tostring(_s, -1));
521  lua_pop(_s, 1);
522  return false;
523  }
524  return true;
525  }
526 };
527 
528 }
#define ai_assert(condition,...)
Provide your own assert - this is only executed in DEBUG mode.
Definition: Types.h:75
Definition: LUAFunctions.h:11
Definition: LUACondition.h:86
void shutdown()
Definition: LUAAIRegistry.h:486
#define ai_log_error(...)
Logging macro to provide your own loggers.
Definition: Types.h:23
Definition: LUATreeNode.h:91
The place to register your TreeNode and ICondition factories at.
Definition: AIRegistry.h:64
int pushCharacterMetatable()
Pushes the character metatable onto the stack. This allows anyone to modify it to provide own functio...
Definition: LUAAIRegistry.h:437
int pushAIMetatable()
Pushes the AI metatable onto the stack. This allows anyone to modify it to provide own functions and ...
Definition: LUAAIRegistry.h:428
Allows you to register lua TreeNodes, Conditions, Filters and ISteerings.
Definition: LUAAIRegistry.h:69
#define ai_log_debug(...)
Logging macro to provide your own loggers.
Definition: Types.h:37
lua_State * getLuaState()
Access to the lua state.
Definition: LUAAIRegistry.h:417
bool registerNodeFactory(const std::string &type, const ITreeNodeFactory &factory)
Registers a tree node factory of the given type.
Definition: AIRegistry.h:221
Definition: LUAFilter.h:76
Definition: Thread.h:56
bool evaluate(const char *luaBuffer, size_t size)
Load your lua scripts into the lua state of the registry. This can be called multiple times to e...
Definition: LUAAIRegistry.h:514
Definition: Thread.h:14
bool init()
Definition: LUAAIRegistry.h:445