1 /*
2 LUA_HUD_SCRIPT.CPP
3
4 Copyright (C) 2009 by Jeremiah Morris and the Aleph One developers
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 This license is contained in the file "COPYING",
17 which is included with this source code; it is available online at
18 http://www.gnu.org/licenses/gpl.html
19
20 Implements Lua HUD state and trigger callbacks
21 */
22
23 // cseries defines HAVE_LUA on A1/SDL
24 #include "cseries.h"
25
26 #include "mouse.h"
27 #include "interface.h"
28
29 #ifdef HAVE_LUA
30 extern "C"
31 {
32 #include "lua.h"
33 #include "lauxlib.h"
34 #include "lualib.h"
35 }
36 #endif
37
38 #include <string>
39 #include <stdlib.h>
40 #include <set>
41
42
43 #include "Logging.h"
44 #include "preferences.h"
45 #include "game_errors.h"
46 #include "Plugins.h"
47
48 #include "lua_hud_script.h"
49 #include "lua_hud_objects.h"
50
51 #include <boost/shared_ptr.hpp>
52 #include <boost/iostreams/device/array.hpp>
53 #include <boost/iostreams/stream_buffer.hpp>
54 namespace io = boost::iostreams;
55
56
57 bool use_lua_hud_crosshairs;
58
59 #ifndef HAVE_LUA
60
L_Call_HUDInit()61 void L_Call_HUDInit() {}
L_Call_HUDCleanup()62 void L_Call_HUDCleanup() {}
L_Call_HUDDraw()63 void L_Call_HUDDraw() {}
L_Call_HUDResize()64 void L_Call_HUDResize() {}
65
66 #else /* HAVE_LUA */
67
68 // LP: used by several functions here
69 extern float AngleConvert;
70
71 // Steal all this stuff
72 extern bool MotionSensorActive;
73
74 extern struct view_data *world_view;
75 extern struct static_data *static_world;
76
77 static const luaL_Reg lualibs[] = {
78 {"", luaopen_base},
79 {LUA_TABLIBNAME, luaopen_table},
80 {LUA_STRLIBNAME, luaopen_string},
81 {LUA_BITLIBNAME, luaopen_bit32},
82 {LUA_MATHLIBNAME, luaopen_math},
83 {LUA_DBLIBNAME, luaopen_debug},
84 {LUA_IOLIBNAME, luaopen_io},
85 {NULL, NULL}
86 };
87
88 void* L_Persistent_Table_Key();
89
90
91 class LuaHUDState
92 {
93 public:
LuaHUDState()94 LuaHUDState() : running_(false), inited_(false), num_scripts_(0) {
95 state_.reset(luaL_newstate(), lua_close);
96 }
97
~LuaHUDState()98 virtual ~LuaHUDState() {
99 }
100
101 public:
102
103 bool Load(const char *buffer, size_t len);
Loaded()104 bool Loaded() { return num_scripts_ > 0; }
Running()105 bool Running() { return running_; }
106 bool Run();
Stop()107 void Stop() { running_ = false; }
108 void MarkCollections(std::set<short>& collections);
109
Initialize()110 virtual void Initialize() {
111 const luaL_Reg *lib = lualibs;
112 for (; lib->func; lib++)
113 {
114 luaL_requiref(State(), lib->name, lib->func, 1);
115 lua_pop(State(), 1);
116 }
117
118 RegisterFunctions();
119 }
120
SetSearchPath(const std::string & path)121 void SetSearchPath(const std::string& path) {
122 L_Set_Search_Path(State(), path);
123 }
124
125 protected:
126 bool GetTrigger(const char *trigger);
127 void CallTrigger(int numArgs = 0);
128
129 virtual void RegisterFunctions();
130
131 boost::shared_ptr<lua_State> state_;
State()132 lua_State* State() { return state_.get(); }
133
134 public:
135 // triggers
136 void Init();
137 void Draw();
138 void Resize();
139 void Cleanup();
140
141 private:
142 bool running_;
143 int num_scripts_;
144 bool inited_;
145 };
146
147 LuaHUDState *hud_state = NULL;
148
GetTrigger(const char * trigger)149 bool LuaHUDState::GetTrigger(const char* trigger)
150 {
151 if (!running_)
152 return false;
153
154 lua_getglobal(State(), "Triggers");
155 if (!lua_istable(State(), -1))
156 {
157 lua_pop(State(), 1);
158 return false;
159 }
160
161 lua_pushstring(State(), trigger);
162 lua_gettable(State(), -2);
163 if (!lua_isfunction(State(), -1))
164 {
165 lua_pop(State(), 2);
166 return false;
167 }
168
169 lua_remove(State(), -2);
170 return true;
171 }
172
CallTrigger(int numArgs)173 void LuaHUDState::CallTrigger(int numArgs)
174 {
175 if (lua_pcall(State(), numArgs, 0, 0) == LUA_ERRRUN)
176 L_Error(lua_tostring(State(), -1));
177 }
178
Init()179 void LuaHUDState::Init()
180 {
181 if (GetTrigger("init"))
182 CallTrigger();
183 inited_ = true;
184 }
185
Draw()186 void LuaHUDState::Draw()
187 {
188 if (!inited_)
189 return;
190 if (GetTrigger("draw"))
191 CallTrigger();
192 }
193
Resize()194 void LuaHUDState::Resize()
195 {
196 if (!inited_)
197 return;
198 if (GetTrigger("resize"))
199 CallTrigger();
200 }
201
Cleanup()202 void LuaHUDState::Cleanup()
203 {
204 if (!inited_)
205 return;
206 if (GetTrigger("cleanup"))
207 CallTrigger();
208 inited_ = false;
209 }
210
RegisterFunctions()211 void LuaHUDState::RegisterFunctions()
212 {
213 Lua_HUDObjects_register(State());
214 }
215
Load(const char * buffer,size_t len)216 bool LuaHUDState::Load(const char *buffer, size_t len)
217 {
218 int status = luaL_loadbufferx(State(), buffer, len, "HUD Lua", "t");
219 if (status == LUA_ERRRUN)
220 logWarning("Lua loading failed: error running script.");
221 if (status == LUA_ERRFILE)
222 logWarning("Lua loading failed: error loading file.");
223 if (status == LUA_ERRSYNTAX) {
224 logWarning("Lua loading failed: syntax error.");
225 logWarning(lua_tostring(State(), -1));
226 }
227 if (status == LUA_ERRMEM)
228 logWarning("Lua loading failed: error allocating memory.");
229 if (status == LUA_ERRERR)
230 logWarning("Lua loading failed: unknown error.");
231
232 num_scripts_ += ((status == 0) ? 1 : 0);
233 return (status == 0);
234 }
235
236
Run()237 bool LuaHUDState::Run()
238 {
239 if (!Loaded()) return false;
240
241 int result = 0;
242 // Reverse the functions we're calling
243 for (int i = 0; i < num_scripts_ - 1; ++i)
244 lua_insert(State(), -(num_scripts_ - i));
245
246 // Call 'em
247 for (int i = 0; i < num_scripts_; ++i)
248 result = result || lua_pcall(State(), 0, LUA_MULTRET, 0);
249
250 if (result == 0) running_ = true;
251 return (result == 0);
252 }
253
MarkCollections(std::set<short> & collections)254 void LuaHUDState::MarkCollections(std::set<short>& collections)
255 {
256 if (!running_)
257 return;
258
259 lua_getglobal(State(), "CollectionsUsed");
260
261 if (lua_istable(State(), -1))
262 {
263 int i = 1;
264 lua_pushnumber(State(), i++);
265 lua_gettable(State(), -2);
266 while (lua_isnumber(State(), -1))
267 {
268 short collection_index = static_cast<short>(lua_tonumber(State(), -1));
269 if (collection_index >= 0 && collection_index < NUMBER_OF_COLLECTIONS)
270 {
271 mark_collection_for_loading(collection_index);
272 collections.insert(collection_index);
273 }
274 lua_pop(State(), 1);
275 lua_pushnumber(State(), i++);
276 lua_gettable(State(), -2);
277 }
278
279 lua_pop(State(), 2);
280 }
281 else if (lua_isnumber(State(), -1))
282 {
283 short collection_index = static_cast<short>(lua_tonumber(State(), -1));
284 if (collection_index >= 0 && collection_index < NUMBER_OF_COLLECTIONS)
285 {
286 mark_collection_for_loading(collection_index);
287 collections.insert(collection_index);
288 }
289
290 lua_pop(State(), 1);
291 }
292 else
293 {
294 lua_pop(State(), 1);
295 }
296 }
297
298
LuaHUDRunning()299 bool LuaHUDRunning()
300 {
301 return (hud_state && hud_state->Running());
302 }
303
L_Call_HUDInit()304 void L_Call_HUDInit()
305 {
306 if (hud_state)
307 hud_state->Init();
308 }
309
L_Call_HUDCleanup()310 void L_Call_HUDCleanup()
311 {
312 if (hud_state)
313 hud_state->Cleanup();
314 }
315
L_Call_HUDDraw()316 void L_Call_HUDDraw()
317 {
318 if (hud_state)
319 hud_state->Draw();
320 }
321
L_Call_HUDResize()322 void L_Call_HUDResize()
323 {
324 if (hud_state)
325 hud_state->Resize();
326 }
327
328
LoadLuaHUDScript(const char * buffer,size_t len)329 bool LoadLuaHUDScript(const char *buffer, size_t len)
330 {
331 if (!hud_state)
332 {
333 hud_state = new LuaHUDState();
334 hud_state->Initialize();
335 }
336 return hud_state->Load(buffer, len);
337 }
338
SetLuaHUDScriptSearchPath(const std::string & directory)339 void SetLuaHUDScriptSearchPath(const std::string& directory)
340 {
341 hud_state->SetSearchPath(directory);
342 }
343
RunLuaHUDScript()344 bool RunLuaHUDScript()
345 {
346 use_lua_hud_crosshairs = false;
347 return (hud_state && hud_state->Run());
348 }
349
LoadHUDLua()350 void LoadHUDLua()
351 {
352 std::string file;
353 std::string directory;
354
355 const Plugin* hud_lua_plugin = Plugins::instance()->find_hud_lua();
356 if (hud_lua_plugin)
357 {
358 file = hud_lua_plugin->hud_lua;
359 directory = hud_lua_plugin->directory.GetPath();
360 }
361
362 if (file.size())
363 {
364 // protect Lua errors from harming error checking
365 short SavedType, SavedError = get_game_error(&SavedType);
366
367 FileSpecifier fs (file.c_str());
368 if (directory.size())
369 {
370 fs.SetNameWithPath(file.c_str(), directory);
371 }
372
373 OpenedFile script_file;
374 if (fs.Open(script_file))
375 {
376 int32 script_length;
377 script_file.GetLength(script_length);
378
379 std::vector<char> script_buffer(script_length);
380 if (script_file.Read(script_length, &script_buffer[0]))
381 {
382 LoadLuaHUDScript(&script_buffer[0], script_length);
383 if (directory.size())
384 {
385 SetLuaHUDScriptSearchPath(directory);
386 }
387 }
388 }
389 set_game_error(SavedType, SavedError);
390 }
391 }
392
CloseLuaHUDScript()393 void CloseLuaHUDScript()
394 {
395 delete hud_state;
396 hud_state = NULL;
397 }
398
MarkLuaHUDCollections(bool loading)399 void MarkLuaHUDCollections(bool loading)
400 {
401 static std::set<short> collections;
402 if (loading)
403 {
404 collections.clear();
405 if (hud_state)
406 hud_state->MarkCollections(collections);
407 }
408 else
409 {
410 for (std::set<short>::iterator it = collections.begin(); it != collections.end(); it++)
411 {
412 mark_collection_for_unloading(*it);
413 }
414 }
415 }
416
417
418
419 #endif /* HAVE_LUA */
420