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