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