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