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