1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "LuaRules.h"
4 
5 #include "LuaInclude.h"
6 
7 #include "LuaUtils.h"
8 #include "LuaUnitRendering.h"
9 #include "LuaCallInCheck.h"
10 
11 #include "Sim/Misc/GlobalSynced.h"
12 #include "Sim/Units/Unit.h"
13 #include "Sim/Units/UnitDef.h"
14 #include "Sim/Units/Scripts/CobInstance.h" // for UNPACK{X,Z}
15 #include "System/Log/ILog.h"
16 #include "System/FileSystem/VFSModes.h" // for SPRING_VFS_*
17 
18 #include <assert.h>
19 #include <mutex>
20 #include <boost/thread/mutex.hpp>
21 
22 CLuaRules* luaRules = NULL;
23 
24 static const char* LuaRulesSyncedFilename   = "LuaRules/main.lua";
25 static const char* LuaRulesUnsyncedFilename = "LuaRules/draw.lua";
26 
27 const int* CLuaRules::currentCobArgs = NULL;
28 
29 
30 /******************************************************************************/
31 /******************************************************************************/
32 
33 static boost::mutex m_singleton;
34 
35 
LoadHandler()36 void CLuaRules::LoadHandler()
37 {
38 	{
39 		std::lock_guard<boost::mutex> lk(m_singleton);
40 		if (luaRules) return;
41 
42 		luaRules = new CLuaRules();
43 	}
44 
45 	if (!luaRules->IsValid()) {
46 		FreeHandler();
47 	}
48 }
49 
50 
FreeHandler()51 void CLuaRules::FreeHandler()
52 {
53 	std::lock_guard<boost::mutex> lk(m_singleton);
54 	if (!luaRules) return;
55 
56 	auto* inst = luaRules;
57 	luaRules = NULL;
58 	inst->KillLua();
59 	delete inst;
60 }
61 
62 
63 /******************************************************************************/
64 /******************************************************************************/
65 
CLuaRules()66 CLuaRules::CLuaRules()
67 : CLuaHandleSynced("LuaRules", LUA_HANDLE_ORDER_RULES)
68 {
69 	if (!IsValid()) {
70 		return;
71 	}
72 
73 	SetFullCtrl(true);
74 	SetFullRead(true);
75 	SetCtrlTeam(CEventClient::AllAccessTeam);
76 	SetReadTeam(CEventClient::AllAccessTeam);
77 	SetReadAllyTeam(CEventClient::AllAccessTeam);
78 	SetSelectTeam(CEventClient::AllAccessTeam);
79 
80 	Init(LuaRulesSyncedFilename, LuaRulesUnsyncedFilename, SPRING_VFS_MOD);
81 
82 	if (!IsValid()) {
83 		return;
84 	}
85 }
86 
~CLuaRules()87 CLuaRules::~CLuaRules()
88 {
89 	luaRules = NULL;
90 }
91 
92 
93 
AddSyncedCode(lua_State * L)94 bool CLuaRules::AddSyncedCode(lua_State *L)
95 {
96 	lua_getglobal(L, "Script");
97 	LuaPushNamedCFunc(L, "PermitHelperAIs", PermitHelperAIs);
98 	lua_pop(L, 1);
99 
100 	return true;
101 }
102 
103 
AddUnsyncedCode(lua_State * L)104 bool CLuaRules::AddUnsyncedCode(lua_State *L)
105 {
106 	lua_getglobal(L, "Spring");
107 	lua_pushliteral(L, "UnitRendering");
108 	lua_newtable(L);
109 	LuaUnitRendering::PushEntries(L);
110 	lua_rawset(L, -3);
111 	lua_pop(L, 1); // Spring
112 
113 	return true;
114 }
115 
116 
117 /******************************************************************************/
118 /******************************************************************************/
119 
UnpackCobArg(lua_State * L)120 int CLuaRules::UnpackCobArg(lua_State* L)
121 {
122 	if (currentCobArgs == NULL) {
123 		luaL_error(L, "Error in UnpackCobArg(), no current args");
124 	}
125 	const int arg = luaL_checkint(L, 1) - 1;
126 	if ((arg < 0) || (arg >= MAX_LUA_COB_ARGS)) {
127 		luaL_error(L, "Error in UnpackCobArg(), bad index");
128 	}
129 	const int value = currentCobArgs[arg];
130 	lua_pushnumber(L, UNPACKX(value));
131 	lua_pushnumber(L, UNPACKZ(value));
132 	return 2;
133 }
134 
135 
Cob2Lua(const LuaHashString & name,const CUnit * unit,int & argsCount,int args[MAX_LUA_COB_ARGS])136 void CLuaRules::Cob2Lua(const LuaHashString& name, const CUnit* unit,
137                         int& argsCount, int args[MAX_LUA_COB_ARGS])
138 {
139 	static int callDepth = 0;
140 	if (callDepth >= 16) {
141 		LOG_L(L_WARNING, "CLuaRules::Cob2Lua() call overflow: %s",
142 				name.GetString().c_str());
143 		args[0] = 0; // failure
144 		return;
145 	}
146 
147 	auto L = syncedLuaHandle.L;
148 
149 	LUA_CALL_IN_CHECK(L);
150 
151 	const int top = lua_gettop(L);
152 
153 	if (!lua_checkstack(L, 1 + 3 + argsCount)) {
154 		LOG_L(L_WARNING, "CLuaRules::Cob2Lua() lua_checkstack() error: %s",
155 				name.GetString().c_str());
156 		args[0] = 0; // failure
157 		lua_settop(L, top);
158 		return;
159 	}
160 
161 	if (!name.GetGlobalFunc(L)) {
162 		LOG_L(L_WARNING, "CLuaRules::Cob2Lua() missing function: %s",
163 				name.GetString().c_str());
164 		args[0] = 0; // failure
165 		lua_settop(L, top);
166 		return;
167 	}
168 
169 	lua_pushnumber(L, unit->id);
170 	lua_pushnumber(L, unit->unitDef->id);
171 	lua_pushnumber(L, unit->team);
172 	for (int a = 0; a < argsCount; a++) {
173 		lua_pushnumber(L, args[a]);
174 	}
175 
176 	// call the routine
177 	callDepth++;
178 	const int* oldArgs = currentCobArgs;
179 	currentCobArgs = args;
180 
181 	const bool error = !syncedLuaHandle.RunCallIn(L, name, 3 + argsCount, LUA_MULTRET);
182 
183 	currentCobArgs = oldArgs;
184 	callDepth--;
185 
186 	// bail on error
187 	if (error) {
188 		args[0] = 0; // failure
189 		lua_settop(L, top);
190 		return;
191 	}
192 
193 	// get the results
194 	const int retArgs = std::min(lua_gettop(L) - top, (MAX_LUA_COB_ARGS - 1));
195 	for (int a = 1; a <= retArgs; a++) {
196 		const int index = (a + top);
197 		if (lua_isnumber(L, index)) {
198 			args[a] = lua_toint(L, index);
199 		}
200 		else if (lua_isboolean(L, index)) {
201 			args[a] = lua_toboolean(L, index) ? 1 : 0;
202 		}
203 		else if (lua_istable(L, index)) {
204 			lua_rawgeti(L, index, 1);
205 			lua_rawgeti(L, index, 2);
206 			if (lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
207 				const int x = lua_toint(L, -2);
208 				const int z = lua_toint(L, -1);
209 				args[a] = PACKXZ(x, z);
210 			} else {
211 				args[a] = 0;
212 			}
213 			lua_pop(L, 2);
214 		}
215 		else {
216 			args[a] = 0;
217 		}
218 	}
219 
220 	args[0] = 1; // success
221 	lua_settop(L, top);
222 	return;
223 }
224 
225 
226 /******************************************************************************/
227 /******************************************************************************/
228 //
229 // LuaRules Call-Outs
230 //
231 
PermitHelperAIs(lua_State * L)232 int CLuaRules::PermitHelperAIs(lua_State* L)
233 {
234 	if (!lua_isboolean(L, 1)) {
235 		luaL_error(L, "Incorrect argument to PermitHelperAIs()");
236 	}
237 	gs->noHelperAIs = !lua_toboolean(L, 1);
238 	LOG("LuaRules has %s helper AIs",
239 			(gs->noHelperAIs ? "disabled" : "enabled"));
240 	return 0;
241 }
242 
243 /******************************************************************************/
244 /******************************************************************************/
245