1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include "LuaHandle.h"
4
5 #include "LuaGaia.h"
6 #include "LuaRules.h"
7 #include "LuaUI.h"
8
9 #include "LuaCallInCheck.h"
10 #include "LuaEventBatch.h"
11 #include "LuaHashString.h"
12 #include "LuaOpenGL.h"
13 #include "LuaBitOps.h"
14 #include "LuaMathExtra.h"
15 #include "LuaUtils.h"
16 #include "LuaZip.h"
17 #include "Game/GlobalUnsynced.h"
18 #include "Game/Players/Player.h"
19 #include "Game/Players/PlayerHandler.h"
20 #include "Net/Protocol/BaseNetProtocol.h"
21 #include "Game/UI/KeyCodes.h"
22 #include "Game/UI/KeySet.h"
23 #include "Game/UI/KeyBindings.h"
24 #include "Game/UI/MiniMap.h"
25 #include "Rendering/GlobalRendering.h"
26 #include "Sim/Misc/GlobalSynced.h"
27 #include "Sim/Misc/TeamHandler.h"
28 #include "Sim/Projectiles/Projectile.h"
29 #include "Sim/Projectiles/WeaponProjectiles/WeaponProjectile.h"
30 #include "Sim/Features/FeatureDef.h"
31 #include "Sim/Units/Unit.h"
32 #include "Sim/Units/UnitDef.h"
33 #include "Sim/Weapons/Weapon.h"
34 #include "Sim/Weapons/WeaponDef.h"
35 #include "System/Config/ConfigHandler.h"
36 #include "System/EventHandler.h"
37 #include "System/Exceptions.h"
38 #include "System/GlobalConfig.h"
39 #include "System/Rectangle.h"
40 #include "System/ScopedFPUSettings.h"
41 #include "System/Log/ILog.h"
42 #include "System/Input/KeyInput.h"
43 #include "System/FileSystem/FileHandler.h"
44 #include "System/Platform/SDL1_keysym.h"
45
46 #include "LuaInclude.h"
47
48 #include <SDL_keyboard.h>
49 #include <SDL_keycode.h>
50 #include <SDL_mouse.h>
51
52
53 #include <string>
54
55 bool CLuaHandle::devMode = false;
56 bool CLuaHandle::modUICtrl = true;
57
58
59 /******************************************************************************/
60 /******************************************************************************/
61
PushTracebackFuncToRegistry(lua_State * L)62 void CLuaHandle::PushTracebackFuncToRegistry(lua_State* L)
63 {
64 LUA_OPEN_LIB(L, luaopen_debug);
65 HSTR_PUSH(L, "traceback");
66 LuaUtils::PushDebugTraceback(L);
67 lua_rawset(L, LUA_REGISTRYINDEX);
68 // We only need the debug.traceback function, the others are unsafe for syncing.
69 // Later CLuaHandle implementations decide themselves if they want to reload the lib or not (LuaUI does).
70 LUA_UNLOAD_LIB(L, LUA_DBLIBNAME);
71 }
72
73
74
handlepanic(lua_State * L)75 static int handlepanic(lua_State *L)
76 {
77 std::string err = luaL_optsstring(L, 1, "lua paniced");
78 throw content_error(err);
79 }
80
81
82
CLuaHandle(const string & _name,int _order,bool _userMode,bool _synced)83 CLuaHandle::CLuaHandle(const string& _name, int _order, bool _userMode, bool _synced)
84 : CEventClient(_name, _order, _synced)
85 , userMode (_userMode)
86 , killMe (false)
87 , callinErrors(0)
88 {
89 D.owner = this;
90 D.synced = _synced;
91 L = LUA_OPEN(&D);
92
93 L_GC = lua_newthread(L);
94 luaL_ref(L,LUA_REGISTRYINDEX);
95
96 // needed for engine traceback
97 PushTracebackFuncToRegistry(L);
98
99 // prevent lua from calling c's exit()
100 lua_atpanic(L, handlepanic);
101 }
102
103
~CLuaHandle()104 CLuaHandle::~CLuaHandle()
105 {
106 eventHandler.RemoveClient(this);
107
108 // KillLua() must be called before dtor!!!
109 assert(!IsValid());
110 }
111
112
KillLua()113 void CLuaHandle::KillLua()
114 {
115 if (IsValid()) {
116 // 1. unlink from eventHandler, so no new events are getting triggered
117 eventHandler.RemoveClient(this);
118 //FIXME when multithreaded lua is enabled, wait for all running events to finish (possible via a mutex?)
119
120 // 2. shutdown
121 Shutdown();
122
123 // 3. delete the lua_State
124 SetHandleRunning(L, true);
125 LUA_CLOSE(L);
126 //SetHandleRunning(L, false); --nope, the state is deleted
127 L = NULL;
128 }
129 }
130
131
132 /******************************************************************************/
133 /******************************************************************************/
134
135
KillActiveHandle(lua_State * L)136 int CLuaHandle::KillActiveHandle(lua_State* L)
137 {
138 CLuaHandle* ah = GetHandle(L);
139 if (ah != NULL) {
140 const int args = lua_gettop(L);
141 if ((args >= 1) && lua_isstring(L, 1)) {
142 ah->killMsg = lua_tostring(L, 1);
143 }
144
145 // get rid of us next GameFrame call
146 ah->killMe = true;
147
148 // don't process any further events
149 eventHandler.RemoveClient(ah);
150 }
151 return 0;
152 }
153
154
155 /******************************************************************************/
156
AddEntriesToTable(lua_State * L,const char * name,bool (* entriesFunc)(lua_State *))157 bool CLuaHandle::AddEntriesToTable(lua_State* L, const char* name,
158 bool (*entriesFunc)(lua_State*))
159 {
160 const int top = lua_gettop(L);
161 lua_pushstring(L, name);
162 lua_rawget(L, -2);
163 if (lua_istable(L, -1)) {
164 bool success = entriesFunc(L);
165 lua_settop(L, top);
166 return success;
167 }
168
169 // make a new table
170 lua_pop(L, 1);
171 lua_pushstring(L, name);
172 lua_newtable(L);
173 if (!entriesFunc(L)) {
174 lua_settop(L, top);
175 return false;
176 }
177 lua_rawset(L, -3);
178
179 lua_settop(L, top);
180 return true;
181 }
182
183
184 /******************************************************************************/
185 /******************************************************************************/
186
CheckStack()187 void CLuaHandle::CheckStack()
188 {
189 if (!IsValid())
190 return;
191
192 const int top = lua_gettop(L);
193 if (top != 0) {
194 LOG_L(L_WARNING, "%s stack check: top = %i", GetName().c_str(), top);
195 lua_settop(L, 0);
196 }
197 }
198
199
XCall(lua_State * srcState,const string & funcName)200 int CLuaHandle::XCall(lua_State* srcState, const string& funcName)
201 {
202 const int top = lua_gettop(L);
203
204 // push the function
205 const LuaHashString funcHash(funcName);
206 if (!funcHash.GetGlobalFunc(L)) {
207 LOG_L(L_WARNING, "Tried to call non-linked Script.%s.%s()", GetName().c_str(), funcName.c_str());
208 return 0;
209 }
210
211 int retCount;
212
213 if (srcState == L) {
214 lua_insert(L, 1); // move the function to the beginning
215
216 // call the function
217 if (!RunCallIn(L, funcHash, top, LUA_MULTRET)) {
218 return 0;
219 }
220 retCount = lua_gettop(L);
221 } else {
222 const int srcCount = lua_gettop(srcState);
223
224 LuaUtils::CopyData(L, srcState, srcCount);
225
226 const bool origDrawingState = LuaOpenGL::IsDrawingEnabled(L);
227 LuaOpenGL::SetDrawingEnabled(L, LuaOpenGL::IsDrawingEnabled(srcState));
228
229 // call the function
230 const bool failed = !RunCallIn(L, funcHash, srcCount, LUA_MULTRET);
231
232 LuaOpenGL::SetDrawingEnabled(L, origDrawingState);
233
234 if (failed)
235 return 0;
236
237 retCount = lua_gettop(L) - top;
238
239 lua_settop(srcState, 0); // pop all passed arguments on caller stack
240 if (retCount > 0) {
241 LuaUtils::CopyData(srcState, L, retCount); // push the new returned arguments on caller stack
242 }
243 lua_settop(L, top); // revert the callee stack
244 }
245
246 return retCount;
247 }
248
249
250 /******************************************************************************/
251 /******************************************************************************/
252
RunCallInTraceback(lua_State * L,const LuaHashString * hs,int inArgs,int outArgs,int errFuncIndex,std::string & tracebackMsg,bool popErrorFunc)253 int CLuaHandle::RunCallInTraceback(
254 lua_State* L,
255 const LuaHashString* hs,
256 int inArgs,
257 int outArgs,
258 int errFuncIndex,
259 std::string& tracebackMsg,
260 bool popErrorFunc
261 ) {
262 // do not signal floating point exceptions in user Lua code
263 ScopedDisableFpuExceptions fe;
264
265 struct ScopedLuaCall {
266 public:
267 ScopedLuaCall(
268 CLuaHandle* handle,
269 lua_State* state,
270 const LuaHashString* func,
271 int _nInArgs,
272 int _nOutArgs,
273 int _errFuncIdx,
274 bool _popErrFunc
275 )
276 : luaState(state)
277 , luaHandle(handle)
278
279 , nInArgs(_nInArgs)
280 , nOutArgs(_nOutArgs)
281 , errFuncIdx(_errFuncIdx)
282 , popErrFunc(_popErrFunc)
283 {
284 handle->SetHandleRunning(state, true);
285
286 GLMatrixStateTracker& matTracker = GetLuaContextData(state)->glMatrixTracker;
287 MatrixStateData prevMatState = matTracker.PushMatrixState();
288 LuaOpenGL::InitMatrixState(state, func);
289
290 top = lua_gettop(state);
291 // note1: disable GC outside of this scope to prevent sync errors and similar
292 // note2: we collect garbage now in its own callin "CollectGarbage"
293 // lua_gc(L, LUA_GCRESTART, 0);
294 error = lua_pcall(state, nInArgs, nOutArgs, errFuncIdx);
295 // only run GC inside of "SetHandleRunning(L, true) ... SetHandleRunning(L, false)"!
296 lua_gc(state, LUA_GCSTOP, 0);
297
298 LuaOpenGL::CheckMatrixState(state, func, error);
299 matTracker.PopMatrixState(prevMatState);
300
301 handle->SetHandleRunning(state, false);
302 }
303
304 ~ScopedLuaCall() {
305 assert(!popErrFunc); // deprecated!
306 if (popErrFunc) {
307 lua_remove(luaState, errFuncIdx);
308 }
309 }
310
311 void CheckFixStack(std::string& trace) {
312 // note: assumes error-handler has not been popped yet (!)
313 const int outArgs = (lua_gettop(luaState) - (GetTop() - 1)) + nInArgs;
314
315 if (GetError() == 0) {
316 if (nOutArgs != LUA_MULTRET) {
317 if (outArgs != nOutArgs) {
318 LOG_L(L_ERROR, "Internal Lua error: %d return values, %d expected", outArgs, nOutArgs);
319 if (outArgs > nOutArgs)
320 lua_pop(luaState, outArgs - nOutArgs);
321 }
322 } else {
323 if (outArgs < 0) {
324 LOG_L(L_ERROR, "Internal Lua error: stack corrupted");
325 }
326 }
327 } else {
328 const int dbgOutArgs = 1; // the traceback string
329
330 if (outArgs > dbgOutArgs) {
331 LOG_L(L_ERROR, "Internal Lua error: %i too many elements on the stack", outArgs - dbgOutArgs);
332 lua_pop(luaState, outArgs - dbgOutArgs); // only leave traceback str on the stack
333 } else if (outArgs < dbgOutArgs) {
334 LOG_L(L_ERROR, "Internal Lua error: stack corrupted");
335 lua_pushnil(luaState); // to make the code below valid
336 }
337
338 trace += "[Internal Lua error: Call failure] ";
339 trace += luaL_optstring(luaState, -1, "[No traceback returned]");
340 lua_pop(luaState, 1); // pop traceback string
341
342 // log only errors that lead to a crash
343 luaHandle->callinErrors += (GetError() == LUA_ERRRUN);
344 }
345 }
346
347 int GetTop() const { return top; }
348 int GetError() const { return error; }
349
350 private:
351 lua_State* luaState;
352 CLuaHandle* luaHandle;
353
354 int nInArgs;
355 int nOutArgs;
356 int errFuncIdx;
357 bool popErrFunc;
358
359 int top;
360 int error;
361 };
362
363 // TODO: use closure so we do not need to copy args
364 ScopedLuaCall call(this, L, hs, inArgs, outArgs, errFuncIndex, popErrorFunc);
365 call.CheckFixStack(tracebackMsg);
366
367 return (call.GetError());
368 }
369
370
RunCallInTraceback(lua_State * L,const LuaHashString & hs,int inArgs,int outArgs,int errFuncIndex,bool popErrFunc)371 bool CLuaHandle::RunCallInTraceback(lua_State* L, const LuaHashString& hs, int inArgs, int outArgs, int errFuncIndex, bool popErrFunc)
372 {
373 std::string traceback;
374 const int error = RunCallInTraceback(L, &hs, inArgs, outArgs, errFuncIndex, traceback, popErrFunc);
375
376 if (error != 0) {
377 LOG_L(L_ERROR, "%s::RunCallIn: error = %i, %s, %s", GetName().c_str(),
378 error, hs.GetString().c_str(), traceback.c_str());
379
380 if (error == LUA_ERRMEM) {
381 // try to free some memory so other lua states can alloc again
382 for (int i=0; i<20; ++i) {
383 CollectGarbage();
384 }
385
386 // Kill
387 KillActiveHandle(L);
388 }
389 return false;
390 }
391 return true;
392 }
393
394 /******************************************************************************/
395 /******************************************************************************/
396
LoadCode(lua_State * L,const string & code,const string & debug)397 bool CLuaHandle::LoadCode(lua_State *L, const string& code, const string& debug)
398 {
399 lua_settop(L, 0);
400
401 const LuaUtils::ScopedDebugTraceBack traceBack(L);
402
403 const int loadError = luaL_loadbuffer(L, code.c_str(), code.size(), debug.c_str());
404 bool ret = true;
405
406 if (loadError == 0) {
407 static const LuaHashString cmdStr("Initialize");
408
409 // call the routine
410 ret = RunCallInTraceback(L, cmdStr, 0, 0, traceBack.GetErrFuncIdx(), false);
411 } else {
412 LOG_L(L_ERROR, "Lua LoadCode loadbuffer error = %i, %s, %s", loadError, debug.c_str(), lua_tostring(L, -1));
413 lua_pop(L, 1);
414 ret = false;
415 }
416
417 return ret;
418 }
419
420 /******************************************************************************/
421 /******************************************************************************/
422
Shutdown()423 void CLuaHandle::Shutdown()
424 {
425 LUA_CALL_IN_CHECK(L);
426 luaL_checkstack(L, 3, __FUNCTION__);
427
428 const LuaUtils::ScopedDebugTraceBack traceBack(L);
429
430 static const LuaHashString cmdStr("Shutdown");
431 if (!cmdStr.GetGlobalFunc(L)) {
432 return; // the call is not defined
433 }
434
435 // call the routine
436 RunCallInTraceback(L, cmdStr, 0, 0, traceBack.GetErrFuncIdx(), false);
437 }
438
439
GotChatMsg(const string & msg,int playerID)440 bool CLuaHandle::GotChatMsg(const string& msg, int playerID)
441 {
442 LUA_CALL_IN_CHECK(L, true);
443 luaL_checkstack(L, 4, __FUNCTION__);
444 static const LuaHashString cmdStr("GotChatMsg");
445
446 bool processed = false;
447 if (cmdStr.GetGlobalFunc(L)) {
448 lua_pushsstring(L, msg);
449 lua_pushnumber(L, playerID);
450
451 // call the routine
452 if (RunCallIn(L, cmdStr, 2, 1)) {
453 processed = luaL_optboolean(L, -1, false);
454 lua_pop(L, 1);
455 }
456 }
457
458 if (!processed && (this == luaUI)) {
459 processed = luaUI->ConfigCommand(msg); //FIXME deprecated
460 }
461 return processed;
462 }
463
464
Load(IArchive * archive)465 void CLuaHandle::Load(IArchive* archive)
466 {
467 LUA_CALL_IN_CHECK(L);
468 luaL_checkstack(L, 4, __FUNCTION__);
469
470 const LuaUtils::ScopedDebugTraceBack traceBack(L);
471
472 static const LuaHashString cmdStr("Load");
473 if (!cmdStr.GetGlobalFunc(L)) {
474 return; // the call is not defined
475 }
476
477 // Load gets ZipFileReader userdatum as single argument
478 LuaZipFileReader::PushNew(L, "", archive);
479
480 // call the routine
481 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
482 }
483
484
HasCallIn(lua_State * L,const string & name)485 bool CLuaHandle::HasCallIn(lua_State* L, const string& name)
486 {
487 if (!IsValid())
488 return false;
489
490 if (name == "CollectGarbage")
491 return true;
492
493 //FIXME should be equal to below, but somehow it isn't and doesn't work as expected!?
494 // lua_getglobal(L, name.c_str());
495 // const bool found = !lua_isfunction(L, -1);
496 // lua_pop(L, 1);
497
498 lua_pushvalue(L, LUA_GLOBALSINDEX);
499 lua_pushsstring(L, name); // push the function name
500 lua_rawget(L, -2); // get the function
501 const bool found = lua_isfunction(L, -1);
502 lua_pop(L, 2);
503
504 return found;
505 }
506
507
UpdateCallIn(lua_State * L,const string & name)508 bool CLuaHandle::UpdateCallIn(lua_State* L, const string& name)
509 {
510 if (HasCallIn(L, name)) {
511 eventHandler.InsertEvent(this, name);
512 } else {
513 eventHandler.RemoveEvent(this, name);
514 }
515 return true;
516 }
517
518
GamePreload()519 void CLuaHandle::GamePreload()
520 {
521 LUA_CALL_IN_CHECK(L);
522 luaL_checkstack(L, 3, __FUNCTION__);
523
524 const LuaUtils::ScopedDebugTraceBack traceBack(L);
525
526 static const LuaHashString cmdStr("GamePreload");
527 if (!cmdStr.GetGlobalFunc(L)) {
528 return; // the call is not defined
529 }
530
531 // call the routine
532 RunCallInTraceback(L, cmdStr, 0, 0, traceBack.GetErrFuncIdx(), false);
533 }
534
GameStart()535 void CLuaHandle::GameStart()
536 {
537 LUA_CALL_IN_CHECK(L);
538 luaL_checkstack(L, 3, __FUNCTION__);
539
540 const LuaUtils::ScopedDebugTraceBack traceBack(L);
541
542 static const LuaHashString cmdStr("GameStart");
543 if (!cmdStr.GetGlobalFunc(L)) {
544 return; // the call is not defined
545 }
546
547 // call the routine
548 RunCallInTraceback(L, cmdStr, 0, 0, traceBack.GetErrFuncIdx(), false);
549 }
550
GameOver(const std::vector<unsigned char> & winningAllyTeams)551 void CLuaHandle::GameOver(const std::vector<unsigned char>& winningAllyTeams)
552 {
553 LUA_CALL_IN_CHECK(L);
554 luaL_checkstack(L, 2, __FUNCTION__);
555
556 const LuaUtils::ScopedDebugTraceBack traceBack(L);
557
558 static const LuaHashString cmdStr("GameOver");
559 if (!cmdStr.GetGlobalFunc(L)) {
560 return; // the call is not defined
561 }
562
563 lua_createtable(L, winningAllyTeams.size(), 0);
564 for (unsigned int i = 0; i < winningAllyTeams.size(); i++) {
565 lua_pushnumber(L, winningAllyTeams[i]);
566 lua_rawseti(L, -2, i + 1);
567 }
568
569 // call the routine
570 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
571 }
572
573
GamePaused(int playerID,bool paused)574 void CLuaHandle::GamePaused(int playerID, bool paused)
575 {
576 LUA_CALL_IN_CHECK(L);
577 luaL_checkstack(L, 5, __FUNCTION__);
578
579 const LuaUtils::ScopedDebugTraceBack traceBack(L);
580
581 static const LuaHashString cmdStr("GamePaused");
582 if (!cmdStr.GetGlobalFunc(L)) {
583 return; // the call is not defined
584 }
585
586 lua_pushnumber(L, playerID);
587 lua_pushboolean(L, paused);
588
589 // call the routine
590 RunCallInTraceback(L, cmdStr, 2, 0, traceBack.GetErrFuncIdx(), false);
591 }
592
593
GameFrame(int frameNum)594 void CLuaHandle::GameFrame(int frameNum)
595 {
596 if (killMe) {
597 string msg = GetName();
598 if (!killMsg.empty()) {
599 msg += ": " + killMsg;
600 }
601 LOG("[%s] disabled %s", __FUNCTION__, msg.c_str());
602 delete this;
603 return;
604 }
605
606 LUA_CALL_IN_CHECK(L);
607 luaL_checkstack(L, 4, __FUNCTION__);
608
609 const LuaUtils::ScopedDebugTraceBack traceBack(L);
610
611 static const LuaHashString cmdStr("GameFrame");
612 if (!cmdStr.GetGlobalFunc(L)) {
613 return; // the call is not defined
614 }
615
616 lua_pushnumber(L, frameNum);
617
618 // call the routine
619 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
620 }
621
622
GameID(const unsigned char * gameID,unsigned int numBytes)623 void CLuaHandle::GameID(const unsigned char* gameID, unsigned int numBytes)
624 {
625 LUA_CALL_IN_CHECK(L);
626 luaL_checkstack(L, 4, __FUNCTION__);
627 const LuaUtils::ScopedDebugTraceBack traceBack(L);
628
629 const LuaHashString cmdStr("GameID");
630 if (!cmdStr.GetGlobalFunc(L)) {
631 return;
632 }
633
634 lua_pushlstring(L, reinterpret_cast<const char*>(gameID), numBytes);
635
636 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
637 }
638
639
TeamDied(int teamID)640 void CLuaHandle::TeamDied(int teamID)
641 {
642 LUA_CALL_IN_CHECK(L);
643 luaL_checkstack(L, 4, __FUNCTION__);
644
645 const LuaUtils::ScopedDebugTraceBack traceBack(L);
646
647 static const LuaHashString cmdStr("TeamDied");
648 if (!cmdStr.GetGlobalFunc(L)) {
649 return; // the call is not defined
650 }
651
652 lua_pushnumber(L, teamID);
653
654 // call the routine
655 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
656 }
657
658
TeamChanged(int teamID)659 void CLuaHandle::TeamChanged(int teamID)
660 {
661 LUA_CALL_IN_CHECK(L);
662 luaL_checkstack(L, 4, __FUNCTION__);
663
664 const LuaUtils::ScopedDebugTraceBack traceBack(L);
665
666 static const LuaHashString cmdStr("TeamChanged");
667 if (!cmdStr.GetGlobalFunc(L)) {
668 return; // the call is not defined
669 }
670
671 lua_pushnumber(L, teamID);
672
673 // call the routine
674 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
675 }
676
677
PlayerChanged(int playerID)678 void CLuaHandle::PlayerChanged(int playerID)
679 {
680 LUA_CALL_IN_CHECK(L);
681 luaL_checkstack(L, 4, __FUNCTION__);
682
683 const LuaUtils::ScopedDebugTraceBack traceBack(L);
684
685 static const LuaHashString cmdStr("PlayerChanged");
686 if (!cmdStr.GetGlobalFunc(L)) {
687 return; // the call is not defined
688 }
689
690 lua_pushnumber(L, playerID);
691
692 // call the routine
693 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
694 }
695
696
PlayerAdded(int playerID)697 void CLuaHandle::PlayerAdded(int playerID)
698 {
699 LUA_CALL_IN_CHECK(L);
700 luaL_checkstack(L, 4, __FUNCTION__);
701
702 const LuaUtils::ScopedDebugTraceBack traceBack(L);
703
704 static const LuaHashString cmdStr("PlayerAdded");
705 if (!cmdStr.GetGlobalFunc(L)) {
706 return; // the call is not defined
707 }
708
709 lua_pushnumber(L, playerID);
710
711 // call the routine
712 RunCallInTraceback(L, cmdStr, 1, 0, traceBack.GetErrFuncIdx(), false);
713 }
714
715
PlayerRemoved(int playerID,int reason)716 void CLuaHandle::PlayerRemoved(int playerID, int reason)
717 {
718 LUA_CALL_IN_CHECK(L);
719 luaL_checkstack(L, 5, __FUNCTION__);
720
721 const LuaUtils::ScopedDebugTraceBack traceBack(L);
722
723 static const LuaHashString cmdStr("PlayerRemoved");
724 if (!cmdStr.GetGlobalFunc(L)) {
725 return; // the call is not defined
726 }
727
728 lua_pushnumber(L, playerID);
729 lua_pushnumber(L, reason);
730
731 // call the routine
732 RunCallInTraceback(L, cmdStr, 2, 0, traceBack.GetErrFuncIdx(), false);
733 }
734
735
736 /******************************************************************************/
737
UnitCallIn(const LuaHashString & hs,const CUnit * unit)738 inline void CLuaHandle::UnitCallIn(const LuaHashString& hs, const CUnit* unit)
739 {
740 LUA_CALL_IN_CHECK(L);
741 luaL_checkstack(L, 6, __FUNCTION__);
742 const LuaUtils::ScopedDebugTraceBack traceBack(L);
743
744 if (!hs.GetGlobalFunc(L)) {
745 return; // the call is not defined
746 }
747
748 lua_pushnumber(L, unit->id);
749 lua_pushnumber(L, unit->unitDef->id);
750 lua_pushnumber(L, unit->team);
751
752 // call the routine
753 RunCallInTraceback(L, hs, 3, 0, traceBack.GetErrFuncIdx(), false);
754 }
755
756
UnitCreated(const CUnit * unit,const CUnit * builder)757 void CLuaHandle::UnitCreated(const CUnit* unit, const CUnit* builder)
758 {
759 LUA_CALL_IN_CHECK(L);
760 luaL_checkstack(L, 7, __FUNCTION__);
761
762 const LuaUtils::ScopedDebugTraceBack traceBack(L);
763
764 static const LuaHashString cmdStr("UnitCreated");
765 if (!cmdStr.GetGlobalFunc(L)) {
766 return; // the call is not defined
767 }
768
769 lua_pushnumber(L, unit->id);
770 lua_pushnumber(L, unit->unitDef->id);
771 lua_pushnumber(L, unit->team);
772 if (builder != NULL) {
773 lua_pushnumber(L, builder->id);
774 }
775
776 int args = (builder != NULL) ? 4 : 3;
777 // call the routine
778 RunCallInTraceback(L, cmdStr, args, 0, traceBack.GetErrFuncIdx(), false);
779 }
780
781
UnitFinished(const CUnit * unit)782 void CLuaHandle::UnitFinished(const CUnit* unit)
783 {
784 static const LuaHashString cmdStr("UnitFinished");
785 UnitCallIn(cmdStr, unit);
786 }
787
788
UnitFromFactory(const CUnit * unit,const CUnit * factory,bool userOrders)789 void CLuaHandle::UnitFromFactory(const CUnit* unit,
790 const CUnit* factory, bool userOrders)
791 {
792 LUA_CALL_IN_CHECK(L);
793 luaL_checkstack(L, 9, __FUNCTION__);
794
795 const LuaUtils::ScopedDebugTraceBack traceBack(L);
796
797 static const LuaHashString cmdStr("UnitFromFactory");
798 if (!cmdStr.GetGlobalFunc(L)) {
799 return; // the call is not defined
800 }
801
802 lua_pushnumber(L, unit->id);
803 lua_pushnumber(L, unit->unitDef->id);
804 lua_pushnumber(L, unit->team);
805 lua_pushnumber(L, factory->id);
806 lua_pushnumber(L, factory->unitDef->id);
807 lua_pushboolean(L, userOrders);
808
809 // call the routine
810 RunCallInTraceback(L, cmdStr, 6, 0, traceBack.GetErrFuncIdx(), false);
811 }
812
813
UnitDestroyed(const CUnit * unit,const CUnit * attacker)814 void CLuaHandle::UnitDestroyed(const CUnit* unit, const CUnit* attacker)
815 {
816 LUA_CALL_IN_CHECK(L);
817 luaL_checkstack(L, 9, __FUNCTION__);
818
819 const LuaUtils::ScopedDebugTraceBack traceBack(L);
820
821 static const LuaHashString cmdStr("UnitDestroyed");
822 if (!cmdStr.GetGlobalFunc(L)) {
823 return; // the call is not defined
824 }
825
826 int argCount = 3;
827 lua_pushnumber(L, unit->id);
828 lua_pushnumber(L, unit->unitDef->id);
829 lua_pushnumber(L, unit->team);
830 if (GetHandleFullRead(L) && (attacker != NULL)) {
831 lua_pushnumber(L, attacker->id);
832 lua_pushnumber(L, attacker->unitDef->id);
833 lua_pushnumber(L, attacker->team);
834 argCount += 3;
835 }
836
837 // call the routine
838 RunCallInTraceback(L, cmdStr, argCount, 0, traceBack.GetErrFuncIdx(), false);
839 }
840
841
UnitTaken(const CUnit * unit,int oldTeam,int newTeam)842 void CLuaHandle::UnitTaken(const CUnit* unit, int oldTeam, int newTeam)
843 {
844 LUA_CALL_IN_CHECK(L);
845 luaL_checkstack(L, 7, __FUNCTION__);
846 const LuaUtils::ScopedDebugTraceBack traceBack(L);
847
848 static const LuaHashString cmdStr("UnitTaken");
849 if (!cmdStr.GetGlobalFunc(L)) {
850 return; // the call is not defined
851 }
852
853 lua_pushnumber(L, unit->id);
854 lua_pushnumber(L, unit->unitDef->id);
855 lua_pushnumber(L, oldTeam);
856 lua_pushnumber(L, newTeam);
857
858 // call the routine
859 RunCallInTraceback(L, cmdStr, 4, 0, traceBack.GetErrFuncIdx(), false);
860 }
861
862
UnitGiven(const CUnit * unit,int oldTeam,int newTeam)863 void CLuaHandle::UnitGiven(const CUnit* unit, int oldTeam, int newTeam)
864 {
865 LUA_CALL_IN_CHECK(L);
866 luaL_checkstack(L, 7, __FUNCTION__);
867 const LuaUtils::ScopedDebugTraceBack traceBack(L);
868
869 static const LuaHashString cmdStr("UnitGiven");
870 if (!cmdStr.GetGlobalFunc(L)) {
871 return; // the call is not defined
872 }
873
874 lua_pushnumber(L, unit->id);
875 lua_pushnumber(L, unit->unitDef->id);
876 lua_pushnumber(L, newTeam);
877 lua_pushnumber(L, oldTeam);
878
879 // call the routine
880 RunCallInTraceback(L, cmdStr, 4, 0, traceBack.GetErrFuncIdx(), false);
881 }
882
883
UnitIdle(const CUnit * unit)884 void CLuaHandle::UnitIdle(const CUnit* unit)
885 {
886 static const LuaHashString cmdStr("UnitIdle");
887 UnitCallIn(cmdStr, unit);
888 }
889
890
UnitCommand(const CUnit * unit,const Command & command)891 void CLuaHandle::UnitCommand(const CUnit* unit, const Command& command)
892 {
893 LUA_CALL_IN_CHECK(L);
894 luaL_checkstack(L, 11, __FUNCTION__);
895
896 const LuaUtils::ScopedDebugTraceBack traceBack(L);
897
898 static const LuaHashString cmdStr("UnitCommand");
899 if (!cmdStr.GetGlobalFunc(L)) {
900 return; // the call is not defined
901 }
902
903 lua_pushnumber(L, unit->id);
904 lua_pushnumber(L, unit->unitDef->id);
905 lua_pushnumber(L, unit->team);
906
907 lua_pushnumber(L, command.GetID());
908
909 //FIXME: perhaps we should push the table version rather than the bitfield directly
910 lua_pushnumber(L, command.options);
911
912 // push the params list
913 LuaUtils::PushCommandParamsTable(L, command, false);
914
915 lua_pushnumber(L, command.tag);
916
917 // call the routine
918 RunCallInTraceback(L, cmdStr, 7, 0, traceBack.GetErrFuncIdx(), false);
919 }
920
921
UnitCmdDone(const CUnit * unit,const Command & command)922 void CLuaHandle::UnitCmdDone(const CUnit* unit, const Command& command)
923 {
924 LUA_CALL_IN_CHECK(L);
925 luaL_checkstack(L, 8, __FUNCTION__);
926
927 const LuaUtils::ScopedDebugTraceBack traceBack(L);
928
929 static const LuaHashString cmdStr("UnitCmdDone");
930 if (!cmdStr.GetGlobalFunc(L)) {
931 return; // the call is not defined
932 }
933
934 lua_pushnumber(L, unit->id);
935 lua_pushnumber(L, unit->unitDef->id);
936 lua_pushnumber(L, unit->team);
937 lua_pushnumber(L, command.GetID());
938 lua_pushnumber(L, command.tag);
939 // push the params list
940 LuaUtils::PushCommandParamsTable(L, command, false);
941 // push the options table
942 LuaUtils::PushCommandOptionsTable(L, command, false);
943
944 // call the routine
945 RunCallInTraceback(L, cmdStr, 7, 0, traceBack.GetErrFuncIdx(), false);
946 }
947
948
UnitDamaged(const CUnit * unit,const CUnit * attacker,float damage,int weaponDefID,int projectileID,bool paralyzer)949 void CLuaHandle::UnitDamaged(
950 const CUnit* unit,
951 const CUnit* attacker,
952 float damage,
953 int weaponDefID,
954 int projectileID,
955 bool paralyzer)
956 {
957 LUA_CALL_IN_CHECK(L);
958 luaL_checkstack(L, 11, __FUNCTION__);
959
960 static const LuaHashString cmdStr(__FUNCTION__);
961 const LuaUtils::ScopedDebugTraceBack traceBack(L);
962
963 if (!cmdStr.GetGlobalFunc(L))
964 return;
965
966 int argCount = 5;
967
968 lua_pushnumber(L, unit->id);
969 lua_pushnumber(L, unit->unitDef->id);
970 lua_pushnumber(L, unit->team);
971 lua_pushnumber(L, damage);
972 lua_pushboolean(L, paralyzer);
973
974 if (GetHandleFullRead(L)) {
975 lua_pushnumber(L, weaponDefID);
976 lua_pushnumber(L, projectileID);
977 argCount += 2;
978
979 if (attacker != NULL) {
980 lua_pushnumber(L, attacker->id);
981 lua_pushnumber(L, attacker->unitDef->id);
982 lua_pushnumber(L, attacker->team);
983 argCount += 3;
984 }
985 }
986
987 // call the routine
988 RunCallInTraceback(L, cmdStr, argCount, 0, traceBack.GetErrFuncIdx(), false);
989 }
990
991
UnitExperience(const CUnit * unit,float oldExperience)992 void CLuaHandle::UnitExperience(const CUnit* unit, float oldExperience)
993 {
994 LUA_CALL_IN_CHECK(L);
995 luaL_checkstack(L, 8, __FUNCTION__);
996
997 const LuaUtils::ScopedDebugTraceBack traceBack(L);
998
999 static const LuaHashString cmdStr("UnitExperience");
1000 if (!cmdStr.GetGlobalFunc(L)) {
1001 return; // the call is not defined
1002 }
1003
1004 lua_pushnumber(L, unit->id);
1005 lua_pushnumber(L, unit->unitDef->id);
1006 lua_pushnumber(L, unit->team);
1007 lua_pushnumber(L, unit->experience);
1008 lua_pushnumber(L, oldExperience);
1009
1010 // call the routine
1011 RunCallInTraceback(L, cmdStr, 5, 0, traceBack.GetErrFuncIdx(), false);
1012 }
1013
1014
UnitHarvestStorageFull(const CUnit * unit)1015 void CLuaHandle::UnitHarvestStorageFull(const CUnit* unit)
1016 {
1017 static const LuaHashString cmdStr("UnitHarvestStorageFull");
1018 UnitCallIn(cmdStr, unit);
1019 }
1020
1021
1022 /******************************************************************************/
1023
UnitSeismicPing(const CUnit * unit,int allyTeam,const float3 & pos,float strength)1024 void CLuaHandle::UnitSeismicPing(const CUnit* unit, int allyTeam,
1025 const float3& pos, float strength)
1026 {
1027 LUA_CALL_IN_CHECK(L);
1028 luaL_checkstack(L, 9, __FUNCTION__);
1029 int readAllyTeam = GetHandleReadAllyTeam(L);
1030 if ((readAllyTeam >= 0) && (unit->losStatus[readAllyTeam] & LOS_INLOS)) {
1031 return; // don't need to see this ping
1032 }
1033
1034 static const LuaHashString cmdStr("UnitSeismicPing");
1035 if (!cmdStr.GetGlobalFunc(L))
1036 return; // the call is not defined
1037
1038 lua_pushnumber(L, pos.x);
1039 lua_pushnumber(L, pos.y);
1040 lua_pushnumber(L, pos.z);
1041 lua_pushnumber(L, strength);
1042 if (GetHandleFullRead(L)) {
1043 lua_pushnumber(L, allyTeam);
1044 lua_pushnumber(L, unit->id);
1045 lua_pushnumber(L, unit->unitDef->id);
1046 }
1047
1048 // call the routine
1049 RunCallIn(L, cmdStr, GetHandleFullRead(L) ? 7 : 4, 0);
1050 }
1051
1052
1053 /******************************************************************************/
1054
LosCallIn(const LuaHashString & hs,const CUnit * unit,int allyTeam)1055 void CLuaHandle::LosCallIn(const LuaHashString& hs,
1056 const CUnit* unit, int allyTeam)
1057 {
1058 LUA_CALL_IN_CHECK(L);
1059 luaL_checkstack(L, 6, __FUNCTION__);
1060 if (!hs.GetGlobalFunc(L))
1061 return; // the call is not defined
1062
1063 lua_pushnumber(L, unit->id);
1064 lua_pushnumber(L, unit->team);
1065 if (GetHandleFullRead(L)) {
1066 lua_pushnumber(L, allyTeam);
1067 lua_pushnumber(L, unit->unitDef->id);
1068 }
1069
1070 // call the routine
1071 RunCallIn(L, hs, GetHandleFullRead(L) ? 4 : 2, 0);
1072 }
1073
1074
UnitEnteredRadar(const CUnit * unit,int allyTeam)1075 void CLuaHandle::UnitEnteredRadar(const CUnit* unit, int allyTeam)
1076 {
1077 static const LuaHashString hs("UnitEnteredRadar");
1078 LosCallIn(hs, unit, allyTeam);
1079 }
1080
1081
UnitEnteredLos(const CUnit * unit,int allyTeam)1082 void CLuaHandle::UnitEnteredLos(const CUnit* unit, int allyTeam)
1083 {
1084 static const LuaHashString hs("UnitEnteredLos");
1085 LosCallIn(hs, unit, allyTeam);
1086 }
1087
1088
UnitLeftRadar(const CUnit * unit,int allyTeam)1089 void CLuaHandle::UnitLeftRadar(const CUnit* unit, int allyTeam)
1090 {
1091 static const LuaHashString hs("UnitLeftRadar");
1092 LosCallIn(hs, unit, allyTeam);
1093 }
1094
1095
UnitLeftLos(const CUnit * unit,int allyTeam)1096 void CLuaHandle::UnitLeftLos(const CUnit* unit, int allyTeam)
1097 {
1098 static const LuaHashString hs("UnitLeftLos");
1099 LosCallIn(hs, unit, allyTeam);
1100 }
1101
1102
1103 /******************************************************************************/
1104
UnitLoaded(const CUnit * unit,const CUnit * transport)1105 void CLuaHandle::UnitLoaded(const CUnit* unit, const CUnit* transport)
1106 {
1107 LUA_CALL_IN_CHECK(L);
1108 luaL_checkstack(L, 8, __FUNCTION__);
1109
1110 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1111
1112 static const LuaHashString cmdStr("UnitLoaded");
1113 if (!cmdStr.GetGlobalFunc(L)) {
1114 return; // the call is not defined
1115 }
1116
1117 lua_pushnumber(L, unit->id);
1118 lua_pushnumber(L, unit->unitDef->id);
1119 lua_pushnumber(L, unit->team);
1120 lua_pushnumber(L, transport->id);
1121 lua_pushnumber(L, transport->team);
1122
1123 // call the routine
1124 RunCallInTraceback(L, cmdStr, 5, 0, traceBack.GetErrFuncIdx(), false);
1125 }
1126
1127
UnitUnloaded(const CUnit * unit,const CUnit * transport)1128 void CLuaHandle::UnitUnloaded(const CUnit* unit, const CUnit* transport)
1129 {
1130 LUA_CALL_IN_CHECK(L);
1131 luaL_checkstack(L, 8, __FUNCTION__);
1132
1133 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1134
1135 static const LuaHashString cmdStr("UnitUnloaded");
1136 if (!cmdStr.GetGlobalFunc(L)) {
1137 return; // the call is not defined
1138 }
1139
1140 lua_pushnumber(L, unit->id);
1141 lua_pushnumber(L, unit->unitDef->id);
1142 lua_pushnumber(L, unit->team);
1143 lua_pushnumber(L, transport->id);
1144 lua_pushnumber(L, transport->team);
1145
1146 // call the routine
1147 RunCallInTraceback(L, cmdStr, 5, 0, traceBack.GetErrFuncIdx(), false);
1148 }
1149
1150
1151 /******************************************************************************/
1152
UnitEnteredWater(const CUnit * unit)1153 void CLuaHandle::UnitEnteredWater(const CUnit* unit)
1154 {
1155 static const LuaHashString cmdStr("UnitEnteredWater");
1156 UnitCallIn(cmdStr, unit);
1157 }
1158
1159
UnitEnteredAir(const CUnit * unit)1160 void CLuaHandle::UnitEnteredAir(const CUnit* unit)
1161 {
1162 static const LuaHashString cmdStr("UnitEnteredAir");
1163 UnitCallIn(cmdStr, unit);
1164 }
1165
1166
UnitLeftWater(const CUnit * unit)1167 void CLuaHandle::UnitLeftWater(const CUnit* unit)
1168 {
1169 static const LuaHashString cmdStr("UnitLeftWater");
1170 UnitCallIn(cmdStr, unit);
1171 }
1172
1173
UnitLeftAir(const CUnit * unit)1174 void CLuaHandle::UnitLeftAir(const CUnit* unit)
1175 {
1176 static const LuaHashString cmdStr("UnitLeftAir");
1177 UnitCallIn(cmdStr, unit);
1178 }
1179
1180
1181 /******************************************************************************/
1182
UnitCloaked(const CUnit * unit)1183 void CLuaHandle::UnitCloaked(const CUnit* unit)
1184 {
1185 static const LuaHashString cmdStr("UnitCloaked");
1186 UnitCallIn(cmdStr, unit);
1187 }
1188
1189
UnitDecloaked(const CUnit * unit)1190 void CLuaHandle::UnitDecloaked(const CUnit* unit)
1191 {
1192 static const LuaHashString cmdStr("UnitDecloaked");
1193 UnitCallIn(cmdStr, unit);
1194 }
1195
1196
1197
UnitUnitCollision(const CUnit * collider,const CUnit * collidee)1198 void CLuaHandle::UnitUnitCollision(const CUnit* collider, const CUnit* collidee)
1199 {
1200 // if empty, we are not a LuaHandleSynced
1201 if (watchUnitDefs.empty()) return;
1202 if (!watchUnitDefs[collider->unitDef->id]) return;
1203 if (!watchUnitDefs[collidee->unitDef->id]) return;
1204
1205 LUA_CALL_IN_CHECK(L);
1206 luaL_checkstack(L, 5, __FUNCTION__);
1207
1208 static const LuaHashString cmdStr("UnitUnitCollision");
1209 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1210
1211 if (!cmdStr.GetGlobalFunc(L)) {
1212 return;
1213 }
1214
1215 lua_pushnumber(L, collider->id);
1216 lua_pushnumber(L, collidee->id);
1217 lua_pushboolean(L, false);
1218
1219 RunCallInTraceback(L, cmdStr, 3, 0, traceBack.GetErrFuncIdx(), false);
1220 }
1221
UnitFeatureCollision(const CUnit * collider,const CFeature * collidee)1222 void CLuaHandle::UnitFeatureCollision(const CUnit* collider, const CFeature* collidee)
1223 {
1224 // if empty, we are not a LuaHandleSynced
1225 if (watchUnitDefs.empty()) return;
1226 if (watchFeatureDefs.empty()) return;
1227 if (!watchUnitDefs[collider->unitDef->id]) return;
1228 if (!watchFeatureDefs[collidee->def->id]) return;
1229
1230 LUA_CALL_IN_CHECK(L);
1231 luaL_checkstack(L, 5, __FUNCTION__);
1232
1233 static const LuaHashString cmdStr("UnitFeatureCollision");
1234 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1235
1236 if (!cmdStr.GetGlobalFunc(L)) {
1237 return;
1238 }
1239
1240 lua_pushnumber(L, collider->id);
1241 lua_pushnumber(L, collidee->id);
1242 lua_pushboolean(L, false);
1243
1244 RunCallInTraceback(L, cmdStr, 3, 0, traceBack.GetErrFuncIdx(), false);
1245 }
1246
UnitMoveFailed(const CUnit * unit)1247 void CLuaHandle::UnitMoveFailed(const CUnit* unit)
1248 {
1249 // if empty, we are not a LuaHandleSynced
1250 if (watchUnitDefs.empty()) return;
1251 if (!watchUnitDefs[unit->unitDef->id]) return;
1252
1253 static const LuaHashString cmdStr("UnitMoveFailed");
1254 UnitCallIn(cmdStr, unit);
1255 }
1256
1257
1258 /******************************************************************************/
1259
FeatureCreated(const CFeature * feature)1260 void CLuaHandle::FeatureCreated(const CFeature* feature)
1261 {
1262 LUA_CALL_IN_CHECK(L);
1263 luaL_checkstack(L, 5, __FUNCTION__);
1264
1265 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1266 static const LuaHashString cmdStr("FeatureCreated");
1267
1268 if (!cmdStr.GetGlobalFunc(L)) {
1269 return; // the call is not defined
1270 }
1271
1272 lua_pushnumber(L, feature->id);
1273 lua_pushnumber(L, feature->allyteam);
1274
1275 // call the routine
1276 RunCallInTraceback(L, cmdStr, 2, 0, traceBack.GetErrFuncIdx(), false);
1277 }
1278
FeatureDestroyed(const CFeature * feature)1279 void CLuaHandle::FeatureDestroyed(const CFeature* feature)
1280 {
1281 LUA_CALL_IN_CHECK(L);
1282 luaL_checkstack(L, 5, __FUNCTION__);
1283
1284 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1285
1286 static const LuaHashString cmdStr("FeatureDestroyed");
1287 if (!cmdStr.GetGlobalFunc(L)) {
1288 return; // the call is not defined
1289 }
1290
1291 lua_pushnumber(L, feature->id);
1292 lua_pushnumber(L, feature->allyteam);
1293
1294 // call the routine
1295 RunCallInTraceback(L, cmdStr, 2, 0, traceBack.GetErrFuncIdx(), false);
1296 }
1297
FeatureDamaged(const CFeature * feature,const CUnit * attacker,float damage,int weaponDefID,int projectileID)1298 void CLuaHandle::FeatureDamaged(
1299 const CFeature* feature,
1300 const CUnit* attacker,
1301 float damage,
1302 int weaponDefID,
1303 int projectileID)
1304 {
1305 LUA_CALL_IN_CHECK(L);
1306 luaL_checkstack(L, 11, __FUNCTION__);
1307 const LuaUtils::ScopedDebugTraceBack traceBack(L);
1308
1309 static const LuaHashString cmdStr(__FUNCTION__);
1310 if (!cmdStr.GetGlobalFunc(L))
1311 return;
1312
1313 int argCount = 4;
1314
1315 lua_pushnumber(L, feature->id);
1316 lua_pushnumber(L, feature->def->id);
1317 lua_pushnumber(L, feature->team);
1318 lua_pushnumber(L, damage);
1319
1320 if (GetHandleFullRead(L)) {
1321 lua_pushnumber(L, weaponDefID); argCount += 1;
1322 lua_pushnumber(L, projectileID); argCount += 1;
1323
1324 if (attacker != NULL) {
1325 lua_pushnumber(L, attacker->id);
1326 lua_pushnumber(L, attacker->unitDef->id);
1327 lua_pushnumber(L, attacker->team);
1328 argCount += 3;
1329 }
1330 }
1331
1332 // call the routine
1333 RunCallInTraceback(L, cmdStr, argCount, 0, traceBack.GetErrFuncIdx(), false);
1334 }
1335
1336
1337 /******************************************************************************/
1338
ProjectileCreated(const CProjectile * p)1339 void CLuaHandle::ProjectileCreated(const CProjectile* p)
1340 {
1341 // if empty, we are not a LuaHandleSynced
1342 if (watchWeaponDefs.empty()) return;
1343
1344 if (!p->synced) return;
1345 if (!p->weapon && !p->piece) return;
1346
1347 const CUnit* owner = p->owner();
1348 const CWeaponProjectile* wp = p->weapon? static_cast<const CWeaponProjectile*>(p): NULL;
1349 const WeaponDef* wd = p->weapon? wp->GetWeaponDef(): NULL;
1350
1351 // if this weapon-type is not being watched, bail
1352 if (p->weapon && (wd == NULL || !watchWeaponDefs[wd->id]))
1353 return;
1354
1355 LUA_CALL_IN_CHECK(L);
1356 luaL_checkstack(L, 5, __FUNCTION__);
1357
1358 static const LuaHashString cmdStr("ProjectileCreated");
1359
1360 if (!cmdStr.GetGlobalFunc(L))
1361 return; // the call is not defined
1362
1363 lua_pushnumber(L, p->id);
1364 lua_pushnumber(L, ((owner != NULL)? owner->id: -1));
1365 lua_pushnumber(L, ((wd != NULL)? wd->id: -1));
1366
1367 // call the routine
1368 RunCallIn(L, cmdStr, 3, 0);
1369 }
1370
1371
ProjectileDestroyed(const CProjectile * p)1372 void CLuaHandle::ProjectileDestroyed(const CProjectile* p)
1373 {
1374 // if empty, we are not a LuaHandleSynced
1375 if (watchWeaponDefs.empty()) return;
1376
1377 if (!p->synced) return;
1378 if (!p->weapon && !p->piece) return;
1379 if (p->weapon) {
1380 const CWeaponProjectile* wp = static_cast<const CWeaponProjectile*>(p);
1381 const WeaponDef* wd = wp->GetWeaponDef();
1382
1383 // if this weapon-type is not being watched, bail
1384 if (wd == NULL || !watchWeaponDefs[wd->id]) return;
1385 }
1386
1387 LUA_CALL_IN_CHECK(L);
1388 luaL_checkstack(L, 4, __FUNCTION__);
1389
1390 static const LuaHashString cmdStr("ProjectileDestroyed");
1391
1392 if (!cmdStr.GetGlobalFunc(L))
1393 return; // the call is not defined
1394
1395 lua_pushnumber(L, p->id);
1396
1397 // call the routine
1398 RunCallIn(L, cmdStr, 1, 0);
1399 }
1400
1401 /******************************************************************************/
1402
Explosion(int weaponDefID,int projectileID,const float3 & pos,const CUnit * owner)1403 bool CLuaHandle::Explosion(int weaponDefID, int projectileID, const float3& pos, const CUnit* owner)
1404 {
1405 // piece-projectile collision (*ALL* other
1406 // explosion events pass valid weaponDefIDs)
1407 if (weaponDefID < 0) return false;
1408
1409 // if empty, we are not a LuaHandleSynced
1410 if (watchWeaponDefs.empty()) return false;
1411 if (!watchWeaponDefs[weaponDefID]) return false;
1412
1413 LUA_CALL_IN_CHECK(L, false);
1414 luaL_checkstack(L, 7, __FUNCTION__);
1415
1416 static const LuaHashString cmdStr("Explosion");
1417 if (!cmdStr.GetGlobalFunc(L))
1418 return false; // the call is not defined
1419
1420 lua_pushnumber(L, weaponDefID);
1421 lua_pushnumber(L, pos.x);
1422 lua_pushnumber(L, pos.y);
1423 lua_pushnumber(L, pos.z);
1424 if (owner != NULL) {
1425 lua_pushnumber(L, owner->id);
1426 }
1427
1428 // call the routine
1429 if (!RunCallIn(L, cmdStr, (owner == NULL) ? 4 : 5, 1))
1430 return false;
1431
1432 // get the results
1433 const bool retval = luaL_optboolean(L, -1, false);
1434 lua_pop(L, 1);
1435 return retval;
1436 }
1437
1438
StockpileChanged(const CUnit * unit,const CWeapon * weapon,int oldCount)1439 void CLuaHandle::StockpileChanged(const CUnit* unit,
1440 const CWeapon* weapon, int oldCount)
1441 {
1442 LUA_CALL_IN_CHECK(L);
1443 luaL_checkstack(L, 8, __FUNCTION__);
1444
1445 static const LuaHashString cmdStr("StockpileChanged");
1446 if (!cmdStr.GetGlobalFunc(L))
1447 return;
1448
1449 lua_pushnumber(L, unit->id);
1450 lua_pushnumber(L, unit->unitDef->id);
1451 lua_pushnumber(L, unit->team);
1452 lua_pushnumber(L, weapon->weaponNum);
1453 lua_pushnumber(L, oldCount);
1454 lua_pushnumber(L, weapon->numStockpiled);
1455
1456 // call the routine
1457 RunCallIn(L, cmdStr, 6, 0);
1458 }
1459
1460
1461
RecvLuaMsg(const string & msg,int playerID)1462 bool CLuaHandle::RecvLuaMsg(const string& msg, int playerID)
1463 {
1464 LUA_CALL_IN_CHECK(L, false);
1465 luaL_checkstack(L, 8, __FUNCTION__);
1466
1467 static const LuaHashString cmdStr("RecvLuaMsg");
1468 if (!cmdStr.GetGlobalFunc(L))
1469 return false;
1470
1471 lua_pushsstring(L, msg); // allows embedded 0's
1472 lua_pushnumber(L, playerID);
1473
1474 // call the routine
1475 if (!RunCallIn(L, cmdStr, 2, 1))
1476 return false;
1477
1478 const bool retval = luaL_optboolean(L, -1, false);
1479 lua_pop(L, 1);
1480 return retval;
1481 }
1482
1483
1484 /******************************************************************************/
1485
HandleLuaMsg(int playerID,int script,int mode,const std::vector<boost::uint8_t> & data)1486 void CLuaHandle::HandleLuaMsg(int playerID, int script, int mode, const std::vector<boost::uint8_t>& data)
1487 {
1488 std::string msg;
1489 msg.resize(data.size());
1490 std::copy(data.begin(), data.end(), msg.begin());
1491 if (script == LUA_HANDLE_ORDER_UI) {
1492 if (luaUI) {
1493 bool sendMsg = false;
1494 if (mode == 0) {
1495 sendMsg = true;
1496 }
1497 else if (mode == 's') {
1498 sendMsg = gu->spectating;
1499 }
1500 else if (mode == 'a') {
1501 const CPlayer* player = playerHandler->Player(playerID);
1502 if (player == NULL) {
1503 return;
1504 }
1505 if (gu->spectatingFullView) {
1506 sendMsg = true;
1507 }
1508 else if (player->spectator) {
1509 sendMsg = gu->spectating;
1510 } else {
1511 const int msgAllyTeam = teamHandler->AllyTeam(player->team);
1512 sendMsg = teamHandler->Ally(msgAllyTeam, gu->myAllyTeam);
1513 }
1514 }
1515 if (sendMsg) {
1516 luaUI->RecvLuaMsg(msg, playerID);
1517 }
1518 }
1519 }
1520 else if (script == LUA_HANDLE_ORDER_GAIA) {
1521 if (luaGaia) {
1522 luaGaia->RecvLuaMsg(msg, playerID);
1523 }
1524 }
1525 else if (script == LUA_HANDLE_ORDER_RULES) {
1526 if (luaRules) {
1527 luaRules->RecvLuaMsg(msg, playerID);
1528 }
1529 }
1530 }
1531
1532
1533 /******************************************************************************/
1534
1535
Save(zipFile archive)1536 void CLuaHandle::Save(zipFile archive)
1537 {
1538 // LuaUI does not get this call-in
1539 if (GetUserMode()) {
1540 return;
1541 }
1542
1543 LUA_CALL_IN_CHECK(L);
1544 luaL_checkstack(L, 3, __FUNCTION__);
1545 static const LuaHashString cmdStr("Save");
1546 if (!cmdStr.GetGlobalFunc(L)) {
1547 return;
1548 }
1549
1550 // Save gets ZipFileWriter userdatum as single argument
1551 LuaZipFileWriter::PushNew(L, "", archive);
1552
1553 // call the routine
1554 RunCallIn(L, cmdStr, 1, 0);
1555 }
1556
1557
UnsyncedHeightMapUpdate(const SRectangle & rect)1558 void CLuaHandle::UnsyncedHeightMapUpdate(const SRectangle& rect)
1559 {
1560 LUA_CALL_IN_CHECK(L);
1561 luaL_checkstack(L, 6, __FUNCTION__);
1562 static const LuaHashString cmdStr("UnsyncedHeightMapUpdate");
1563 if (!cmdStr.GetGlobalFunc(L)) {
1564 return;
1565 }
1566
1567 lua_pushnumber(L, rect.x1);
1568 lua_pushnumber(L, rect.z1);
1569 lua_pushnumber(L, rect.x2);
1570 lua_pushnumber(L, rect.z2);
1571
1572 // call the routine
1573 RunCallIn(L, cmdStr, 4, 0);
1574 }
1575
1576
Update()1577 void CLuaHandle::Update()
1578 {
1579 LUA_CALL_IN_CHECK(L);
1580 luaL_checkstack(L, 2, __FUNCTION__);
1581 static const LuaHashString cmdStr("Update");
1582 if (!cmdStr.GetGlobalFunc(L)) {
1583 return;
1584 }
1585
1586 // call the routine
1587 RunCallIn(L, cmdStr, 0, 0);
1588 }
1589
1590
ViewResize()1591 void CLuaHandle::ViewResize()
1592 {
1593 LUA_CALL_IN_CHECK(L);
1594 luaL_checkstack(L, 5, __FUNCTION__);
1595 static const LuaHashString cmdStr("ViewResize");
1596 if (!cmdStr.GetGlobalFunc(L)) {
1597 return;
1598 }
1599
1600 const int winPosY_bl = globalRendering->screenSizeY - globalRendering->winSizeY - globalRendering->winPosY; //! origin BOTTOMLEFT
1601
1602 lua_newtable(L);
1603 LuaPushNamedNumber(L, "screenSizeX", globalRendering->screenSizeX);
1604 LuaPushNamedNumber(L, "screenSizeY", globalRendering->screenSizeY);
1605 LuaPushNamedNumber(L, "screenPosX", 0.0f);
1606 LuaPushNamedNumber(L, "screenPosY", 0.0f);
1607 LuaPushNamedNumber(L, "windowSizeX", globalRendering->winSizeX);
1608 LuaPushNamedNumber(L, "windowSizeY", globalRendering->winSizeY);
1609 LuaPushNamedNumber(L, "windowPosX", globalRendering->winPosX);
1610 LuaPushNamedNumber(L, "windowPosY", winPosY_bl);
1611 LuaPushNamedNumber(L, "viewSizeX", globalRendering->viewSizeX);
1612 LuaPushNamedNumber(L, "viewSizeY", globalRendering->viewSizeY);
1613 LuaPushNamedNumber(L, "viewPosX", globalRendering->viewPosX);
1614 LuaPushNamedNumber(L, "viewPosY", globalRendering->viewPosY);
1615
1616 // call the routine
1617 RunCallIn(L, cmdStr, 1, 0);
1618 }
1619
1620
DefaultCommand(const CUnit * unit,const CFeature * feature,int & cmd)1621 bool CLuaHandle::DefaultCommand(const CUnit* unit,
1622 const CFeature* feature, int& cmd)
1623 {
1624 LUA_CALL_IN_CHECK(L, false);
1625 luaL_checkstack(L, 4, __FUNCTION__);
1626 static const LuaHashString cmdStr("DefaultCommand");
1627 if (!cmdStr.GetGlobalFunc(L)) {
1628 return false;
1629 }
1630
1631 int args = 0;
1632 if (unit) {
1633 HSTR_PUSH(L, "unit");
1634 lua_pushnumber(L, unit->id);
1635 args = 2;
1636 }
1637 else if (feature) {
1638 HSTR_PUSH(L, "feature");
1639 lua_pushnumber(L, feature->id);
1640 args = 2;
1641 }
1642 /* FIXME
1643 else if (groundPos) {
1644 HSTR_PUSH(L, "ground");
1645 lua_pushnumber(L, groundPos->x);
1646 lua_pushnumber(L, groundPos->y);
1647 lua_pushnumber(L, groundPos->z);
1648 args = 4;
1649 }
1650 else {
1651 HSTR_PUSH(L, "selection");
1652 args = 1;
1653 }
1654 */
1655
1656 // call the routine
1657 if (!RunCallIn(L, cmdStr, args, 1))
1658 return false;
1659
1660 if (!lua_isnumber(L, 1)) {
1661 lua_pop(L, 1);
1662 return false;
1663 }
1664
1665 cmd = lua_toint(L, -1);
1666 lua_pop(L, 1);
1667 return true;
1668 }
1669
1670
RunDrawCallIn(const LuaHashString & hs)1671 void CLuaHandle::RunDrawCallIn(const LuaHashString& hs)
1672 {
1673 LUA_CALL_IN_CHECK(L);
1674 luaL_checkstack(L, 2, __FUNCTION__);
1675 if (!hs.GetGlobalFunc(L)) {
1676 return;
1677 }
1678
1679 LuaOpenGL::SetDrawingEnabled(L, true);
1680
1681 // call the routine
1682 RunCallIn(L, hs, 0, 0);
1683
1684 LuaOpenGL::SetDrawingEnabled(L, false);
1685 }
1686
1687
DrawGenesis()1688 void CLuaHandle::DrawGenesis()
1689 {
1690 static const LuaHashString cmdStr("DrawGenesis");
1691 RunDrawCallIn(cmdStr);
1692 }
1693
1694
DrawWorld()1695 void CLuaHandle::DrawWorld()
1696 {
1697 static const LuaHashString cmdStr("DrawWorld");
1698 RunDrawCallIn(cmdStr);
1699 }
1700
1701
DrawWorldPreUnit()1702 void CLuaHandle::DrawWorldPreUnit()
1703 {
1704 static const LuaHashString cmdStr("DrawWorldPreUnit");
1705 RunDrawCallIn(cmdStr);
1706 }
1707
1708
DrawWorldShadow()1709 void CLuaHandle::DrawWorldShadow()
1710 {
1711 static const LuaHashString cmdStr("DrawWorldShadow");
1712 RunDrawCallIn(cmdStr);
1713 }
1714
1715
DrawWorldReflection()1716 void CLuaHandle::DrawWorldReflection()
1717 {
1718 static const LuaHashString cmdStr("DrawWorldReflection");
1719 RunDrawCallIn(cmdStr);
1720 }
1721
1722
DrawWorldRefraction()1723 void CLuaHandle::DrawWorldRefraction()
1724 {
1725 static const LuaHashString cmdStr("DrawWorldRefraction");
1726 RunDrawCallIn(cmdStr);
1727 }
1728
1729
DrawScreen()1730 void CLuaHandle::DrawScreen()
1731 {
1732 LUA_CALL_IN_CHECK(L);
1733 luaL_checkstack(L, 4, __FUNCTION__);
1734 static const LuaHashString cmdStr("DrawScreen");
1735 if (!cmdStr.GetGlobalFunc(L)) {
1736 return;
1737 }
1738
1739 lua_pushnumber(L, globalRendering->viewSizeX);
1740 lua_pushnumber(L, globalRendering->viewSizeY);
1741
1742 LuaOpenGL::SetDrawingEnabled(L, true);
1743
1744 // call the routine
1745 RunCallIn(L, cmdStr, 2, 0);
1746
1747 LuaOpenGL::SetDrawingEnabled(L, false);
1748 }
1749
1750
DrawScreenEffects()1751 void CLuaHandle::DrawScreenEffects()
1752 {
1753 LUA_CALL_IN_CHECK(L);
1754 luaL_checkstack(L, 4, __FUNCTION__);
1755 static const LuaHashString cmdStr("DrawScreenEffects");
1756 if (!cmdStr.GetGlobalFunc(L)) {
1757 return;
1758 }
1759
1760 lua_pushnumber(L, globalRendering->viewSizeX);
1761 lua_pushnumber(L, globalRendering->viewSizeY);
1762
1763 LuaOpenGL::SetDrawingEnabled(L, true);
1764
1765 // call the routine
1766 RunCallIn(L, cmdStr, 2, 0);
1767
1768 LuaOpenGL::SetDrawingEnabled(L, false);
1769 }
1770
1771
DrawInMiniMap()1772 void CLuaHandle::DrawInMiniMap()
1773 {
1774 LUA_CALL_IN_CHECK(L);
1775 luaL_checkstack(L, 4, __FUNCTION__);
1776 static const LuaHashString cmdStr("DrawInMiniMap");
1777 if (!cmdStr.GetGlobalFunc(L)) {
1778 return;
1779 }
1780
1781 lua_pushnumber(L, minimap->GetSizeX());
1782 lua_pushnumber(L, minimap->GetSizeY());
1783
1784 const bool origDrawingState = LuaOpenGL::IsDrawingEnabled(L);
1785 LuaOpenGL::SetDrawingEnabled(L, true);
1786
1787 // call the routine
1788 RunCallIn(L, cmdStr, 2, 0);
1789
1790 LuaOpenGL::SetDrawingEnabled(L, origDrawingState);
1791 }
1792
1793
DrawInMiniMapBackground()1794 void CLuaHandle::DrawInMiniMapBackground()
1795 {
1796 LUA_CALL_IN_CHECK(L);
1797 luaL_checkstack(L, 4, __FUNCTION__);
1798 static const LuaHashString cmdStr("DrawInMiniMapBackground");
1799 if (!cmdStr.GetGlobalFunc(L)) {
1800 return;
1801 }
1802
1803 lua_pushnumber(L, minimap->GetSizeX());
1804 lua_pushnumber(L, minimap->GetSizeY());
1805
1806 const bool origDrawingState = LuaOpenGL::IsDrawingEnabled(L);
1807 LuaOpenGL::SetDrawingEnabled(L, true);
1808
1809 // call the routine
1810 RunCallIn(L, cmdStr, 2, 0);
1811
1812 LuaOpenGL::SetDrawingEnabled(L, origDrawingState);
1813 }
1814
1815
GameProgress(int frameNum)1816 void CLuaHandle::GameProgress(int frameNum )
1817 {
1818 LUA_CALL_IN_CHECK(L);
1819 luaL_checkstack(L, 3, __FUNCTION__);
1820 static const LuaHashString cmdStr("GameProgress");
1821 if (!cmdStr.GetGlobalFunc(L)) {
1822 return;
1823 }
1824
1825 lua_pushnumber(L, frameNum);
1826
1827 // call the routine
1828 RunCallIn(L, cmdStr, 1, 0);
1829 }
1830
1831
1832 /******************************************************************************/
1833 /******************************************************************************/
1834
KeyPress(int key,bool isRepeat)1835 bool CLuaHandle::KeyPress(int key, bool isRepeat)
1836 {
1837 if (!CheckModUICtrl()) {
1838 return false;
1839 }
1840 LUA_CALL_IN_CHECK(L, false);
1841 luaL_checkstack(L, 6, __FUNCTION__);
1842 static const LuaHashString cmdStr("KeyPress");
1843 if (!cmdStr.GetGlobalFunc(L)) {
1844 return false; // the call is not defined, do not take the event
1845 }
1846
1847 //FIXME we should never had started using directly SDL consts, somaeday we should weakly force lua-devs to fix their code
1848 lua_pushinteger(L, SDL21_keysyms(key));
1849
1850 lua_createtable(L, 0, 4);
1851 HSTR_PUSH_BOOL(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT));
1852 HSTR_PUSH_BOOL(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL));
1853 HSTR_PUSH_BOOL(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI));
1854 HSTR_PUSH_BOOL(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT));
1855
1856 lua_pushboolean(L, isRepeat);
1857
1858 CKeySet ks(key, false);
1859 lua_pushsstring(L, ks.GetString(true));
1860 lua_pushinteger(L, 0); //FIXME remove, was deprecated utf32 char (now uses TextInput for that)
1861
1862 // call the function
1863 if (!RunCallIn(L, cmdStr, 5, 1))
1864 return false;
1865
1866 const bool retval = luaL_optboolean(L, -1, false);
1867 lua_pop(L, 1);
1868 return retval;
1869 }
1870
1871
KeyRelease(int key)1872 bool CLuaHandle::KeyRelease(int key)
1873 {
1874 if (!CheckModUICtrl()) {
1875 return false;
1876 }
1877 LUA_CALL_IN_CHECK(L, false);
1878 luaL_checkstack(L, 5, __FUNCTION__);
1879 static const LuaHashString cmdStr("KeyRelease");
1880 if (!cmdStr.GetGlobalFunc(L)) {
1881 return false; // the call is not defined, do not take the event
1882 }
1883
1884 lua_pushinteger(L, SDL21_keysyms(key));
1885
1886 lua_createtable(L, 0, 4);
1887 HSTR_PUSH_BOOL(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT));
1888 HSTR_PUSH_BOOL(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL));
1889 HSTR_PUSH_BOOL(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI));
1890 HSTR_PUSH_BOOL(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT));
1891
1892 CKeySet ks(key, false);
1893 lua_pushsstring(L, ks.GetString(true));
1894 lua_pushinteger(L, 0); //FIXME remove, was deprecated utf32 char (now uses TextInput for that)
1895
1896 // call the function
1897 if (!RunCallIn(L, cmdStr, 4, 1))
1898 return false;
1899
1900 const bool retval = luaL_optboolean(L, -1, false);
1901 lua_pop(L, 1);
1902 return retval;
1903 }
1904
1905
TextInput(const std::string & utf8)1906 bool CLuaHandle::TextInput(const std::string& utf8)
1907 {
1908 if (!CheckModUICtrl()) {
1909 return false;
1910 }
1911 LUA_CALL_IN_CHECK(L, false);
1912 luaL_checkstack(L, 3, __FUNCTION__);
1913 static const LuaHashString cmdStr("TextInput");
1914 if (!cmdStr.GetGlobalFunc(L)) {
1915 return false; // the call is not defined, do not take the event
1916 }
1917
1918 lua_pushsstring(L, utf8);
1919 //lua_pushnumber(L, UTF8toUTF32(utf8));
1920
1921 // call the function
1922 if (!RunCallIn(L, cmdStr, 1, 1))
1923 return false;
1924
1925 const bool retval = luaL_optboolean(L, -1, false);
1926 lua_pop(L, 1);
1927 return retval;
1928 }
1929
1930
MousePress(int x,int y,int button)1931 bool CLuaHandle::MousePress(int x, int y, int button)
1932 {
1933 if (!CheckModUICtrl()) {
1934 return false;
1935 }
1936 LUA_CALL_IN_CHECK(L, false);
1937 luaL_checkstack(L, 5, __FUNCTION__);
1938 static const LuaHashString cmdStr("MousePress");
1939 if (!cmdStr.GetGlobalFunc(L)) {
1940 return false; // the call is not defined, do not take the event
1941 }
1942
1943 lua_pushnumber(L, x - globalRendering->viewPosX);
1944 lua_pushnumber(L, globalRendering->viewSizeY - y - 1);
1945 lua_pushnumber(L, button);
1946
1947 // call the function
1948 if (!RunCallIn(L, cmdStr, 3, 1))
1949 return false;
1950
1951 const bool retval = luaL_optboolean(L, -1, false);
1952 lua_pop(L, 1);
1953 return retval;
1954 }
1955
1956
MouseRelease(int x,int y,int button)1957 void CLuaHandle::MouseRelease(int x, int y, int button)
1958 {
1959 if (!CheckModUICtrl()) {
1960 return;
1961 }
1962 LUA_CALL_IN_CHECK(L, false);
1963 luaL_checkstack(L, 5, __FUNCTION__);
1964 static const LuaHashString cmdStr("MouseRelease");
1965 if (!cmdStr.GetGlobalFunc(L)) {
1966 return; // the call is not defined, do not take the event
1967 }
1968
1969 lua_pushnumber(L, x - globalRendering->viewPosX);
1970 lua_pushnumber(L, globalRendering->viewSizeY - y - 1);
1971 lua_pushnumber(L, button);
1972
1973 // call the function
1974 RunCallIn(L, cmdStr, 3, 0);
1975 }
1976
1977
MouseMove(int x,int y,int dx,int dy,int button)1978 bool CLuaHandle::MouseMove(int x, int y, int dx, int dy, int button)
1979 {
1980 if (!CheckModUICtrl()) {
1981 return false;
1982 }
1983 LUA_CALL_IN_CHECK(L, false);
1984 luaL_checkstack(L, 7, __FUNCTION__);
1985 static const LuaHashString cmdStr("MouseMove");
1986 if (!cmdStr.GetGlobalFunc(L)) {
1987 return false; // the call is not defined, do not take the event
1988 }
1989
1990 lua_pushnumber(L, x - globalRendering->viewPosX);
1991 lua_pushnumber(L, globalRendering->viewSizeY - y - 1);
1992 lua_pushnumber(L, dx);
1993 lua_pushnumber(L, -dy);
1994 lua_pushnumber(L, button);
1995
1996 // call the function
1997 if (!RunCallIn(L, cmdStr, 5, 1))
1998 return false;
1999
2000 const bool retval = luaL_optboolean(L, -1, false);
2001 lua_pop(L, 1);
2002 return retval;
2003 }
2004
2005
MouseWheel(bool up,float value)2006 bool CLuaHandle::MouseWheel(bool up, float value)
2007 {
2008 if (!CheckModUICtrl()) {
2009 return false;
2010 }
2011 LUA_CALL_IN_CHECK(L, false);
2012 luaL_checkstack(L, 4, __FUNCTION__);
2013 static const LuaHashString cmdStr("MouseWheel");
2014 if (!cmdStr.GetGlobalFunc(L)) {
2015 return false; // the call is not defined, do not take the event
2016 }
2017
2018 lua_pushboolean(L, up);
2019 lua_pushnumber(L, value);
2020
2021 // call the function
2022 if (!RunCallIn(L, cmdStr, 2, 1))
2023 return false;
2024
2025 const bool retval = luaL_optboolean(L, -1, false);
2026 lua_pop(L, 1);
2027 return retval;
2028 }
2029
JoystickEvent(const std::string & event,int val1,int val2)2030 bool CLuaHandle::JoystickEvent(const std::string& event, int val1, int val2)
2031 {
2032 if (!CheckModUICtrl()) {
2033 return false;
2034 }
2035 LUA_CALL_IN_CHECK(L, false);
2036 luaL_checkstack(L, 4, __FUNCTION__);
2037 const LuaHashString cmdStr(event);
2038 if (!cmdStr.GetGlobalFunc(L)) {
2039 return false; // the call is not defined, do not take the event
2040 }
2041
2042 lua_pushnumber(L, val1);
2043 lua_pushnumber(L, val2);
2044
2045 // call the function
2046 if (!RunCallIn(L, cmdStr, 2, 1))
2047 return false;
2048
2049 const bool retval = luaL_optboolean(L, -1, false);
2050 lua_pop(L, 1);
2051 return retval;
2052 }
2053
IsAbove(int x,int y)2054 bool CLuaHandle::IsAbove(int x, int y)
2055 {
2056 if (!CheckModUICtrl()) {
2057 return false;
2058 }
2059 LUA_CALL_IN_CHECK(L, false);
2060 luaL_checkstack(L, 4, __FUNCTION__);
2061 static const LuaHashString cmdStr("IsAbove");
2062 if (!cmdStr.GetGlobalFunc(L)) {
2063 return false; // the call is not defined
2064 }
2065
2066 lua_pushnumber(L, x - globalRendering->viewPosX);
2067 lua_pushnumber(L, globalRendering->viewSizeY - y - 1);
2068
2069 // call the function
2070 if (!RunCallIn(L, cmdStr, 2, 1))
2071 return false;
2072
2073 const bool retval = luaL_optboolean(L, -1, false);
2074 lua_pop(L, 1);
2075 return retval;
2076 }
2077
2078
GetTooltip(int x,int y)2079 string CLuaHandle::GetTooltip(int x, int y)
2080 {
2081 if (!CheckModUICtrl()) {
2082 return "";
2083 }
2084 LUA_CALL_IN_CHECK(L, "");
2085 luaL_checkstack(L, 4, __FUNCTION__);
2086 static const LuaHashString cmdStr("GetTooltip");
2087 if (!cmdStr.GetGlobalFunc(L)) {
2088 return ""; // the call is not defined
2089 }
2090
2091 lua_pushnumber(L, x - globalRendering->viewPosX);
2092 lua_pushnumber(L, globalRendering->viewSizeY - y - 1);
2093
2094 // call the function
2095 if (!RunCallIn(L, cmdStr, 2, 1))
2096 return "";
2097
2098 const string retval = luaL_optsstring(L, -1, "");
2099 lua_pop(L, 1);
2100 return retval;
2101 }
2102
2103
CommandNotify(const Command & cmd)2104 bool CLuaHandle::CommandNotify(const Command& cmd)
2105 {
2106 if (!CheckModUICtrl()) {
2107 return false;
2108 }
2109 LUA_CALL_IN_CHECK(L, false);
2110 luaL_checkstack(L, 5, __FUNCTION__);
2111 static const LuaHashString cmdStr("CommandNotify");
2112 if (!cmdStr.GetGlobalFunc(L)) {
2113 return false; // the call is not defined
2114 }
2115
2116 // push the command id
2117 lua_pushnumber(L, cmd.GetID());
2118
2119 // push the params list
2120 LuaUtils::PushCommandParamsTable(L, cmd, false);
2121 // push the options table
2122 LuaUtils::PushCommandOptionsTable(L, cmd, false);
2123
2124 // call the function
2125 if (!RunCallIn(L, cmdStr, 3, 1))
2126 return false;
2127
2128 // get the results
2129 const bool retval = luaL_optboolean(L, -1, false);
2130 lua_pop(L, 1);
2131 return retval;
2132 }
2133
2134
AddConsoleLine(const string & msg,const string & section,int level)2135 bool CLuaHandle::AddConsoleLine(const string& msg, const string& section, int level)
2136 {
2137 if (!CheckModUICtrl()) {
2138 return true; // FIXME?
2139 }
2140
2141 LUA_CALL_IN_CHECK(L, true);
2142 luaL_checkstack(L, 4, __FUNCTION__);
2143 static const LuaHashString cmdStr("AddConsoleLine");
2144 if (!cmdStr.GetGlobalFunc(L)) {
2145 return true; // the call is not defined
2146 }
2147
2148 lua_pushsstring(L, msg);
2149 lua_pushnumber(L, level);
2150
2151 // call the function
2152 if (!RunCallIn(L, cmdStr, 2, 0))
2153 return false;
2154
2155 return true;
2156 }
2157
2158
2159
GroupChanged(int groupID)2160 bool CLuaHandle::GroupChanged(int groupID)
2161 {
2162 if (!CheckModUICtrl()) {
2163 return false;
2164 }
2165 LUA_CALL_IN_CHECK(L, false);
2166 luaL_checkstack(L, 3, __FUNCTION__);
2167 static const LuaHashString cmdStr("GroupChanged");
2168 if (!cmdStr.GetGlobalFunc(L)) {
2169 return false; // the call is not defined
2170 }
2171
2172 lua_pushnumber(L, groupID);
2173
2174 // call the routine
2175 if (!RunCallIn(L, cmdStr, 1, 0))
2176 return false;
2177
2178 return true;
2179 }
2180
2181
2182
WorldTooltip(const CUnit * unit,const CFeature * feature,const float3 * groundPos)2183 string CLuaHandle::WorldTooltip(const CUnit* unit,
2184 const CFeature* feature,
2185 const float3* groundPos)
2186 {
2187 if (!CheckModUICtrl()) {
2188 return "";
2189 }
2190 LUA_CALL_IN_CHECK(L, "");
2191 luaL_checkstack(L, 6, __FUNCTION__);
2192 static const LuaHashString cmdStr("WorldTooltip");
2193 if (!cmdStr.GetGlobalFunc(L)) {
2194 return ""; // the call is not defined
2195 }
2196
2197 int args;
2198 if (unit) {
2199 HSTR_PUSH(L, "unit");
2200 lua_pushnumber(L, unit->id);
2201 args = 2;
2202 }
2203 else if (feature) {
2204 HSTR_PUSH(L, "feature");
2205 lua_pushnumber(L, feature->id);
2206 args = 2;
2207 }
2208 else if (groundPos) {
2209 HSTR_PUSH(L, "ground");
2210 lua_pushnumber(L, groundPos->x);
2211 lua_pushnumber(L, groundPos->y);
2212 lua_pushnumber(L, groundPos->z);
2213 args = 4;
2214 }
2215 else {
2216 HSTR_PUSH(L, "selection");
2217 args = 1;
2218 }
2219
2220 // call the routine
2221 if (!RunCallIn(L, cmdStr, args, 1))
2222 return "";
2223
2224 const string retval = luaL_optstring(L, -1, "");
2225 lua_pop(L, 1);
2226 return retval;
2227 }
2228
2229
MapDrawCmd(int playerID,int type,const float3 * pos0,const float3 * pos1,const string * label)2230 bool CLuaHandle::MapDrawCmd(int playerID, int type,
2231 const float3* pos0,
2232 const float3* pos1,
2233 const string* label)
2234 {
2235 if (!CheckModUICtrl()) {
2236 return false;
2237 }
2238 LUA_CALL_IN_CHECK(L, false);
2239 luaL_checkstack(L, 9, __FUNCTION__);
2240 static const LuaHashString cmdStr("MapDrawCmd");
2241 if (!cmdStr.GetGlobalFunc(L)) {
2242 return false; // the call is not defined
2243 }
2244
2245 int args;
2246
2247 lua_pushnumber(L, playerID);
2248
2249 if (type == MAPDRAW_POINT) {
2250 HSTR_PUSH(L, "point");
2251 lua_pushnumber(L, pos0->x);
2252 lua_pushnumber(L, pos0->y);
2253 lua_pushnumber(L, pos0->z);
2254 lua_pushsstring(L, *label);
2255 args = 6;
2256 }
2257 else if (type == MAPDRAW_LINE) {
2258 HSTR_PUSH(L, "line");
2259 lua_pushnumber(L, pos0->x);
2260 lua_pushnumber(L, pos0->y);
2261 lua_pushnumber(L, pos0->z);
2262 lua_pushnumber(L, pos1->x);
2263 lua_pushnumber(L, pos1->y);
2264 lua_pushnumber(L, pos1->z);
2265 args = 8;
2266 }
2267 else if (type == MAPDRAW_ERASE) {
2268 HSTR_PUSH(L, "erase");
2269 lua_pushnumber(L, pos0->x);
2270 lua_pushnumber(L, pos0->y);
2271 lua_pushnumber(L, pos0->z);
2272 lua_pushnumber(L, 100.0f); // radius
2273 args = 6;
2274 }
2275 else {
2276 LOG_L(L_WARNING, "Unknown MapDrawCmd() type: %i", type);
2277 lua_pop(L, 2); // pop the function and playerID
2278 return false;
2279 }
2280
2281 // call the routine
2282 if (!RunCallIn(L, cmdStr, args, 1))
2283 return false;
2284
2285 // take the event?
2286 const bool retval = luaL_optboolean(L, -1, false);
2287 lua_pop(L, 1);
2288 return retval;
2289 }
2290
2291
GameSetup(const string & state,bool & ready,const map<int,string> & playerStates)2292 bool CLuaHandle::GameSetup(const string& state, bool& ready,
2293 const map<int, string>& playerStates)
2294 {
2295 if (!CheckModUICtrl()) {
2296 return false;
2297 }
2298 LUA_CALL_IN_CHECK(L, false);
2299 luaL_checkstack(L, 5, __FUNCTION__);
2300 static const LuaHashString cmdStr("GameSetup");
2301 if (!cmdStr.GetGlobalFunc(L)) {
2302 return false;
2303 }
2304
2305 lua_pushsstring(L, state);
2306
2307 lua_pushboolean(L, ready);
2308
2309 lua_newtable(L);
2310 map<int, string>::const_iterator it;
2311 for (it = playerStates.begin(); it != playerStates.end(); ++it) {
2312 lua_pushsstring(L, it->second);
2313 lua_rawseti(L, -2, it->first);
2314 }
2315
2316 // call the routine
2317 if (!RunCallIn(L, cmdStr, 3, 2))
2318 return false;
2319
2320 if (lua_isboolean(L, -2)) {
2321 if (lua_toboolean(L, -2)) {
2322 // only allow ready-state change if Lua takes the event
2323 if (lua_isboolean(L, -1)) {
2324 ready = lua_toboolean(L, -1);
2325 }
2326 lua_pop(L, 2);
2327 return true;
2328 }
2329 }
2330 lua_pop(L, 2);
2331 return false;
2332 }
2333
2334
2335
RecvSkirmishAIMessage(int aiTeam,const char * inData,int inSize)2336 const char* CLuaHandle::RecvSkirmishAIMessage(int aiTeam, const char* inData, int inSize)
2337 {
2338 LUA_CALL_IN_CHECK(L, NULL);
2339 luaL_checkstack(L, 4, __FUNCTION__);
2340
2341 static const LuaHashString cmdStr("RecvSkirmishAIMessage");
2342
2343 // <this> is either CLuaRules* or CLuaUI*,
2344 // but the AI call-in is always unsynced!
2345 if (!cmdStr.GetGlobalFunc(L)) {
2346 return NULL;
2347 }
2348
2349 lua_pushnumber(L, aiTeam);
2350
2351 int argCount = 1;
2352 const char* outData = NULL;
2353
2354 if (inData != NULL) {
2355 if (inSize < 0) {
2356 inSize = strlen(inData);
2357 }
2358 lua_pushlstring(L, inData, inSize);
2359 argCount = 2;
2360 }
2361
2362 if (!RunCallIn(L, cmdStr, argCount, 1))
2363 return NULL;
2364
2365 if (lua_isstring(L, -1))
2366 outData = lua_tolstring(L, -1, NULL);
2367
2368 lua_pop(L, 1);
2369 return outData;
2370 }
2371
2372 /******************************************************************************/
2373 /******************************************************************************/
2374
2375 CONFIG(float, MaxLuaGarbageCollectionTime ).defaultValue(5.f).minimumValue(1.0f).description("in MilliSecs");
2376
2377
CollectGarbage()2378 void CLuaHandle::CollectGarbage()
2379 {
2380 lua_lock(L_GC);
2381 //SCOPED_MT_TIMER("CollectGarbage"); // this func doesn't run in parallel yet, cause of problems with IsHandleRunning()
2382
2383 // note: total footprint INCLUDING garbage
2384 int luaMemFootPrintKB = lua_gc(L_GC, LUA_GCCOUNT, 0);
2385
2386 // 30x per second !!!
2387 static const float maxLuaGarbageCollectTime = configHandler->GetFloat("MaxLuaGarbageCollectionTime");
2388 float maxRunTime = smoothstep(10, 100, luaMemFootPrintKB / 1024) * maxLuaGarbageCollectTime;
2389
2390 const spring_time startTime = spring_gettime();
2391 const spring_time endTime = startTime + spring_msecs(maxRunTime);
2392 static int gcsteps = 10;
2393 int numLuaGarbageCollectIters = 0;
2394
2395 SetHandleRunning(L_GC, true);
2396
2397 // collect garbage until time runs out
2398 while (spring_gettime() < endTime) {
2399 numLuaGarbageCollectIters++;
2400 if (!lua_gc(L_GC, LUA_GCSTEP, gcsteps))
2401 continue;
2402
2403 // garbage-collection cycle finished
2404 const int luaMemFootPrintNow = lua_gc(L_GC, LUA_GCCOUNT, 0);
2405 const int luaMemFootPrintChange = luaMemFootPrintNow - luaMemFootPrintKB;
2406 luaMemFootPrintKB = luaMemFootPrintNow;
2407
2408 // cycle didn't freed any memory early-exit
2409 if (luaMemFootPrintChange == 0)
2410 break;
2411 }
2412
2413 lua_gc(L_GC, LUA_GCSTOP, 0); // don't collect garbage outside of this function
2414 SetHandleRunning(L_GC, false);
2415 lua_unlock(L_GC);
2416
2417 const spring_time finishTime = spring_gettime();
2418
2419 if (gcsteps > 1 && numLuaGarbageCollectIters > 0) {
2420 // runtime optimize number of steps to process in a batch
2421 const float avgTimePerLoopIter = (finishTime - startTime).toMilliSecsf() / numLuaGarbageCollectIters;
2422
2423 if (avgTimePerLoopIter > (maxLuaGarbageCollectTime * 0.150f)) gcsteps--;
2424 if (avgTimePerLoopIter < (maxLuaGarbageCollectTime * 0.075f)) gcsteps++;
2425 }
2426
2427 eventHandler.DbgTimingInfo(TIMING_GC, startTime, finishTime);
2428 }
2429
2430 /******************************************************************************/
2431 /******************************************************************************/
2432
AddBasicCalls(lua_State * L)2433 bool CLuaHandle::AddBasicCalls(lua_State *L)
2434 {
2435 HSTR_PUSH(L, "Script");
2436 lua_newtable(L); {
2437 HSTR_PUSH_CFUNC(L, "Kill", KillActiveHandle);
2438 HSTR_PUSH_CFUNC(L, "UpdateCallIn", CallOutUpdateCallIn);
2439 HSTR_PUSH_CFUNC(L, "GetName", CallOutGetName);
2440 HSTR_PUSH_CFUNC(L, "GetSynced", CallOutGetSynced);
2441 HSTR_PUSH_CFUNC(L, "GetFullCtrl", CallOutGetFullCtrl);
2442 HSTR_PUSH_CFUNC(L, "GetFullRead", CallOutGetFullRead);
2443 HSTR_PUSH_CFUNC(L, "GetCtrlTeam", CallOutGetCtrlTeam);
2444 HSTR_PUSH_CFUNC(L, "GetReadTeam", CallOutGetReadTeam);
2445 HSTR_PUSH_CFUNC(L, "GetReadAllyTeam", CallOutGetReadAllyTeam);
2446 HSTR_PUSH_CFUNC(L, "GetSelectTeam", CallOutGetSelectTeam);
2447 HSTR_PUSH_CFUNC(L, "GetGlobal", CallOutGetGlobal);
2448 HSTR_PUSH_CFUNC(L, "GetRegistry", CallOutGetRegistry);
2449 HSTR_PUSH_CFUNC(L, "GetCallInList", CallOutGetCallInList);
2450 HSTR_PUSH_CFUNC(L, "IsEngineMinVersion", CallOutIsEngineMinVersion);
2451 // special team constants
2452 HSTR_PUSH_NUMBER(L, "NO_ACCESS_TEAM", CEventClient::NoAccessTeam);
2453 HSTR_PUSH_NUMBER(L, "ALL_ACCESS_TEAM", CEventClient::AllAccessTeam);
2454 }
2455 lua_rawset(L, -3);
2456
2457 // extra math utilities
2458 lua_getglobal(L, "math");
2459 LuaBitOps::PushEntries(L);
2460 LuaMathExtra::PushEntries(L);
2461 lua_pop(L, 1);
2462
2463 return true;
2464 }
2465
2466
CallOutGetName(lua_State * L)2467 int CLuaHandle::CallOutGetName(lua_State* L)
2468 {
2469 lua_pushsstring(L, GetHandle(L)->GetName());
2470 return 1;
2471 }
2472
2473
CallOutGetSynced(lua_State * L)2474 int CLuaHandle::CallOutGetSynced(lua_State* L)
2475 {
2476 lua_pushboolean(L, GetHandleSynced(L));
2477 return 1;
2478 }
2479
2480
CallOutGetFullCtrl(lua_State * L)2481 int CLuaHandle::CallOutGetFullCtrl(lua_State* L)
2482 {
2483 lua_pushboolean(L, GetHandleFullCtrl(L));
2484 return 1;
2485 }
2486
2487
CallOutGetFullRead(lua_State * L)2488 int CLuaHandle::CallOutGetFullRead(lua_State* L)
2489 {
2490 lua_pushboolean(L, GetHandleFullRead(L));
2491 return 1;
2492 }
2493
2494
CallOutGetCtrlTeam(lua_State * L)2495 int CLuaHandle::CallOutGetCtrlTeam(lua_State* L)
2496 {
2497 lua_pushnumber(L, GetHandleCtrlTeam(L));
2498 return 1;
2499 }
2500
2501
CallOutGetReadTeam(lua_State * L)2502 int CLuaHandle::CallOutGetReadTeam(lua_State* L)
2503 {
2504 lua_pushnumber(L, GetHandleReadTeam(L));
2505 return 1;
2506 }
2507
2508
CallOutGetReadAllyTeam(lua_State * L)2509 int CLuaHandle::CallOutGetReadAllyTeam(lua_State* L)
2510 {
2511 lua_pushnumber(L, GetHandleReadAllyTeam(L));
2512 return 1;
2513 }
2514
2515
CallOutGetSelectTeam(lua_State * L)2516 int CLuaHandle::CallOutGetSelectTeam(lua_State* L)
2517 {
2518 lua_pushnumber(L, GetHandleSelectTeam(L));
2519 return 1;
2520 }
2521
2522
CallOutGetGlobal(lua_State * L)2523 int CLuaHandle::CallOutGetGlobal(lua_State* L)
2524 {
2525 if (devMode) {
2526 lua_pushvalue(L, LUA_GLOBALSINDEX);
2527 return 1;
2528 }
2529 return 0;
2530 }
2531
2532
CallOutGetRegistry(lua_State * L)2533 int CLuaHandle::CallOutGetRegistry(lua_State* L)
2534 {
2535 if (devMode) {
2536 lua_pushvalue(L, LUA_REGISTRYINDEX);
2537 return 1;
2538 }
2539 return 0;
2540 }
2541
2542
CallOutIsEngineMinVersion(lua_State * L)2543 int CLuaHandle::CallOutIsEngineMinVersion(lua_State* L)
2544 {
2545 const int minMajorVer = luaL_checkint(L, 1);
2546 const int minMinorVer = luaL_optinteger(L, 2, 0);
2547 const int minCommits = luaL_optinteger(L, 3, 0);
2548
2549 if (StringToInt(SpringVersion::GetMajor()) < minMajorVer) {
2550 lua_pushboolean(L, false);
2551 return 1;
2552 }
2553
2554 if (StringToInt(SpringVersion::GetMajor()) == minMajorVer) {
2555 if (StringToInt(SpringVersion::GetMinor()) < minMinorVer) {
2556 if (GetHandleSynced(L)) { // minor is only allowed to contain unsynced changes!
2557 lua_pushboolean(L, false);
2558 return 1;
2559 }
2560 }
2561
2562 if (StringToInt(SpringVersion::GetCommits()) < minCommits) {
2563 lua_pushboolean(L, false);
2564 return 1;
2565 }
2566 }
2567
2568 lua_pushboolean(L, true);
2569 return 1;
2570 }
2571
2572
CallOutGetCallInList(lua_State * L)2573 int CLuaHandle::CallOutGetCallInList(lua_State* L)
2574 {
2575 vector<string> list;
2576 eventHandler.GetEventList(list);
2577 lua_createtable(L, 0, list.size());
2578 for (unsigned int i = 0; i < list.size(); i++) {
2579 lua_pushsstring(L, list[i]);
2580 lua_newtable(L); {
2581 lua_pushliteral(L, "unsynced");
2582 lua_pushboolean(L, eventHandler.IsUnsynced(list[i]));
2583 lua_rawset(L, -3);
2584 lua_pushliteral(L, "controller");
2585 lua_pushboolean(L, eventHandler.IsController(list[i]));
2586 lua_rawset(L, -3);
2587 }
2588 lua_rawset(L, -3);
2589 }
2590 return 1;
2591 }
2592
2593
CallOutUpdateCallIn(lua_State * L)2594 int CLuaHandle::CallOutUpdateCallIn(lua_State* L)
2595 {
2596
2597 const string name = luaL_checkstring(L, 1);
2598 CLuaHandle* lh = GetHandle(L);
2599 lh->UpdateCallIn(L, name);
2600 return 0;
2601 }
2602
2603
2604 /******************************************************************************/
2605 /******************************************************************************/
2606
2607