Cuberite
A lightweight, fast and extensible game server for Minecraft
LuaState.cpp
Go to the documentation of this file.
1 
2 // LuaState.cpp
3 
4 // Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions
5 
6 #include "Globals.h"
7 #include "LuaState.h"
8 
9 extern "C"
10 {
11  #include "lua/src/lualib.h"
12 }
13 
14 #undef TOLUA_TEMPLATE_BIND
15 #include "tolua++/include/tolua++.h"
16 #include "Bindings.h"
17 #include "ManualBindings.h"
18 #include "DeprecatedBindings.h"
19 #include "LuaJson.h"
20 #include "../Entities/Entity.h"
21 #include "../BlockEntities/BlockEntity.h"
22 #include "../DeadlockDetect.h"
23 #include "../UUID.h"
24 
25 
26 
27 
28 
29 // Hotpatching the Macro to prevent a Clang Warning (0 for pointer used)
30 #undef lua_tostring
31 #define lua_tostring(L, i) lua_tolstring(L, (i), nullptr)
32 
33 
34 
35 
36 
37 // fwd: "SQLite/lsqlite3.c"
38 extern "C"
39 {
40  int luaopen_lsqlite3(lua_State * L);
41 }
42 
43 // fwd: "LuaExpat/lxplib.c":
44 extern "C"
45 {
46  int luaopen_lxp(lua_State * L);
47 }
48 
49 
50 
51 
52 
55 
56 
57 
58 
59 
61 // cCanonLuaStates:
62 
67 {
68 public:
70  static cLuaState * GetCanonState(lua_State * a_LuaState)
71  {
72  auto & inst = GetInstance();
73  cCSLock lock(inst.m_CS);
74  auto itr = inst.m_CanonStates.find(a_LuaState);
75  if (itr == inst.m_CanonStates.end())
76  {
77  return nullptr;
78  }
79  return itr->second;
80  }
81 
84  static void Add(cLuaState & a_LuaState)
85  {
86  auto & inst = GetInstance();
87  cCSLock lock(inst.m_CS);
88  ASSERT(inst.m_CanonStates.find(a_LuaState) == inst.m_CanonStates.end());
89  inst.m_CanonStates[a_LuaState.operator lua_State *()] = &a_LuaState;
90  }
91 
94  static void Remove(cLuaState & a_LuaState)
95  {
96  auto & inst = GetInstance();
97  cCSLock lock(inst.m_CS);
98  auto itr = inst.m_CanonStates.find(a_LuaState);
99  ASSERT(itr != inst.m_CanonStates.end());
100  inst.m_CanonStates.erase(itr);
101  }
102 
103 protected:
106 
108  std::map<lua_State *, cLuaState *> m_CanonStates;
109 
110 
113  {
114  static cCanonLuaStates canonLuaStates;
115  return canonLuaStates;
116  }
117 };
118 
119 
120 
121 
122 
124 // cLuaStateTracker:
125 
127 {
128  auto & Instance = Get();
129  cCSLock Lock(Instance.m_CSLuaStates);
130  Instance.m_LuaStates.push_back(&a_LuaState);
131 }
132 
133 
134 
135 
136 
138 {
139  auto & Instance = Get();
140  cCSLock Lock(Instance.m_CSLuaStates);
141  Instance.m_LuaStates.erase(
142  std::remove_if(
143  Instance.m_LuaStates.begin(), Instance.m_LuaStates.end(),
144  [&a_LuaState](cLuaStatePtr a_StoredLuaState)
145  {
146  return (&a_LuaState == a_StoredLuaState);
147  }
148  ),
149  Instance.m_LuaStates.end()
150  );
151 }
152 
153 
154 
155 
156 
158 {
159  auto & Instance = Get();
160  cCSLock Lock(Instance.m_CSLuaStates);
161  AString res;
162  int Total = 0;
163  for (auto state: Instance.m_LuaStates)
164  {
165  int Mem = 0;
166  if (!state->Call("collectgarbage", "count", cLuaState::Return, Mem))
167  {
168  res.append(Printf("Cannot query memory for state \"%s\"\n", state->GetSubsystemName().c_str()));
169  }
170  else
171  {
172  res.append(Printf("State \"%s\" is using %d KiB of memory\n", state->GetSubsystemName().c_str(), Mem));
173  Total += Mem;
174  }
175  }
176  res.append(Printf("Total memory used by Lua: %d KiB\n", Total));
177  return res;
178 }
179 
180 
181 
182 
183 
185 {
186  static cLuaStateTracker Inst; // The singleton
187  return Inst;
188 }
189 
190 
191 
192 
193 
195 // cLuaState::cTrackedRef:
196 
198  m_CS(nullptr)
199 {
200 }
201 
202 
203 
204 
205 
206 bool cLuaState::cTrackedRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
207 {
208  // Clear any previous callback:
209  Clear();
210 
211  // Add self to LuaState's callback-tracking:
212  auto canonState = a_LuaState.QueryCanonLuaState();
213  canonState->TrackRef(*this);
214 
215  // Store the new callback:
216  m_CS = &(canonState->m_CS);
217  m_Ref.RefStack(*canonState, a_StackPos);
218  return true;
219 }
220 
221 
222 
223 
224 
226 {
227  // Free the reference:
228  lua_State * luaState = nullptr;
229  {
230  auto cs = m_CS.load();
231  if (cs != nullptr)
232  {
233  cCSLock Lock(*cs);
234  if (!m_Ref.IsValid())
235  {
236  return;
237  }
238  luaState = m_Ref.GetLuaState();
239  m_Ref.UnRef();
240  }
241  }
242  m_CS = nullptr;
243 
244  // Remove from LuaState's callback-tracking:
245  if (luaState == nullptr)
246  {
247  return;
248  }
249  cLuaState(luaState).UntrackRef(*this);
250 }
251 
252 
253 
254 
255 
257 {
258  auto cs = m_CS.load();
259  if (cs == nullptr)
260  {
261  return false;
262  }
263  cCSLock lock(*cs);
264  return m_Ref.IsValid();
265 }
266 
267 
268 
269 
270 
272 {
273  auto cs = m_CS.load();
274  if (cs == nullptr)
275  {
276  return false;
277  }
278  cCSLock lock(*cs);
279  if (!m_Ref.IsValid())
280  {
281  return false;
282  }
283  auto canonState = a_LuaState.QueryCanonLuaState();
284  if (canonState == nullptr)
285  {
286  return false;
287  }
288  return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState));
289 }
290 
291 
292 
293 
294 
296 {
297  auto cs = m_CS.load();
298  if (cs == nullptr)
299  {
300  // Already invalid
301  return;
302  }
303  cCSLock Lock(*cs);
304  if (!m_Ref.IsValid())
305  {
306  LOGD("%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen",
307  __FUNCTION__, static_cast<void *>(this)
308  );
309  return;
310  }
311  m_Ref.UnRef();
312  m_CS = nullptr;
313 }
314 
315 
316 
317 
318 
320 // cLuaState::cCallback:
321 
322 bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
323 {
324  // Check if the stack contains a function:
325  if (!lua_isfunction(a_LuaState, a_StackPos))
326  {
327  return false;
328  }
329 
330  return Super::RefStack(a_LuaState, a_StackPos);
331 }
332 
333 
334 
335 
336 
338 // cLuaState::cOptionalCallback:
339 
340 bool cLuaState::cOptionalCallback::RefStack(cLuaState & a_LuaState, int a_StackPos)
341 {
342  // If the stack pos is nil, make this an empty callback:
343  if (lua_isnil(a_LuaState, a_StackPos))
344  {
345  Clear();
346  return true;
347  }
348 
349  // Use default cCallback implementation:
350  return Super::RefStack(a_LuaState, a_StackPos);
351 }
352 
353 
354 
355 
356 
358 // cLuaState::cTableRef:
359 
360 bool cLuaState::cTableRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
361 {
362  // Check if the stack contains a table:
363  if (!lua_istable(a_LuaState, a_StackPos))
364  {
365  return false;
366  }
367 
368  return Super::RefStack(a_LuaState, a_StackPos);
369 }
370 
371 
372 
373 
374 
376 // cLuaState::cStackTable:
377 
378 cLuaState::cStackTable::cStackTable(cLuaState & a_LuaState, int a_StackPos):
379  m_LuaState(a_LuaState),
380  m_StackPos(a_StackPos)
381 {
382  ASSERT(lua_istable(a_LuaState, a_StackPos));
383 }
384 
385 
386 
387 
388 
389 void cLuaState::cStackTable::ForEachArrayElement(cFunctionRef<bool(cLuaState & a_LuaState, int a_Index)> a_ElementCallback) const
390 {
391  auto numElements = luaL_getn(m_LuaState, m_StackPos);
392  #ifdef _DEBUG
393  auto stackTop = lua_gettop(m_LuaState);
394  #endif
395  for (int idx = 1; idx <= numElements; idx++)
396  {
397  // Push the idx-th element of the array onto stack top and call the callback:
398  lua_rawgeti(m_LuaState, m_StackPos, idx);
399  auto shouldAbort = a_ElementCallback(m_LuaState, idx);
400  ASSERT(lua_gettop(m_LuaState) == stackTop + 1); // The element callback must not change the Lua stack below the value
401  lua_pop(m_LuaState, 1);
402  if (shouldAbort)
403  {
404  // The callback wants to abort
405  return;
406  }
407  }
408 }
409 
410 
411 
412 
413 
414 void cLuaState::cStackTable::ForEachElement(cFunctionRef<bool(cLuaState & a_LuaState)> a_ElementCallback) const
415 {
416  #ifdef _DEBUG
417  auto stackTop = lua_gettop(m_LuaState);
418  #endif
419  lua_pushvalue(m_LuaState, m_StackPos); // Stk: <table>
420  lua_pushnil(m_LuaState); // Stk: <table> nil
421  while (lua_next(m_LuaState, -2)) // Stk: <table> <key> <val>
422  {
423  auto shouldAbort = a_ElementCallback(m_LuaState);
424  ASSERT(lua_gettop(m_LuaState) == stackTop + 3); // The element callback must not change the Lua stack below the value
425  lua_pop(m_LuaState, 1); // Stk: <table> <key>
426  if (shouldAbort)
427  {
428  // The callback wants to abort
429  lua_pop(m_LuaState, 2); // Stk: empty
430  return;
431  }
432  }
433  // Stk: <table>
434  lua_pop(m_LuaState, 1); // Stk: empty
435 }
436 
437 
438 
439 
440 
442 // cLuaState:
443 
444 cLuaState::cLuaState(const AString & a_SubsystemName) :
445  m_LuaState(nullptr),
446  m_IsOwned(false),
447  m_SubsystemName(a_SubsystemName),
449 {
450 }
451 
452 
453 
454 
455 
456 cLuaState::cLuaState(lua_State * a_AttachState) :
457  m_LuaState(a_AttachState),
458  m_IsOwned(false),
459  m_SubsystemName("<attached>"),
461 {
462 }
463 
464 
465 
466 
467 
469 {
470  if (IsValid())
471  {
472  if (m_IsOwned)
473  {
474  Close();
475  }
476  else
477  {
478  Detach();
479  }
480  }
481 }
482 
483 
484 
485 
486 
488 {
489  if (m_LuaState != nullptr)
490  {
491  LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__);
492  return;
493  }
494  m_LuaState = lua_open();
495  luaL_openlibs(m_LuaState);
496  m_IsOwned = true;
497  cLuaStateTracker::Add(*this);
498  cCanonLuaStates::Add(*this);
499 }
500 
501 
502 
503 
504 
506 {
507  auto top = lua_gettop(m_LuaState);
508  tolua_AllToLua_open(m_LuaState);
509  ASSERT(top == lua_gettop(m_LuaState));
510  cManualBindings::Bind(m_LuaState);
511  ASSERT(top == lua_gettop(m_LuaState));
512  DeprecatedBindings::Bind(m_LuaState);
513  ASSERT(top == lua_gettop(m_LuaState));
514  cLuaJson::Bind(*this);
515  ASSERT(top == lua_gettop(m_LuaState));
517  if (top == lua_gettop(m_LuaState) - 1)
518  {
519  // lsqlite3 left the "sqlite3" table on the stack, pop it:
520  lua_pop(m_LuaState, 1);
521  }
522  ASSERT(top == lua_gettop(m_LuaState));
524  if (top == lua_gettop(m_LuaState) - 1)
525  {
526  // lxp left the unregistered "lxp" table on the stack, register and pop it (#3304):
527  lua_setglobal(m_LuaState, "lxp");
528  }
529  ASSERT(top == lua_gettop(m_LuaState));
530 }
531 
532 
533 
534 
535 
537 {
538  if (m_LuaState == nullptr)
539  {
540  LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__);
541  return;
542  }
543  if (!m_IsOwned)
544  {
545  LOGWARNING(
546  "%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.",
547  __FUNCTION__, static_cast<void *>(m_LuaState)
548  );
549  Detach();
550  return;
551  }
552 
553  // Invalidate all tracked refs:
554  {
555  cCSLock Lock(m_CSTrackedRefs);
556  for (auto & r: m_TrackedRefs)
557  {
558  r->Invalidate();
559  }
560  m_TrackedRefs.clear();
561  }
562 
564  cLuaStateTracker::Del(*this);
565  lua_close(m_LuaState);
566  m_LuaState = nullptr;
567  m_IsOwned = false;
568 }
569 
570 
571 
572 
573 
574 void cLuaState::Attach(lua_State * a_State)
575 {
576  if (m_LuaState != nullptr)
577  {
578  LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, static_cast<void *>(m_LuaState));
579  if (m_IsOwned)
580  {
581  Close();
582  }
583  else
584  {
585  Detach();
586  }
587  }
588  m_LuaState = a_State;
589  m_IsOwned = false;
590 }
591 
592 
593 
594 
595 
597 {
598  if (m_LuaState == nullptr)
599  {
600  return;
601  }
602  if (m_IsOwned)
603  {
604  LOGWARNING(
605  "%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).",
606  __FUNCTION__, static_cast<void *>(m_LuaState)
607  );
608  Close();
609  return;
610  }
611  m_LuaState = nullptr;
612 }
613 
614 
615 
616 
617 
618 void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path)
619 {
621 
622  // Get the current path:
623  lua_getfield(m_LuaState, LUA_GLOBALSINDEX, "package"); // Stk: <package>
624  lua_getfield(m_LuaState, -1, a_PathVariable.c_str()); // Stk: <package> <package.path>
625  size_t len = 0;
626  const char * PackagePath = lua_tolstring(m_LuaState, -1, &len);
627 
628  // Append the new path:
629  AString NewPackagePath(PackagePath, len);
630  NewPackagePath.append(LUA_PATHSEP);
631  NewPackagePath.append(a_Path);
632 
633  // Set the new path to the environment:
634  lua_pop(m_LuaState, 1); // Stk: <package>
635  lua_pushlstring(m_LuaState, NewPackagePath.c_str(), NewPackagePath.length()); // Stk: <package> <NewPackagePath>
636  lua_setfield(m_LuaState, -2, a_PathVariable.c_str()); // Stk: <package>
637  lua_pop(m_LuaState, 1); // Stk: -
638 }
639 
640 
641 
642 
643 
644 bool cLuaState::LoadFile(const AString & a_FileName, bool a_LogWarnings)
645 {
646  ASSERT(IsValid());
648 
649  // Load the file:
650  int s = luaL_loadfile(m_LuaState, a_FileName.c_str());
651  if (s != 0)
652  {
653  if (a_LogWarnings)
654  {
655  LOGWARNING("Can't load %s because of a load error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
656  }
657  lua_pop(m_LuaState, 1);
658  return false;
659  }
660 
661  // Execute the globals:
662  s = lua_pcall(m_LuaState, 0, 0, 0);
663  if (s != 0)
664  {
665  if (a_LogWarnings)
666  {
667  LOGWARNING("Can't load %s because of an initialization error in file %s: %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
668  }
669  lua_pop(m_LuaState, 1);
670  return false;
671  }
672 
673  return true;
674 }
675 
676 
677 
678 
679 
680 bool cLuaState::LoadString(const AString & a_StringToLoad, const AString & a_FileName, bool a_LogWarnings)
681 {
682  ASSERT(IsValid());
684 
685  // Load the file:
686  int s = luaL_loadstring(m_LuaState, a_StringToLoad.c_str());
687  if (s != 0)
688  {
689  if (a_LogWarnings)
690  {
691  LOGWARNING("Can't load %s because of a load error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
692  }
693  lua_pop(m_LuaState, 1);
694  return false;
695  }
696 
697  // Execute the globals:
698  s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
699  if (s != 0)
700  {
701  if (a_LogWarnings)
702  {
703  LOGWARNING("Can't load %s because of an initialization error in string from \"%s\": %d (%s)", m_SubsystemName.c_str(), a_FileName.c_str(), s, lua_tostring(m_LuaState, -1));
704  }
705  lua_pop(m_LuaState, 1);
706  return false;
707  }
708 
709  return true;
710 }
711 
712 
713 
714 
715 
716 bool cLuaState::HasFunction(const char * a_FunctionName)
717 {
718  if (!IsValid())
719  {
720  // This happens if cPlugin::Initialize() fails with an error
721  return false;
722  }
723 
725  lua_getglobal(m_LuaState, a_FunctionName);
726  bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1));
727  lua_pop(m_LuaState, 1);
728  return res;
729 }
730 
731 
732 
733 
734 
735 bool cLuaState::PushFunction(const char * a_FunctionName)
736 {
737  ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
738 
739  if (!IsValid())
740  {
741  // This happens if cPlugin::Initialize() fails with an error
742  return false;
743  }
744 
745  // Push the error handler for lua_pcall()
746  lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
747 
748  lua_getglobal(m_LuaState, a_FunctionName);
749  if (!lua_isfunction(m_LuaState, -1))
750  {
751  LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
752  lua_pop(m_LuaState, 2);
753  return false;
754  }
755  m_CurrentFunctionName.assign(a_FunctionName);
757  return true;
758 }
759 
760 
761 
762 
763 
764 bool cLuaState::PushFunction(const cRef & a_FnRef)
765 {
766  ASSERT(IsValid());
767  ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
768 
769  // Push the error handler for lua_pcall()
770  lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
771 
772  lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_FnRef)); // same as lua_getref()
773  if (!lua_isfunction(m_LuaState, -1))
774  {
775  lua_pop(m_LuaState, 2);
776  return false;
777  }
778  m_CurrentFunctionName = "<callback>";
780  return true;
781 }
782 
783 
784 
785 
786 
787 bool cLuaState::PushFunction(const cRef & a_TableRef, const char * a_FnName)
788 {
789  ASSERT(IsValid());
790  ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
791 
792  if (!a_TableRef.IsValid())
793  {
794  return false;
795  }
796 
797  // Push the error handler for lua_pcall()
798  lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
799 
800  // Get the function from the table:
801  lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_TableRef));
802  if (!lua_istable(m_LuaState, -1))
803  {
804  // Not a table, bail out
805  lua_pop(m_LuaState, 2);
806  return false;
807  }
808  lua_getfield(m_LuaState, -1, a_FnName);
809  if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
810  {
811  // Not a valid function, bail out
812  lua_pop(m_LuaState, 3);
813  return false;
814  }
815 
816  // Pop the table off the stack:
817  lua_remove(m_LuaState, -2);
818 
819  Printf(m_CurrentFunctionName, "<table-callback %s>", a_FnName);
821  return true;
822 }
823 
824 
825 
826 
827 
828 void cLuaState::Push(const AString & a_String)
829 {
830  ASSERT(IsValid());
831 
832  lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
833 }
834 
835 
836 
837 
838 
839 void cLuaState::Push(const AStringMap & a_Dictionary)
840 {
841  ASSERT(IsValid());
842 
843  lua_createtable(m_LuaState, 0, static_cast<int>(a_Dictionary.size()));
844  int newTable = lua_gettop(m_LuaState);
845  for (const auto & item: a_Dictionary)
846  {
847  Push(item.first); // key
848  Push(item.second); // value
849  lua_rawset(m_LuaState, newTable);
850  }
851 }
852 
853 
854 
855 
856 
857 void cLuaState::Push(const AStringVector & a_Vector)
858 {
859  ASSERT(IsValid());
860 
861  lua_createtable(m_LuaState, static_cast<int>(a_Vector.size()), 0);
862  int newTable = lua_gettop(m_LuaState);
863  int index = 1;
864  for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index)
865  {
866  tolua_pushstring(m_LuaState, itr->c_str());
867  lua_rawseti(m_LuaState, newTable, index);
868  }
869 }
870 
871 
872 
873 
874 
875 void cLuaState::Push(const char * a_Value)
876 {
877  ASSERT(IsValid());
878 
879  tolua_pushstring(m_LuaState, a_Value);
880 }
881 
882 
883 
884 
885 
886 void cLuaState::Push(const cItem & a_Item)
887 {
888  ASSERT(IsValid());
889  auto c = new cItem(a_Item);
890  tolua_pushusertype_and_takeownership(m_LuaState, c, "cItem");
891 }
892 
893 
894 
895 
896 
897 void cLuaState::Push(const cNil & a_Nil)
898 {
899  ASSERT(IsValid());
900 
901  lua_pushnil(m_LuaState);
902 }
903 
904 
905 
906 
907 
908 void cLuaState::Push(const cLuaState::cRef & a_Ref)
909 {
910  ASSERT(IsValid());
911 
912  lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_Ref));
913 }
914 
915 
916 
917 
918 
919 void cLuaState::Push(const Vector3d & a_Vector)
920 {
921  ASSERT(IsValid());
922  auto c = new Vector3d(a_Vector);
923  tolua_pushusertype_and_takeownership(m_LuaState, c, "Vector3<double>");
924 }
925 
926 
927 
928 
929 
930 void cLuaState::Push(const Vector3i & a_Vector)
931 {
932  ASSERT(IsValid());
933  auto c = new Vector3i(a_Vector);
934  tolua_pushusertype_and_takeownership(m_LuaState, c, "Vector3<int>");
935 }
936 
937 
938 
939 
940 
941 void cLuaState::Push(bool a_Value)
942 {
943  ASSERT(IsValid());
944 
945  tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
946 }
947 
948 
949 
950 
951 
952 void cLuaState::Push(cEntity * a_Entity)
953 {
954  ASSERT(IsValid());
955 
956  if (a_Entity == nullptr)
957  {
958  lua_pushnil(m_LuaState);
959  }
960  else
961  {
962  const char * ClassName = [&]
963  {
964  switch (a_Entity->GetEntityType())
965  {
966  case cEntity::etBoat: return "cBoat";
967  case cEntity::etExpOrb: return "cExpOrb";
968  case cEntity::etFallingBlock: return "cFallingBlock";
969  case cEntity::etFloater: return "cFloater";
970  case cEntity::etItemFrame: return "cItemFrame";
971  case cEntity::etLeashKnot: return "cLeashKnot";
972  case cEntity::etPainting: return "cPainting";
973  case cEntity::etPickup: return "cPickup";
974  case cEntity::etPlayer: return "cPlayer";
975  case cEntity::etTNT: return "cTNTEntity";
976 
977  case cEntity::etMonster:
978  {
979  // Don't push specific mob types, as those are not exported in the API:
980  return "cMonster";
981  }
983  {
984  // Push the specific projectile type:
985  return a_Entity->GetClass();
986  }
987 
988  case cEntity::etEntity:
990  case cEntity::etMinecart:
991  {
992  // Push the generic entity class type:
993  return "cEntity";
994  }
995  } // switch (EntityType)
996  UNREACHABLE("Unsupported entity type");
997  }();
998  tolua_pushusertype(m_LuaState, a_Entity, ClassName);
999  }
1000 }
1001 
1002 
1003 
1004 
1005 
1006 void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
1007 {
1008  ASSERT(IsValid());
1009 
1010  tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
1011 }
1012 
1013 
1014 
1015 
1016 
1017 void cLuaState::Push(cLuaTCPLink * a_TCPLink)
1018 {
1019  ASSERT(IsValid());
1020 
1021  tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
1022 }
1023 
1024 
1025 
1026 
1027 
1028 void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint)
1029 {
1030  ASSERT(IsValid());
1031 
1032  tolua_pushusertype(m_LuaState, a_UDPEndpoint, "cUDPEndpoint");
1033 }
1034 
1035 
1036 
1037 
1038 
1039 void cLuaState::Push(double a_Value)
1040 {
1041  ASSERT(IsValid());
1042 
1043  tolua_pushnumber(m_LuaState, a_Value);
1044 }
1045 
1046 
1047 
1048 
1049 
1050 void cLuaState::Push(int a_Value)
1051 {
1052  ASSERT(IsValid());
1053 
1054  tolua_pushnumber(m_LuaState, a_Value);
1055 }
1056 
1057 
1058 
1059 
1060 
1061 void cLuaState::Push(long a_Value)
1062 {
1063  ASSERT(IsValid());
1064 
1065  tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value));
1066 }
1067 
1068 
1069 
1070 
1071 
1073 {
1074  ASSERT(IsValid());
1075 
1076  tolua_pushnumber(m_LuaState, a_Value);
1077 }
1078 
1079 
1080 
1081 
1082 
1083 void cLuaState::Push(std::chrono::milliseconds a_Value)
1084 {
1085  ASSERT(IsValid());
1086 
1087  tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count()));
1088 }
1089 
1090 
1091 
1092 
1093 
1094 void cLuaState::Pop(int a_NumValuesToPop)
1095 {
1096  ASSERT(IsValid());
1097 
1098  lua_pop(m_LuaState, a_NumValuesToPop);
1099 }
1100 
1101 
1102 
1103 
1104 
1105 bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
1106 {
1107  size_t len = 0;
1108  const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
1109  if (data != nullptr)
1110  {
1111  a_Value.assign(data, len);
1112  return true;
1113  }
1114  return false;
1115 }
1116 
1117 
1118 
1119 
1120 
1121 bool cLuaState::GetStackValue(int a_StackPos, AStringMap & a_Value)
1122 {
1123  // Retrieve all values in a string => string dictionary table:
1124  if (!lua_istable(m_LuaState, a_StackPos))
1125  {
1126  return false;
1127  }
1128  cStackTable tbl(*this, a_StackPos);
1129  bool isValid = true;
1130  tbl.ForEachElement([&isValid, &a_Value](cLuaState & a_LuaState)
1131  {
1132  AString key, val;
1133  if (a_LuaState.GetStackValues(-2, key, val))
1134  {
1135  a_Value[key] = val;
1136  }
1137  else
1138  {
1139  isValid = false;
1140  return true;
1141  }
1142  return false;
1143  }
1144  );
1145  return isValid;
1146 }
1147 
1148 
1149 
1150 
1151 
1152 bool cLuaState::GetStackValue(int a_StackPos, AStringVector & a_Value)
1153 {
1154  // Retrieve all values in an array of string table:
1155  if (!lua_istable(m_LuaState, a_StackPos))
1156  {
1157  return false;
1158  }
1159  cStackTable tbl(*this, a_StackPos);
1160  bool isValid = true;
1161  tbl.ForEachArrayElement([&](cLuaState & a_LuaState, int a_Index)
1162  {
1163  AString tempStr;
1164  if (a_LuaState.GetStackValue(-1, tempStr))
1165  {
1166  a_Value.push_back(std::move(tempStr));
1167  }
1168  else
1169  {
1170  isValid = false;
1171  return true;
1172  }
1173  return false;
1174  }
1175  );
1176  return isValid;
1177 }
1178 
1179 
1180 
1181 
1182 
1183 bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
1184 {
1185  a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
1186  return true;
1187 }
1188 
1189 
1190 
1191 
1192 
1193 bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback)
1194 {
1195  return a_Callback.RefStack(*this, a_StackPos);
1196 }
1197 
1198 
1199 
1200 
1201 
1202 bool cLuaState::GetStackValue(int a_StackPos, cCallbackPtr & a_Callback)
1203 {
1204  if (a_Callback == nullptr)
1205  {
1206  a_Callback = cpp14::make_unique<cCallback>();
1207  }
1208  return a_Callback->RefStack(*this, a_StackPos);
1209 }
1210 
1211 
1212 
1213 
1214 
1215 bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallback & a_Callback)
1216 {
1217  return a_Callback.RefStack(*this, a_StackPos);
1218 }
1219 
1220 
1221 
1222 
1223 
1224 bool cLuaState::GetStackValue(int a_StackPos, cOptionalCallbackPtr & a_Callback)
1225 {
1226  if (a_Callback == nullptr)
1227  {
1228  a_Callback = cpp14::make_unique<cOptionalCallback>();
1229  }
1230  return a_Callback->RefStack(*this, a_StackPos);
1231 }
1232 
1233 
1234 
1235 
1236 
1237 bool cLuaState::GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback)
1238 {
1239  if (a_Callback == nullptr)
1240  {
1241  a_Callback = std::make_shared<cCallback>();
1242  }
1243  return a_Callback->RefStack(*this, a_StackPos);
1244 }
1245 
1246 
1247 
1248 
1249 
1251 {
1252  if (lua_isnumber(m_LuaState, a_StackPos))
1253  {
1254  a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result))));
1255  return true;
1256  }
1257  return false;
1258 }
1259 
1260 
1261 
1262 
1263 
1264 bool cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
1265 {
1266  a_Ref.RefStack(*this, a_StackPos);
1267  return true;
1268 }
1269 
1270 
1271 
1272 
1273 
1274 bool cLuaState::GetStackValue(int a_StackPos, cStackTablePtr & a_StackTable)
1275 {
1276  // Only allow tables to be stored in a_StackTable:
1277  if (!lua_istable(m_LuaState, a_StackPos))
1278  {
1279  return false;
1280  }
1281 
1282  // Assign the StackTable to the specified stack position:
1283  a_StackTable = cpp14::make_unique<cStackTable>(*this, a_StackPos);
1284  return true;
1285 }
1286 
1287 
1288 
1289 
1290 
1291 bool cLuaState::GetStackValue(int a_StackPos, cTableRef & a_TableRef)
1292 {
1293  return a_TableRef.RefStack(*this, a_StackPos);
1294 }
1295 
1296 
1297 
1298 
1299 
1300 bool cLuaState::GetStackValue(int a_StackPos, cTableRefPtr & a_TableRef)
1301 {
1302  if (a_TableRef == nullptr)
1303  {
1304  a_TableRef = cpp14::make_unique<cTableRef>();
1305  }
1306  return a_TableRef->RefStack(*this, a_StackPos);
1307 }
1308 
1309 
1310 
1311 
1312 
1313 bool cLuaState::GetStackValue(int a_StackPos, cTrackedRef & a_Ref)
1314 {
1315  return a_Ref.RefStack(*this, a_StackPos);
1316 }
1317 
1318 
1319 
1320 
1321 
1322 bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefPtr & a_Ref)
1323 {
1324  if (a_Ref == nullptr)
1325  {
1326  a_Ref = cpp14::make_unique<cTrackedRef>();
1327  }
1328  return a_Ref->RefStack(*this, a_StackPos);
1329 }
1330 
1331 
1332 
1333 
1334 
1335 bool cLuaState::GetStackValue(int a_StackPos, cTrackedRefSharedPtr & a_Ref)
1336 {
1337  if (a_Ref == nullptr)
1338  {
1339  a_Ref = std::make_shared<cTrackedRef>();
1340  }
1341  return a_Ref->RefStack(*this, a_StackPos);
1342 }
1343 
1344 
1345 
1346 
1347 
1348 bool cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
1349 {
1350  if (lua_isnumber(m_LuaState, a_StackPos))
1351  {
1352  a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal);
1353  return true;
1354  }
1355  return false;
1356 }
1357 
1358 
1359 
1360 
1361 
1362 bool cLuaState::GetStackValue(int a_StackPos, eBlockFace & a_ReturnedVal)
1363 {
1364  if (!lua_isnumber(m_LuaState, a_StackPos))
1365  {
1366  return false;
1367  }
1368  a_ReturnedVal = static_cast<eBlockFace>(Clamp(
1369  static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
1370  static_cast<int>(BLOCK_FACE_MIN), static_cast<int>(BLOCK_FACE_MAX))
1371  );
1372  return true;
1373 }
1374 
1375 
1376 
1377 
1378 
1379 bool cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
1380 {
1381  if (!lua_isnumber(m_LuaState, a_StackPos))
1382  {
1383  return false;
1384  }
1385  a_ReturnedVal = static_cast<eWeather>(Clamp(
1386  static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)),
1387  static_cast<int>(wSunny), static_cast<int>(wThunderstorm))
1388  );
1389  return true;
1390 }
1391 
1392 
1393 
1394 
1395 
1396 bool cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal)
1397 {
1398  if (lua_isnumber(m_LuaState, a_StackPos))
1399  {
1400  a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal));
1401  return true;
1402  }
1403  return false;
1404 }
1405 
1406 
1407 
1408 
1409 
1410 bool cLuaState::GetStackValue(int a_StackPos, cUUID & a_Value)
1411 {
1412  if (lua_isnil(m_LuaState, a_StackPos))
1413  {
1414  return false;
1415  }
1416 
1417  tolua_Error tolua_Err;
1418  if (tolua_isusertype(m_LuaState, a_StackPos, "cUUID", 0, &tolua_Err))
1419  {
1420  // Found a cUUID, copy into output value
1421  cUUID * PtrUUID = nullptr;
1422  GetStackValue(a_StackPos, PtrUUID);
1423  if (PtrUUID == nullptr)
1424  {
1425  return false;
1426  }
1427  a_Value = *PtrUUID;
1428  return true;
1429  }
1430 
1431  // Try to get a string and parse as a UUID into the output
1432  AString StrUUID;
1433  if (!GetStackValue(a_StackPos, StrUUID))
1434  {
1435  return false;
1436  }
1437  return a_Value.FromString(StrUUID);
1438 }
1439 
1440 
1441 
1442 
1443 
1445 {
1446  // There needs to be at least one value on the stack:
1447  ASSERT(lua_gettop(m_LuaState) > 0);
1448 
1449  // Iterate over path and replace the top of the stack with the walked element
1450  lua_pushvalue(m_LuaState, -1); // Copy the stack value into the "working area"
1451  auto path = StringSplit(a_Name, ".");
1452  for (const auto & elem: path)
1453  {
1454  // If the value is not a table, bail out (error):
1455  if (!lua_istable(m_LuaState, -1))
1456  {
1457  lua_pop(m_LuaState, 1);
1458  return cStackValue();
1459  }
1460 
1461  // Get the next part of the path:
1462  lua_getfield(m_LuaState, -1, elem.c_str());
1463 
1464  // Remove the previous value from the stack (keep only the new one):
1465  lua_remove(m_LuaState, -2);
1466  } // for elem - path[]
1467  if (lua_isnil(m_LuaState, -1))
1468  {
1469  lua_pop(m_LuaState, 1);
1470  return cStackValue();
1471  }
1472  return cStackValue(*this);
1473 }
1474 
1475 
1476 
1477 
1478 
1480 {
1481  // Iterate over path and replace the top of the stack with the walked element
1482  lua_getglobal(m_LuaState, "_G");
1483  auto path = StringSplit(a_Name, ".");
1484  for (const auto & elem: path)
1485  {
1486  // If the value is not a table, bail out (error):
1487  if (!lua_istable(m_LuaState, -1))
1488  {
1489  lua_pop(m_LuaState, 1);
1490  return cStackValue();
1491  }
1492 
1493  // Get the next part of the path:
1494  lua_getfield(m_LuaState, -1, elem.c_str());
1495 
1496  // Remove the previous value from the stack (keep only the new one):
1497  lua_remove(m_LuaState, -2);
1498  } // for elem - path[]
1499  if (lua_isnil(m_LuaState, -1))
1500  {
1501  lua_pop(m_LuaState, 1);
1502  return cStackValue();
1503  }
1504  return cStackValue(*this);
1505 }
1506 
1507 
1508 
1509 
1510 
1511 bool cLuaState::CallFunction(int a_NumResults)
1512 {
1513  ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
1514  ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
1515  ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
1516 
1517  // Save the current "stack" state and reset, in case the callback calls another function:
1518  AString CurrentFunctionName;
1519  std::swap(m_CurrentFunctionName, CurrentFunctionName);
1520  int NumArgs = m_NumCurrentFunctionArgs;
1522 
1523  // Call the function:
1524  int s = lua_pcall(m_LuaState, NumArgs, a_NumResults, -NumArgs - 2);
1525  if (s != 0)
1526  {
1527  // The error has already been printed together with the stacktrace
1528  LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str());
1529 
1530  // Remove the error handler and error message from the stack:
1531  auto top = lua_gettop(m_LuaState);
1532  if (top < 2)
1533  {
1534  LogStackValues(Printf("The Lua stack is in an unexpected state, expected at least two values there, but got %d", top).c_str());
1535  }
1536  lua_pop(m_LuaState, std::min(2, top));
1537  return false;
1538  }
1539 
1540  // Remove the error handler from the stack:
1541  lua_remove(m_LuaState, -a_NumResults - 1);
1542  return true;
1543 }
1544 
1545 
1546 
1547 
1548 
1549 bool cLuaState::CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam)
1550 {
1551  ASSERT(IsValid());
1552 
1553  if (a_EndParam < 0)
1554  {
1555  a_EndParam = a_StartParam;
1556  }
1557 
1558  tolua_Error tolua_err;
1559  for (int i = a_StartParam; i <= a_EndParam; i++)
1560  {
1561  if (tolua_isusertable(m_LuaState, i, a_UserTable, 0, &tolua_err))
1562  {
1563  continue;
1564  }
1565  // Not the correct parameter
1566  lua_Debug entry;
1567  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1568  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1569  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1570  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1571  return false;
1572  } // for i - Param
1573 
1574  // All params checked ok
1575  return true;
1576 }
1577 
1578 
1579 
1580 
1581 
1582 bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam)
1583 {
1584  ASSERT(IsValid());
1585 
1586  if (a_EndParam < 0)
1587  {
1588  a_EndParam = a_StartParam;
1589  }
1590 
1591  tolua_Error tolua_err;
1592  for (int i = a_StartParam; i <= a_EndParam; i++)
1593  {
1594  if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err))
1595  {
1596  continue;
1597  }
1598  // Not the correct parameter
1599  lua_Debug entry;
1600  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1601  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1602  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1603  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1604  return false;
1605  } // for i - Param
1606 
1607  // All params checked ok
1608  return true;
1609 }
1610 
1611 
1612 
1613 
1614 
1615 bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
1616 {
1617  ASSERT(IsValid());
1618 
1619  if (a_EndParam < 0)
1620  {
1621  a_EndParam = a_StartParam;
1622  }
1623 
1624  tolua_Error tolua_err;
1625  for (int i = a_StartParam; i <= a_EndParam; i++)
1626  {
1627  if (tolua_istable(m_LuaState, i, 0, &tolua_err))
1628  {
1629  continue;
1630  }
1631  // Not the correct parameter
1632  lua_Debug entry;
1633  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1634  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1635  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1636 
1638 
1639  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1640  return false;
1641  } // for i - Param
1642 
1643  // All params checked ok
1644  return true;
1645 }
1646 
1647 
1648 
1649 
1650 
1651 bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam)
1652 {
1653  ASSERT(IsValid());
1654 
1655  if (a_EndParam < 0)
1656  {
1657  a_EndParam = a_StartParam;
1658  }
1659 
1660  tolua_Error tolua_err;
1661  for (int i = a_StartParam; i <= a_EndParam; i++)
1662  {
1663  if (tolua_isnumber(m_LuaState, i, 0, &tolua_err))
1664  {
1665  continue;
1666  }
1667  // Not the correct parameter
1668  lua_Debug entry;
1669  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1670  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1671  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1672  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1673  return false;
1674  } // for i - Param
1675 
1676  // All params checked ok
1677  return true;
1678 }
1679 
1680 
1681 
1682 
1683 
1684 bool cLuaState::CheckParamBool(int a_StartParam, int a_EndParam)
1685 {
1686  ASSERT(IsValid());
1687 
1688  if (a_EndParam < 0)
1689  {
1690  a_EndParam = a_StartParam;
1691  }
1692 
1693  tolua_Error tolua_err;
1694  for (int i = a_StartParam; i <= a_EndParam; i++)
1695  {
1696  if (tolua_isboolean(m_LuaState, i, 0, &tolua_err))
1697  {
1698  continue;
1699  }
1700  // Not the correct parameter
1701  lua_Debug entry;
1702  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1703  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1704  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1705  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1706  return false;
1707  } // for i - Param
1708 
1709  // All params checked ok
1710  return true;
1711 }
1712 
1713 
1714 
1715 
1716 
1717 bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
1718 {
1719  ASSERT(IsValid());
1720 
1721  if (a_EndParam < 0)
1722  {
1723  a_EndParam = a_StartParam;
1724  }
1725 
1726  tolua_Error tolua_err;
1727  for (int i = a_StartParam; i <= a_EndParam; i++)
1728  {
1729  if (lua_isstring(m_LuaState, i))
1730  {
1731  continue;
1732  }
1733  // Not the correct parameter
1734  lua_Debug entry;
1735  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1736  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1737  tolua_err.array = 0;
1738  tolua_err.type = "string";
1739  tolua_err.index = i;
1740  AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
1741  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1742  return false;
1743  } // for i - Param
1744 
1745  // All params checked ok
1746  return true;
1747 }
1748 
1749 
1750 
1751 
1752 
1753 bool cLuaState::CheckParamFunction(int a_StartParam, int a_EndParam)
1754 {
1755  ASSERT(IsValid());
1756 
1757  if (a_EndParam < 0)
1758  {
1759  a_EndParam = a_StartParam;
1760  }
1761 
1762  for (int i = a_StartParam; i <= a_EndParam; i++)
1763  {
1764  if (lua_isfunction(m_LuaState, i))
1765  {
1766  continue;
1767  }
1768  // Not the correct parameter
1769  lua_Debug entry;
1770  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1771  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1772  luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
1773  (entry.name != nullptr) ? entry.name : "?", i, GetTypeText(i).c_str()
1774  );
1775  return false;
1776  } // for i - Param
1777 
1778  // All params checked ok
1779  return true;
1780 }
1781 
1782 
1783 
1784 
1785 
1786 bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
1787 {
1788  ASSERT(IsValid());
1789 
1790  if (a_EndParam < 0)
1791  {
1792  a_EndParam = a_StartParam;
1793  }
1794 
1795  for (int i = a_StartParam; i <= a_EndParam; i++)
1796  {
1797  if (lua_isfunction(m_LuaState, i) || lua_isnil(m_LuaState, i))
1798  {
1799  continue;
1800  }
1801  // Not the correct parameter
1802  lua_Debug entry;
1803  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1804  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1805  luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
1806  (entry.name != nullptr) ? entry.name : "?", i, GetTypeText(i).c_str()
1807  );
1808  return false;
1809  } // for i - Param
1810 
1811  // All params checked ok
1812  return true;
1813 }
1814 
1815 
1816 
1817 
1818 
1819 bool cLuaState::CheckParamUUID(int a_StartParam, int a_EndParam)
1820 {
1821  ASSERT(IsValid());
1822 
1823  if (a_EndParam < 0)
1824  {
1825  a_EndParam = a_StartParam;
1826  }
1827 
1828  cUUID tempUUID;
1829  AString tempStr;
1830  // Accept either a cUUID or a string that contains a valid UUID
1831  for (int i = a_StartParam; i <= a_EndParam; ++i)
1832  {
1833  tolua_Error err;
1834  if (tolua_isusertype(m_LuaState, i, "cUUID", 0, &err) && !lua_isnil(m_LuaState, i))
1835  {
1836  continue;
1837  }
1838 
1839  if (!tolua_iscppstring(m_LuaState, i, 0, &err))
1840  {
1841  ApiParamError("Failed to read parameter #%d. UUID expected, got %s", i, GetTypeText(i).c_str());
1842  return false;
1843  }
1844 
1845  // Check string is a valid UUID
1846  GetStackValue(i, tempStr);
1847  if (!tempUUID.FromString(tempStr))
1848  {
1849  ApiParamError("Failed to read parameter #%d. UUID expected, got non-UUID string:\n\t\"%s\"", i, tempStr.c_str());
1850  return false;
1851  }
1852  }
1853  return true;
1854 }
1855 
1856 
1857 
1858 
1859 
1860 bool cLuaState::CheckParamEnd(int a_Param)
1861 {
1862  tolua_Error tolua_err;
1863  if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1)
1864  {
1865  return true;
1866  }
1867  // Not the correct parameter
1868  lua_Debug entry;
1869  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1870  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1871  AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != nullptr) ? entry.name : "?");
1872  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1873  return false;
1874 }
1875 
1876 
1877 
1878 
1879 
1880 bool cLuaState::CheckParamSelf(const char * a_SelfClassName)
1881 {
1882  tolua_Error tolua_err;
1883  if (tolua_isusertype(m_LuaState, 1, a_SelfClassName, 0, &tolua_err) && !lua_isnil(m_LuaState, 1))
1884  {
1885  return true;
1886  }
1887 
1888  // Not the correct parameter
1889  lua_Debug entry;
1890  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1891  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1892  AString ErrMsg = Printf(
1893  "Error in function '%s'. The 'self' parameter is not of the expected type, \"instance of %s\". " \
1894  "Make sure you're using the correct calling convention (obj:fn() instead of obj.fn()).",
1895  (entry.name != nullptr) ? entry.name : "<unknown>", a_SelfClassName
1896  );
1897  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1898  return false;
1899 }
1900 
1901 
1902 
1903 
1904 
1905 bool cLuaState::CheckParamStaticSelf(const char * a_SelfClassName)
1906 {
1907  tolua_Error tolua_err;
1908  if (tolua_isusertable(m_LuaState, 1, a_SelfClassName, 0, &tolua_err) && !lua_isnil(m_LuaState, 1))
1909  {
1910  return true;
1911  }
1912 
1913  // Not the correct parameter
1914  lua_Debug entry;
1915  VERIFY(lua_getstack(m_LuaState, 0, &entry));
1916  VERIFY(lua_getinfo (m_LuaState, "n", &entry));
1917  AString ErrMsg = Printf(
1918  "Error in function '%s'. The 'self' parameter is not of the expected type, \"class %s\". " \
1919  "Make sure you're using the correct calling convention (cClassName:fn() instead of cClassName.fn() or obj:fn()).",
1920  (entry.name != nullptr) ? entry.name : "<unknown>", a_SelfClassName
1921  );
1922  tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
1923  return false;
1924 }
1925 
1926 
1927 
1928 
1929 
1930 bool cLuaState::IsParamUserType(int a_Param, AString a_UserType)
1931 {
1932  ASSERT(IsValid());
1933 
1934  tolua_Error tolua_err;
1935  return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1);
1936 }
1937 
1938 
1939 
1940 
1941 
1942 bool cLuaState::IsParamNumber(int a_Param)
1943 {
1944  ASSERT(IsValid());
1945 
1946  tolua_Error tolua_err;
1947  return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1);
1948 }
1949 
1950 
1951 
1952 
1953 
1954 bool cLuaState::ReportErrors(int a_Status)
1955 {
1956  return ReportErrors(m_LuaState, a_Status);
1957 }
1958 
1959 
1960 
1961 
1962 
1963 bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
1964 {
1965  if (a_Status == 0)
1966  {
1967  // No error to report
1968  return false;
1969  }
1970 
1971  LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1));
1972  lua_pop(a_LuaState, 1);
1973  return true;
1974 }
1975 
1976 
1977 
1978 
1979 
1980 void cLuaState::LogStackTrace(int a_StartingDepth)
1981 {
1982  LogStackTrace(m_LuaState, a_StartingDepth);
1983 }
1984 
1985 
1986 
1987 
1988 
1989 void cLuaState::LogStackTrace(lua_State * a_LuaState, int a_StartingDepth)
1990 {
1991  LOGWARNING("Stack trace:");
1992  lua_Debug entry;
1993  int depth = a_StartingDepth;
1994  while (lua_getstack(a_LuaState, depth, &entry))
1995  {
1996  lua_getinfo(a_LuaState, "Sln", &entry);
1997  LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
1998  depth++;
1999  }
2000  LOGWARNING("Stack trace end");
2001 }
2002 
2003 
2004 
2005 
2006 
2007 int cLuaState::ApiParamError(fmt::StringRef a_Msg)
2008 {
2009  // Retrieve current function name
2010  lua_Debug entry;
2011  VERIFY(lua_getstack(m_LuaState, 0, &entry));
2012  VERIFY(lua_getinfo(m_LuaState, "n", &entry));
2013 
2014  // Compose the error message:
2015  AString errorMsg = fmt::format("{0}: {1}", (entry.name != nullptr) ? entry.name : "<unknown function>", a_Msg);
2016 
2017  // Log everything into the console:
2018  LOGWARNING("%s", errorMsg.c_str());
2019  // cLuaState::LogStackTrace(a_LuaState); // Do NOT log stack trace, it is already output as part of the Lua error handling
2020  LogStackValues(m_LuaState, "Parameters on the stack");
2021 
2022  // Raise Lua error:
2023  lua_pushstring(m_LuaState, errorMsg.c_str());
2024  return lua_error(m_LuaState);
2025 }
2026 
2027 
2028 
2029 
2030 
2032 {
2033  return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos));
2034 }
2035 
2036 
2037 
2038 
2039 
2041  const AString & a_FunctionName,
2042  cLuaState & a_SrcLuaState,
2043  int a_SrcParamStart,
2044  int a_SrcParamEnd
2045 )
2046 {
2047  ASSERT(IsValid());
2048  ASSERT(a_SrcLuaState.IsValid());
2049 
2050  // Store the stack position before any changes
2051  int OldTop = lua_gettop(m_LuaState);
2052 
2053  // Push the function to call, including the error handler:
2054  if (!PushFunction(a_FunctionName.c_str()))
2055  {
2056  LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
2057  lua_settop(m_LuaState, OldTop);
2058  return -1;
2059  }
2060 
2061  // Copy the function parameters to the target state
2062  if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
2063  {
2064  // Something went wrong, fix the stack and exit
2065  lua_settop(m_LuaState, OldTop);
2067  m_CurrentFunctionName.clear();
2068  return -1;
2069  }
2070 
2071  // Call the function, with an error handler:
2072  int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop + 1);
2073  if (ReportErrors(s))
2074  {
2075  LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
2076  // Reset the stack:
2077  lua_settop(m_LuaState, OldTop);
2078 
2079  // Reset the internal checking mechanisms:
2081  m_CurrentFunctionName.clear();
2082 
2083  // Make Lua think everything is okay and return 0 values, so that plugins continue executing.
2084  // The failure is indicated by the zero return values.
2085  return 0;
2086  }
2087 
2088  // Reset the internal checking mechanisms:
2090  m_CurrentFunctionName.clear();
2091 
2092  // Remove the error handler from the stack:
2093  lua_remove(m_LuaState, OldTop + 1);
2094 
2095  // Return the number of return values:
2096  return lua_gettop(m_LuaState) - OldTop;
2097 }
2098 
2099 
2100 
2101 
2102 
2103 int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd, int a_NumAllowedNestingLevels)
2104 {
2105  /*
2106  // DEBUG:
2107  LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd);
2108  a_SrcLuaState.LogStack("Src stack before copying:");
2109  LogStack("Dst stack before copying:");
2110  */
2111  for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
2112  {
2113  if (!CopySingleValueFrom(a_SrcLuaState, i, a_NumAllowedNestingLevels))
2114  {
2115  lua_pop(m_LuaState, i - a_SrcStart);
2116  return -1;
2117  }
2118  }
2119  return a_SrcEnd - a_SrcStart + 1;
2120 }
2121 
2122 
2123 
2124 
2125 
2126 bool cLuaState::CopyTableFrom(cLuaState & a_SrcLuaState, int a_SrcStackIdx, int a_NumAllowedNestingLevels)
2127 {
2128  // Create the dest table:
2129  #ifdef _DEBUG
2130  auto srcTop = lua_gettop(a_SrcLuaState);
2131  auto dstTop = lua_gettop(m_LuaState);
2132  #endif
2133  lua_createtable(m_LuaState, 0, 0); // DST: <table>
2134  lua_pushvalue(a_SrcLuaState, a_SrcStackIdx); // SRC: <table>
2135  lua_pushnil(a_SrcLuaState); // SRC: <table> <key>
2136  while (lua_next(a_SrcLuaState, -2) != 0) // SRC: <table> <key> <value>
2137  {
2138  ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
2139  ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
2140 
2141  // Copy the key:
2142  if (!CopySingleValueFrom(a_SrcLuaState, -2, a_NumAllowedNestingLevels)) // DST: <table> <key>
2143  {
2144  lua_pop(m_LuaState, 1);
2145  lua_pop(a_SrcLuaState, 3);
2146  ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
2147  ASSERT(lua_gettop(m_LuaState) == dstTop);
2148  return false;
2149  }
2150  ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
2151  ASSERT(lua_gettop(m_LuaState) == dstTop + 2);
2152 
2153  // Copy the value:
2154  if (!CopySingleValueFrom(a_SrcLuaState, -1, a_NumAllowedNestingLevels - 1)) // DST: <table> <key> <value>
2155  {
2156  lua_pop(m_LuaState, 2); // DST: empty
2157  lua_pop(a_SrcLuaState, 3); // SRC: empty
2158  ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
2159  ASSERT(lua_gettop(m_LuaState) == dstTop);
2160  return false;
2161  }
2162  ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 3);
2163  ASSERT(lua_gettop(m_LuaState) == dstTop + 3);
2164 
2165  // Set the value and fix up stacks:
2166  lua_rawset(m_LuaState, -3); // DST: <table>
2167  lua_pop(a_SrcLuaState, 1); // SRC: <table> <key>
2168  ASSERT(lua_gettop(a_SrcLuaState) == srcTop + 2);
2169  ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
2170  }
2171  lua_pop(a_SrcLuaState, 1); // SRC: empty
2172  ASSERT(lua_gettop(a_SrcLuaState) == srcTop);
2173  ASSERT(lua_gettop(m_LuaState) == dstTop + 1);
2174  return true;
2175 }
2176 
2177 
2178 
2179 
2180 
2181 bool cLuaState::CopySingleValueFrom(cLuaState & a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels)
2182 {
2183  int t = lua_type(a_SrcLuaState, a_StackIdx);
2184  switch (t)
2185  {
2186  case LUA_TNIL:
2187  {
2188  lua_pushnil(m_LuaState);
2189  return true;
2190  }
2191  case LUA_TSTRING:
2192  {
2193  AString s;
2194  a_SrcLuaState.ToString(a_StackIdx, s);
2195  Push(s);
2196  return true;
2197  }
2198  case LUA_TBOOLEAN:
2199  {
2200  bool b = (tolua_toboolean(a_SrcLuaState, a_StackIdx, false) != 0);
2201  Push(b);
2202  return true;
2203  }
2204  case LUA_TNUMBER:
2205  {
2206  lua_Number d = tolua_tonumber(a_SrcLuaState, a_StackIdx, 0);
2207  Push(d);
2208  return true;
2209  }
2210  case LUA_TUSERDATA:
2211  {
2212  // Get the class name:
2213  const char * type = nullptr;
2214  if (lua_getmetatable(a_SrcLuaState, a_StackIdx) == 0)
2215  {
2216  LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, a_StackIdx);
2217  return false;
2218  }
2219  lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1
2220  type = lua_tostring(a_SrcLuaState, -1);
2221  lua_pop(a_SrcLuaState, 1); // Stack -1
2222 
2223  // Copy the value:
2224  void * ud = tolua_touserdata(a_SrcLuaState, a_StackIdx, nullptr);
2225  tolua_pushusertype(m_LuaState, ud, type);
2226  return true;
2227  }
2228  case LUA_TTABLE:
2229  {
2230  if (!CopyTableFrom(a_SrcLuaState, a_StackIdx, a_NumAllowedNestingLevels - 1))
2231  {
2232  LOGWARNING("%s: Failed to copy table in pos %d.", __FUNCTION__, a_StackIdx);
2233  return false;
2234  }
2235  return true;
2236  }
2237  default:
2238  {
2239  LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools, classes and simple tables!",
2240  __FUNCTION__, lua_typename(a_SrcLuaState, t), a_StackIdx
2241  );
2242  return false;
2243  }
2244  }
2245 }
2246 
2247 
2248 
2249 
2250 
2251 void cLuaState::ToString(int a_StackPos, AString & a_String)
2252 {
2253  size_t len;
2254  const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
2255  if (s != nullptr)
2256  {
2257  a_String.assign(s, len);
2258  }
2259 }
2260 
2261 
2262 
2263 
2264 
2265 void cLuaState::LogStackValues(const char * a_Header)
2266 {
2267  LogStackValues(m_LuaState, a_Header);
2268 }
2269 
2270 
2271 
2272 
2273 
2274 void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header)
2275 {
2276  // Format string consisting only of %s is used to appease the compiler
2277  ASSERT_LUA_STACK_BALANCE(a_LuaState, false);
2278  LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:");
2279  for (int i = lua_gettop(a_LuaState); i > 0; i--)
2280  {
2281  AString Value;
2282  int Type = lua_type(a_LuaState, i);
2283  switch (Type)
2284  {
2285  case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break;
2286  case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
2287  case LUA_TNUMBER: Printf(Value, "%f", static_cast<double>(lua_tonumber(a_LuaState, i))); break;
2288  case LUA_TSTRING:
2289  {
2290  size_t len;
2291  const char * txt = lua_tolstring(a_LuaState, i, &len);
2292  Value.assign(txt, std::min<size_t>(len, 50)); // Only log up to 50 characters of the string
2293  break;
2294  }
2295  case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
2296  case LUA_TFUNCTION: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
2297  case LUA_TUSERDATA:
2298  {
2299  Printf(Value, "%p (%s)", lua_touserdata(a_LuaState, i), tolua_typename(a_LuaState, i));
2300  // tolua_typename pushes the string onto Lua stack, pop it off again:
2301  lua_pop(a_LuaState, 1);
2302  break;
2303  }
2304  default: break;
2305  }
2306  LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
2307  } // for i - stack idx
2308 }
2309 
2310 
2311 
2312 
2313 
2315 {
2317 }
2318 
2319 
2320 
2321 
2322 
2323 void cLuaState::LogApiCallParamFailure(const char * a_FnName, const char * a_ParamNames)
2324 {
2325  LOGWARNING("%s: Cannot read params: %s, bailing out.", a_FnName, a_ParamNames);
2326  LogStackTrace();
2327  LogStackValues("Values on the stack");
2328 }
2329 
2330 
2331 
2332 
2333 
2335 {
2336  a_DeadlockDetect.TrackCriticalSection(m_CS, Printf("cLuaState %s", m_SubsystemName.c_str()));
2337 }
2338 
2339 
2340 
2341 
2342 
2344 {
2345  a_DeadlockDetect.UntrackCriticalSection(m_CS);
2346 }
2347 
2348 
2349 
2350 
2351 
2352 int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
2353 {
2354  LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
2355  LogStackTrace(a_LuaState, 1);
2356  BreakIntoDebugger(a_LuaState);
2357  return 1; // We left the error message on the stack as the return value
2358 }
2359 
2360 
2361 
2362 
2363 
2364 int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
2365 {
2366  ASSERT_LUA_STACK_BALANCE(a_LuaState);
2367  // Call the BreakIntoDebugger function, if available:
2368  lua_getglobal(a_LuaState, "BreakIntoDebugger");
2369  if (!lua_isfunction(a_LuaState, -1))
2370  {
2371  LOGD("LUA: BreakIntoDebugger() not found / not a function");
2372  lua_pop(a_LuaState, 1);
2373  return 1;
2374  }
2375  lua_pushvalue(a_LuaState, -2); // Copy the string that has been passed to us
2376  LOGD("Calling BreakIntoDebugger()...");
2377  lua_call(a_LuaState, 1, 0);
2378  LOGD("Returned from BreakIntoDebugger().");
2379  return 0;
2380 }
2381 
2382 
2383 
2384 
2385 
2387 {
2388  // Get the CanonLuaState global from Lua:
2389  auto canonState = QueryCanonLuaState();
2390  if (canonState == nullptr)
2391  {
2392  LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, static_cast<void *>(m_LuaState));
2393  return;
2394  }
2395 
2396  // Add the callback:
2397  cCSLock Lock(canonState->m_CSTrackedRefs);
2398  canonState->m_TrackedRefs.push_back(&a_Ref);
2399 }
2400 
2401 
2402 
2403 
2404 
2406 {
2407  // Get the CanonLuaState global from Lua:
2408  auto canonState = QueryCanonLuaState();
2409  if (canonState == nullptr)
2410  {
2411  LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, static_cast<void *>(m_LuaState));
2412  return;
2413  }
2414 
2415  // Remove the callback (note that another thread may have cleared the callbacks by closing the LuaState):
2416  cCSLock Lock(canonState->m_CSTrackedRefs);
2417  auto & trackedRefs = canonState->m_TrackedRefs;
2418  for (auto itr = trackedRefs.begin(), end = trackedRefs.end(); itr != end; ++itr)
2419  {
2420  if (*itr == &a_Ref)
2421  {
2422  trackedRefs.erase(itr);
2423  break;
2424  }
2425  }
2426 }
2427 
2428 
2429 
2430 
2431 
2433 // cLuaState::cRef:
2434 
2436  m_LuaState(nullptr),
2437  m_Ref(LUA_REFNIL)
2438 {
2439 }
2440 
2441 
2442 
2443 
2444 
2445 cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
2446  m_LuaState(nullptr),
2447  m_Ref(LUA_REFNIL)
2448 {
2449  RefStack(a_LuaState, a_StackPos);
2450 }
2451 
2452 
2453 
2454 
2455 
2457  m_LuaState(a_FromRef.m_LuaState),
2458  m_Ref(a_FromRef.m_Ref)
2459 {
2460  a_FromRef.m_LuaState = nullptr;
2461  a_FromRef.m_Ref = LUA_REFNIL;
2462 }
2463 
2464 
2465 
2466 
2467 
2469 {
2470  if (m_LuaState != nullptr)
2471  {
2472  UnRef();
2473  }
2474 }
2475 
2476 
2477 
2478 
2479 
2480 void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
2481 {
2482  ASSERT(a_LuaState.IsValid());
2483  if (m_LuaState != nullptr)
2484  {
2485  UnRef();
2486  }
2488  m_LuaState = a_LuaState;
2489  lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
2490  m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX);
2491 }
2492 
2493 
2494 
2495 
2496 
2498 {
2499  if (IsValid())
2500  {
2502  luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
2503  }
2504  m_LuaState = nullptr;
2505  m_Ref = LUA_REFNIL;
2506 }
2507 
2508 
2509 
2510 
cTrackedRef(void)
Creates an unbound ref instance.
Definition: LuaState.cpp:197
void LogApiCallParamFailure(const char *a_FnName, const char *a_ParamNames)
Outputs to log a warning about API call being unable to read its parameters from the stack...
Definition: LuaState.cpp:2323
bool IsValid(void)
Returns true if the contained reference is valid.
Definition: LuaState.cpp:256
std::atomic< cCriticalSection * > m_CS
The mutex protecting m_Ref against multithreaded access.
Definition: LuaState.h:252
bool CheckParamString(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are strings; also logs warning if not...
Definition: LuaState.cpp:1717
A dummy class that&#39;s used only to push a constant nil as a function parameter in Call().
Definition: LuaState.h:450
std::unique_ptr< cTableRef > cTableRefPtr
Definition: LuaState.h:420
int luaopen_lsqlite3(lua_State *L)
bool RefStack(cLuaState &a_LuaState, int a_StackPos)
Set the contained callback to the function in the specified Lua state&#39;s stack position.
Definition: LuaState.cpp:340
bool IsSameLuaState(cLuaState &a_LuaState)
Returns true if the reference resides in the specified Lua state.
Definition: LuaState.cpp:271
int m_StackPos
The stack index where the table resides in the Lua state.
Definition: LuaState.h:541
bool CallFunction(int a_NumReturnValues)
Pushes a usertype of the specified class type onto the stack.
Definition: LuaState.cpp:1511
Represents a table on the Lua stack.
Definition: LuaState.h:516
bool CheckParamUserTable(int a_StartParam, const char *a_UserTable, int a_EndParam=-1)
Returns true if the specified parameters on the stack are of the specified usertable type; also logs ...
Definition: LuaState.cpp:1549
eEntityType GetEntityType(void) const
Definition: Entity.h:168
void ForEachArrayElement(cFunctionRef< bool(cLuaState &a_LuaState, int a_Index)> a_ElementCallback) const
Iterates over all array elements in the table consecutively and calls the a_ElementCallback for each...
Definition: LuaState.cpp:389
eWeather
Definition: Defines.h:151
std::shared_ptr< cTrackedRef > cTrackedRefSharedPtr
Definition: LuaState.h:275
static AString GetStats(void)
Returns the statistics for all the registered LuaStates.
Definition: LuaState.cpp:157
bool IsLockedByCurrentThread(void)
Returns true if the CS is currently locked by the thread calling this function.
void RefStack(cLuaState &a_LuaState, int a_StackPos)
Creates a reference to Lua object at the specified stack pos, binds this object to it...
Definition: LuaState.cpp:2480
void Invalidate(void)
Invalidates the callback, without untracking it from the cLuaState.
Definition: LuaState.cpp:295
#define VERIFY(x)
Definition: Globals.h:339
Same thing as cCallback, but GetStackValue() won&#39;t fail if the callback value is nil.
Definition: LuaState.h:334
bool RefStack(cLuaState &a_LuaState, int a_StackPos)
Set the contained reference to the object at the specified Lua state&#39;s stack position.
Definition: LuaState.cpp:206
std::vector< cTrackedRef * > m_TrackedRefs
The tracked references.
Definition: LuaState.h:923
bool CheckParamStaticSelf(const char *a_SelfClassName)
Returns true if the first parameter is the expected class (static).
Definition: LuaState.cpp:1905
bool CopySingleValueFrom(cLuaState &a_SrcLuaState, int a_StackIdx, int a_NumAllowedNestingLevels)
Copies a single value from the specified stack index of the source Lua state to the top of this Lua s...
Definition: LuaState.cpp:2181
friend class cLuaState
Definition: LuaState.h:247
Represents a stored Lua table with callback functions that C++ code can call.
Definition: LuaState.h:369
bool FromString(const AString &a_StringUUID)
Tries to interpret the string as a short or long form UUID and assign from it.
Definition: UUID.cpp:102
AString m_SubsystemName
The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScrip...
Definition: LuaState.h:912
#define LOGWARN
Definition: LoggerSimple.h:43
int luaopen_lxp(lua_State *L)
static void Bind(cLuaState &a_LuaState)
Registers the Json library in the specified Lua state.
Definition: LuaJson.cpp:385
bool CheckParamNumber(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are numbers; also logs warning if not...
Definition: LuaState.cpp:1651
Represents a reference to a Lua object that has a tracked lifetime -.
Definition: LuaState.h:217
#define lua_tostring(L, i)
Definition: LuaState.cpp:31
bool LoadString(const AString &a_StringToLoad, const AString &a_FileName, bool a_LogWarnings=true)
Loads the specified string.
Definition: LuaState.cpp:680
bool CheckParamSelf(const char *a_SelfClassName)
Returns true if the first parameter is an instance of the expected class name.
Definition: LuaState.cpp:1880
bool CheckParamBool(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are bools; also logs warning if not...
Definition: LuaState.cpp:1684
Represents a stored callback to Lua that C++ code can call.
Definition: LuaState.h:285
cStackValue WalkToNamedGlobal(const AString &a_Name)
Pushes the named value in the global table to the top of the stack.
Definition: LuaState.cpp:1479
void LogStackTrace(int a_StartingDepth=0)
Logs all items in the current stack trace to the server console.
Definition: LuaState.cpp:1980
AString m_CurrentFunctionName
Name of the currently pushed function (for the Push / Call chain)
Definition: LuaState.h:915
cLuaState(const AString &a_SubsystemName)
Creates a new instance.
Definition: LuaState.cpp:444
bool GetStackValue(int a_StackPos, AString &a_Value)
Definition: LuaState.cpp:1105
bool PushFunction(const char *a_FunctionName)
Pushes the function of the specified name onto the stack.
Definition: LuaState.cpp:735
void UnRef(void)
Removes the bound reference, resets the object to Unbound state.
Definition: LuaState.cpp:2497
static void Remove(cLuaState &a_LuaState)
Removes the bindings between the specified canon state and its lua_State pointer. ...
Definition: LuaState.cpp:94
Encapsulates a Lua state and provides some syntactic sugar for common operations. ...
Definition: LuaState.h:57
static const cNil Nil
Definition: LuaState.h:453
bool CheckParamFunctionOrNil(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are functions or nils; also logs warning if not...
Definition: LuaState.cpp:1786
lua_State * m_LuaState
Definition: LuaState.h:202
bool m_IsOwned
If true, the state is owned by this object and will be auto-Closed.
Definition: LuaState.h:908
bool CheckParamTable(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are tables; also logs warning if not...
Definition: LuaState.cpp:1615
Definition: UUID.h:10
bool CheckParamUserType(int a_StartParam, const char *a_UserType, int a_EndParam=-1)
Returns true if the specified parameters on the stack are of the specified usertype; also logs warnin...
Definition: LuaState.cpp:1582
bool CopyTableFrom(cLuaState &a_SrcLuaState, int a_TableIdx, int a_NumAllowedNestingLevels)
Copies a table at the specified stack index of the source Lua state to the top of this Lua state&#39;s st...
Definition: LuaState.cpp:2126
void TrackCriticalSection(cCriticalSection &a_CS, const AString &a_Name)
Adds the critical section for tracking.
std::vector< AString > AStringVector
Definition: StringUtils.h:14
Tracks the canon cLuaState instances for each lua_State pointer.
Definition: LuaState.cpp:66
cLuaState * QueryCanonLuaState(void) const
Returns the canon Lua state (the primary cLuaState instance that was used to create, rather than attach, the lua_State structure).
Definition: LuaState.cpp:2314
#define ASSERT_LUA_STACK_BALANCE(...)
Definition: LuaState.h:106
bool CheckParamEnd(int a_Param)
Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) ...
Definition: LuaState.cpp:1860
void Pop(int a_NumValuesToPop=1)
Pops the specified number of values off the top of the Lua stack.
Definition: LuaState.cpp:1094
static cLuaStateTracker & Get(void)
Returns the single instance of this class.
Definition: LuaState.cpp:184
cStackValue WalkToValue(const AString &a_Name)
Pushes the named value in the table at the top of the stack.
Definition: LuaState.cpp:1444
std::unique_ptr< cOptionalCallback > cOptionalCallbackPtr
Definition: LuaState.h:358
bool IsValid(void) const
Returns true if the reference is valid.
Definition: LuaState.h:184
Keeps track of all create cLuaState instances.
Definition: LuaState.h:1037
void UntrackCriticalSection(cCriticalSection &a_CS)
Removes the CS from the tracking.
void UntrackRef(cTrackedRef &a_Ref)
Removes the specified reference from tracking.
Definition: LuaState.cpp:2405
Vector3< double > Vector3d
Definition: Vector3.h:445
int m_NumCurrentFunctionArgs
Number of arguments currently pushed (for the Push / Call chain)
Definition: LuaState.h:918
bool CheckParamFunction(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are functions; also logs warning if not...
Definition: LuaState.cpp:1753
static int BreakIntoDebugger(lua_State *a_LuaState)
Tries to break into the MobDebug debugger, if it is installed.
Definition: LuaState.cpp:2364
std::unique_ptr< cCallback > cCallbackPtr
Definition: LuaState.h:328
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:165
AString & Printf(AString &str, const char *format, fmt::ArgList args)
Output the formatted text into the string.
Definition: StringUtils.cpp:55
void Clear(void)
Frees the contained reference, if any.
Definition: LuaState.cpp:225
std::unique_ptr< cStackTable > cStackTablePtr
Definition: LuaState.h:543
cCriticalSection m_CS
The mutex protecting m_CanonStates against multithreaded access.
Definition: LuaState.cpp:105
cStackTable(cLuaState &a_LuaState, int a_StackPos)
Definition: LuaState.cpp:378
void ForEachElement(cFunctionRef< bool(cLuaState &a_LuaState)> a_ElementCallback) const
Iterates over all dictionary elements in the table in random order, and calls the a_ElementCallback f...
Definition: LuaState.cpp:414
std::map< lua_State *, cLuaState * > m_CanonStates
Map of lua_State pointers to their canon cLuaState instances.
Definition: LuaState.cpp:108
void Detach(void)
Detaches a previously attached state.
Definition: LuaState.cpp:596
#define ASSERT(x)
Definition: Globals.h:335
std::map< AString, AString > AStringMap
A string dictionary, used for key-value pairs.
Definition: StringUtils.h:18
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
static cLuaState * GetCanonState(lua_State *a_LuaState)
Returns the canon Lua state for the specified lua_State pointer.
Definition: LuaState.cpp:70
#define LOGD(...)
Definition: LoggerSimple.h:40
void Attach(lua_State *a_State)
Attaches the specified state.
Definition: LuaState.cpp:574
Used for storing references to object in the global registry.
Definition: LuaState.h:161
cLuaState & m_LuaState
The Lua state in which the table resides.
Definition: LuaState.h:538
static void Del(cLuaState &a_LuaState)
Deletes the specified Lua state from the internal list of LuaStates.
Definition: LuaState.cpp:137
int CallFunctionWithForeignParams(const AString &a_FunctionName, cLuaState &a_SrcLuaState, int a_SrcParamStart, int a_SrcParamEnd)
Calls the function specified by its name, with arguments copied off the foreign state.
Definition: LuaState.cpp:2040
void TrackInDeadlockDetect(cDeadlockDetect &a_DeadlockDetect)
Adds this object&#39;s CS to the DeadlockDetect&#39;s tracked CSs.
Definition: LuaState.cpp:2334
void LogStackValues(const char *a_Header=nullptr)
Logs all the elements&#39; types on the API stack, with an optional header for the listing.
Definition: LuaState.cpp:2265
static const cRet Return
Definition: LuaState.h:446
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:351
static int ReportFnCallErrors(lua_State *a_LuaState)
Used as the error reporting function for function calls.
Definition: LuaState.cpp:2352
A RAII class for values pushed onto the Lua stack.
Definition: LuaState.h:458
void Close(void)
Closes the m_LuaState, if not closed already.
Definition: LuaState.cpp:536
std::string AString
Definition: StringUtils.h:13
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc...
Definition: Defines.h:29
cCriticalSection m_CSTrackedRefs
Protects m_TrackedRefs against multithreaded access.
Definition: LuaState.h:926
cRef(void)
Creates an unbound reference object.
Definition: LuaState.cpp:2435
void AddPackagePath(const AString &a_PathVariable, const AString &a_Path)
Adds the specified path to package.
Definition: LuaState.cpp:618
bool IsParamNumber(int a_Param)
Definition: LuaState.cpp:1942
static cCanonLuaStates & GetInstance(void)
Returns the singleton instance of this class.
Definition: LuaState.cpp:112
lua_State * GetLuaState(void)
Returns the Lua state associated with the value.
Definition: LuaState.h:190
void Create(void)
Creates the m_LuaState, if not created already.
Definition: LuaState.cpp:487
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:156
bool HasFunction(const char *a_FunctionName)
Returns true if a_FunctionName is a valid Lua function that can be called.
Definition: LuaState.cpp:716
bool LoadFile(const AString &a_FileName, bool a_LogWarnings=true)
Loads the specified file Returns false and optionally logs a warning to the console if not successful...
Definition: LuaState.cpp:644
Definition: Entity.h:73
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
Definition: StringUtils.cpp:76
bool IsParamUserType(int a_Param, AString a_UserType)
Definition: LuaState.cpp:1930
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void RegisterAPILibs(void)
Registers all the API libraries that MCS provides into m_LuaState.
Definition: LuaState.cpp:505
unsigned int UInt32
Definition: Globals.h:113
bool IsValid(void) const
Returns true if the m_LuaState is valid.
Definition: LuaState.h:581
Vector3< int > Vector3i
Definition: Vector3.h:447
cCriticalSection m_CS
Definition: LuaState.h:903
bool ReportErrors(int status)
If the status is nonzero, prints the text on the top of Lua stack and returns true.
Definition: LuaState.cpp:1954
std::unique_ptr< cTrackedRef > cTrackedRefPtr
Definition: LuaState.h:274
bool GetStackValues(int a_StartStackPos, Arg1 &&a_Arg1, Args &&...args)
Retrieves a list of values from the Lua stack, starting at the specified index.
Definition: LuaState.h:759
int CopyStackFrom(cLuaState &a_SrcLuaState, int a_SrcStart, int a_SrcEnd, int a_NumAllowedNestingLevels=16)
Copies objects on the stack from the specified state.
Definition: LuaState.cpp:2103
bool RefStack(cLuaState &a_LuaState, int a_StackPos)
Set the contained reference to the table in the specified Lua state&#39;s stack position.
Definition: LuaState.cpp:360
static void Add(cLuaState &a_LuaState)
Adds a new canon cLuaState instance to the map.
Definition: LuaState.cpp:84
lua_State * m_LuaState
Definition: LuaState.h:905
virtual const char * GetClass(void) const
Returns the topmost class name for the object.
Definition: Entity.cpp:113
void Push(Arg1 &&a_Arg1, Arg2 &&a_Arg2, Args &&...a_Args)
Pushes multiple arguments onto the Lua stack.
Definition: LuaState.h:605
A dummy class that&#39;s used only to delimit function args from return values for cLuaState::Call() ...
Definition: LuaState.h:443
std::shared_ptr< cCallback > cCallbackSharedPtr
Definition: LuaState.h:329
void TrackRef(cTrackedRef &a_Ref)
Adds the specified reference to tracking.
Definition: LuaState.cpp:2386
bool CheckParamUUID(int a_StartParam, int a_EndParam=-1)
Returns true if the specified parameters on the stack are UUIDs; also logs warning if not Accepts eit...
Definition: LuaState.cpp:1819
cRef m_Ref
Reference to the Lua callback.
Definition: LuaState.h:255
Definition: Item.h:36
#define UNREACHABLE(x)
Use to mark code that should be impossible to reach.
Definition: Globals.h:344
void UntrackInDeadlockDetect(cDeadlockDetect &a_DeadlockDetect)
Removes this object&#39;s CS from the DeadlockDetect&#39;s tracked CSs.
Definition: LuaState.cpp:2343
AString GetTypeText(int a_StackPos)
Returns the type of the item on the specified position in the stack.
Definition: LuaState.cpp:2031
static void Add(cLuaState &a_LuaState)
Adds the specified Lua state to the internal list of LuaStates.
Definition: LuaState.cpp:126
bool RefStack(cLuaState &a_LuaState, int a_StackPos)
Set the contained callback to the function in the specified Lua state&#39;s stack position.
Definition: LuaState.cpp:322
int ApiParamError(fmt::StringRef a_Msg)
Prints the message, prefixed with the current function name, then logs the stack contents and raises ...
Definition: LuaState.cpp:2007
void ToString(int a_StackPos, AString &a_String)
Reads the value at the specified stack position as a string and sets it to a_String.
Definition: LuaState.cpp:2251