The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
application_lua_kernel.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2016 by Chris Beck <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Provides a Lua interpreter, to drive the game_controller.
18  *
19  * @note Naming conventions:
20  * - intf_ functions are exported in the wesnoth domain,
21  * - impl_ functions are hidden inside metatables,
22  * - cfun_ functions are closures,
23  * - luaW_ functions are helpers in Lua style.
24  */
25 
27 
28 #include "global.hpp"
29 
30 #include "config.hpp"
31 #include "game_errors.hpp"
32 #include "log.hpp"
33 #include "scripting/lua_api.hpp"
34 #include "scripting/lua_common.hpp"
38 #include "scripting/lua_types.hpp"
41 
42 #ifdef DEBUG_LUA
43 #include "scripting/debug_lua.hpp"
44 #endif
45 
46 #include <map>
47 #include <sstream>
48 #include <string>
49 #include <utility>
50 
51 #include "utils/functional.hpp"
52 #include <boost/make_shared.hpp>
53 #include <boost/noncopyable.hpp>
54 #include <boost/range/adaptors.hpp>
55 #include <boost/shared_ptr.hpp>
56 
57 #include "lua/lauxlib.h"
58 #include "lua/lua.h"
59 #include "lua/luaconf.h"
60 
61 class CVideo;
62 struct lua_State;
63 
64 static lg::log_domain log_scripting_lua("scripting/lua");
65 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
66 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
67 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
68 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
69 
71 {
72  std::cerr << "describe plugins (" << plugins_manager::get()->size() << "):\n";
73  lua_getglobal(L, "print");
74  for (size_t i = 0; i < plugins_manager::get()->size(); ++i) {
75  lua_pushvalue(L,-1); //duplicate the print
76 
77  std::stringstream line;
78  line << i
79  << ":\t"
81  << "\t\t"
83  << "\n";
84 
85  DBG_LUA << line.str();
86 
87  lua_pushstring(L, line.str().c_str());
88  lua_call(L, 1, 0);
89  }
90  if (!plugins_manager::get()->size()) {
91  lua_pushstring(L, "No plugins available.\n");
92  lua_call(L, 1, 0);
93  }
94  return 0;
95 }
96 
98  : lua_kernel_base(ptr)
99 {
101  lua_setglobal(mState, "describe_plugins");
102  lua_settop(mState, 0);
103 }
104 
105 application_lua_kernel::thread::thread(lua_State * T) : T_(T), started_(false) {}
106 
108 {
109  if (!started_) {
110  if (lua_status(T_) == LUA_OK) {
111  return "not started";
112  } else {
113  return "load error";
114  }
115  }
116  switch (lua_status(T_)) {
117  case LUA_OK:
118  return "dead";
119  case LUA_YIELD:
120  return "started";
121  default:
122  return "error";
123  }
124 }
125 
127  return started_ ? (lua_status(T_) == LUA_YIELD) : (lua_status(T_) == LUA_OK);
128 }
129 
130 static char * v_threadtableKey = 0;
131 static void * const threadtableKey = static_cast<void *> (& v_threadtableKey);
132 
134 {
136  lua_pushvalue(L,1); // duplicate script key, since we need to store later
137  // stack is now [script key] [script key]
138 
139  lua_rawget(L, LUA_REGISTRYINDEX); // get the script table from the registry, on the top of the stack
140  if (!lua_istable(L,-1)) { // if it doesn't exist create it
141  lua_pop(L,1);
142  lua_newtable(L);
143  } // stack is now [script key] [table]
144 
145  lua_pushinteger(L, lua_objlen(L, -1) + 1); // push #table + 1 onto the stack
146 
147  lua_State * T = lua_newthread(L); // create new thread T
148  // stack is now [script key] [table] [#table + 1] [thread]
149  lua_rawset(L, -3); // store the new thread at #table +1 index of the table.
150  // stack is now [script key] [table]
152  // stack L is now empty
153  return T; // now we can set up T's stack appropriately
154 }
155 
157 {
159  // now we are operating on T's stack, leaving a compiled C function on it.
160 
161  DBG_LUA << "created thread: status = " << lua_status(T) << (lua_status(T) == LUA_OK ? " == OK" : " == ?") << "\n";
162  DBG_LUA << "loading script from string:\n<<\n" << prog << "\n>>\n";
163 
164  int errcode = luaL_loadstring(T, prog.c_str());
165  if (errcode != LUA_OK) {
166  const char * err_str = lua_tostring(T, -1);
167  std::string msg = err_str ? err_str : "null string";
168 
169  std::string context = "When parsing a string to a lua thread, ";
170 
171  if (errcode == LUA_ERRSYNTAX) {
172  context += " a syntax error";
173  } else if(errcode == LUA_ERRMEM){
174  context += " a memory error";
175  } else if(errcode == LUA_ERRGCMM) {
176  context += " an error in garbage collection metamethod";
177  } else {
178  context += " an unknown error";
179  }
180 
181  throw game::lua_error(msg, context);
182  }
183  if (!lua_kernel_base::protected_call(T, 0, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
184  throw game::lua_error("Error when executing a script to make a lua thread.");
185  }
186  if (!lua_isfunction(T, -1)) {
187  throw game::lua_error(std::string("Error when executing a script to make a lua thread -- function was not produced, found a ") + lua_typename(T, lua_type(T, -1)) );
188  }
189 
190  return new application_lua_kernel::thread(T);
191 }
192 
194 {
196  // now we are operating on T's stack, leaving a compiled C function on it.
197 
198  lua_pushstring(T, file.c_str());
200  if (!lua_kernel_base::protected_call(T, 0, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
201  throw game::lua_error("Error when executing a file to make a lua thread.");
202  }
203  if (!lua_isfunction(T, -1)) {
204  throw game::lua_error(std::string("Error when executing a file to make a lua thread -- function was not produced, found a ") + lua_typename(T, lua_type(T, -1)) );
205  }
206 
207  return new application_lua_kernel::thread(T);
208 }
209 
211  std::vector<plugins_manager::event> requests;
212  bool valid;
213 
215  : requests()
216  , valid(true)
217  {}
218 };
219 
221 {
222  if (!backend->valid) {
223  luaL_error(L , "Error, you tried to use an invalid context object in a lua thread");
224  }
225 
227  evt.name = req_name;
228  evt.data = luaW_checkconfig(L, -1);
229 
230  backend->requests.push_back(evt);
231  return 0;
232 }
233 
235 {
236  if (!backend->valid) {
237  luaL_error(L , "Error, you tried to use an invalid context object in a lua thread");
238  }
239 
240  if(lua_gettop(L)) {
241  config temp;
242  if(!luaW_toconfig(L, 1, temp)) {
243  luaL_argerror(L, 1, "Error, tried to parse a config but some fields were invalid");
244  }
245  luaW_pushconfig(L, func(temp));
246  return 1;
247  } else {
248  luaW_pushconfig(L, func(config()));
249  return 1;
250  }
251 }
252 
253 application_lua_kernel::request_list application_lua_kernel::thread::run_script(const plugins_context & ctxt, const std::vector<plugins_manager::event> & queue)
254 {
255  // There are two possibilities: (1) this is the first execution, and the C function is the only thing on the stack
256  // (2) this is a subsequent execution, and there is nothing on the stack.
257  // Either way we push the arguments to the function and call resume.
258 
259  // First we have to create the event table, by concatenating the event queue into a table.
260  lua_newtable(T_); //this will be the event table
261  for (size_t i = 0; i < queue.size(); ++i) {
262  lua_newtable(T_);
263  lua_pushstring(T_, queue[i].name.c_str());
264  lua_rawseti(T_, -2, 1);
265  luaW_pushconfig(T_, queue[i].data);
266  lua_rawseti(T_, -2, 2);
267  lua_rawseti(T_, -2, i+1);
268  }
269 
270  // Now we have to create the context object. It is arranged as a table of boost functions.
271  boost::shared_ptr<lua_context_backend> this_context_backend = boost::make_shared<lua_context_backend> (lua_context_backend());
272  lua_newtable(T_); // this will be the context table
273  for (const std::string & key : ctxt.callbacks_ | boost::adaptors::map_keys ) {
274  lua_pushstring(T_, key.c_str());
275  lua_cpp::push_function(T_, std::bind(&impl_context_backend, _1, this_context_backend, key));
276  lua_settable(T_, -3);
277  }
278 
279  // Now we have to create the info object (context accessors). It is arranged as a table of boost functions.
280  lua_newtable(T_); // this will be the info table
281  lua_pushstring(T_, "name");
282  lua_pushstring(T_, ctxt.name_.c_str());
283  lua_settable(T_, -3);
284  for (const plugins_context::accessor_list::value_type & v : ctxt.accessors_) {
285  const std::string & key = v.first;
286  const plugins_context::accessor_function & func = v.second;
287  lua_pushstring(T_, key.c_str());
288  lua_cpp::push_function(T_, std::bind(&impl_context_accessor, _1, this_context_backend, func));
289  lua_settable(T_, -3);
290  }
291 
292  // Now we resume the function, calling the coroutine with the three arguments (events, context, info).
293  lua_resume(T_, nullptr, 3);
294 
295  started_ = true;
296 
297  this_context_backend->valid = false; //invalidate the context object for lua
298 
299  if (lua_status(T_) != LUA_YIELD) {
300  LOG_LUA << "Thread status = '" << lua_status(T_) << "'\n";
301  if (lua_status(T_) != LUA_OK) {
302  std::stringstream ss;
303  ss << "encountered a";
304  switch(lua_status(T_)) {
305  case LUA_ERRSYNTAX:
306  ss << " syntax ";
307  break;
308  case LUA_ERRRUN:
309  ss << " runtime ";
310  break;
311  case LUA_ERRERR:
312  ss << " error-handler ";
313  break;
314  default:
315  ss << " ";
316  break;
317  }
318  ss << "error:\n" << lua_tostring(T_, -1) << "\n";
319  ERR_LUA << ss.str() << std::endl;
320  }
321  }
322 
324 
325  for (const plugins_manager::event & req : this_context_backend->requests) {
326  results.push_back(std::bind(ctxt.callbacks_.find(req.name)->second, req.data));
327  //results.push_back(std::make_pair(ctxt.callbacks_.find(req.name)->second, req.data));
328  }
329  return results;
330 }
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
Definition: lapi.cpp:579
#define lua_pushcfunction(L, f)
Definition: lua.h:328
callback_list callbacks_
Definition: context.hpp:80
static void *const threadtableKey
LUA_API void lua_settop(lua_State *L, int idx)
Definition: lapi.cpp:159
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:243
LUALIB_API int luaL_loadstring(lua_State *L, const char *s)
Definition: lauxlib.cpp:693
Definition: video.hpp:58
LUA_API void lua_getglobal(lua_State *L, const char *var)
Definition: lapi.cpp:602
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:154
LUA_API void lua_settable(lua_State *L, int idx)
Definition: lapi.cpp:741
STATUS get_status(size_t idx)
Definition: manager.cpp:75
#define LOG_LUA
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
thread * load_script_from_string(const std::string &)
Definitions for the interface to Wesnoth Markup Language (WML).
void push_function(lua_State *L, const lua_function &f)
Pushes a std::function wrapper object onto the stack.
#define lua_pop(L, n)
Definition: lua.h:322
static lg::log_domain log_scripting_lua("scripting/lua")
LUA_API void lua_setglobal(lua_State *L, const char *var)
Definition: lapi.cpp:728
#define lua_objlen(L, i)
Definition: luaconf.h:305
std::vector< std::function< bool(void)> > request_list
#define ERR_LUA
#define LUA_YIELD
Definition: lua.h:45
static char * v_threadtableKey
std::vector< plugins_manager::event > requests
const GLdouble * v
Definition: glew.h:1359
static lua_State * get_new_thread(lua_State *L)
bool protected_call(int nArgs, int nRets, error_handler)
LUA_API int lua_status(lua_State *L)
Definition: lapi.cpp:1006
#define lua_newtable(L)
Definition: lua.h:324
#define DBG_LUA
#define LUA_ERRGCMM
Definition: lua.h:49
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:675
#define LUA_ERRERR
Definition: lua.h:50
LUA_API void lua_rawset(lua_State *L, int idx)
Definition: lapi.cpp:764
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:749
std::string name
Definition: manager.hpp:68
accessor_list accessors_
Definition: context.hpp:81
#define lua_isfunction(L, n)
Definition: lua.h:330
LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *extramsg)
Definition: lauxlib.cpp:152
size_t i
Definition: function.cpp:1057
#define LUA_ERRSYNTAX
Definition: lua.h:47
#define lua_tostring(L, i)
Definition: lua.h:345
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
LUA_API void lua_rawseti(lua_State *L, int idx, int n)
Definition: lapi.cpp:778
static int impl_context_backend(lua_State *L, boost::shared_ptr< lua_context_backend > backend, std::string req_name)
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:229
#define LUA_ERRMEM
Definition: lua.h:48
int load_file(lua_State *L)
Loads a Lua file and pushes the contents on the stack.
std::function< config(config)> accessor_function
Definition: context.hpp:37
LUA_API int lua_resume(lua_State *L, lua_State *from, int nargs)
Definition: ldo.cpp:557
virtual void log_error(char const *msg, char const *context="Lua error")
#define lua_call(L, n, r)
Definition: lua.h:252
LUALIB_API int luaL_error(lua_State *L, const char *fmt,...)
Definition: lauxlib.cpp:198
std::string get_name(size_t idx)
Definition: manager.cpp:97
GLuint const GLchar * name
Definition: glew.h:1782
GLsizeiptr size
Definition: glew.h:1649
#define LUA_REGISTRYINDEX
Definition: lua.h:39
LUA_API int lua_error(lua_State *L)
Definition: lapi.cpp:1099
#define LUA_OK
Definition: lua.h:44
#define lua_istable(L, n)
Definition: lua.h:331
void luaW_pushconfig(lua_State *L, config const &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:663
request_list run_script(const plugins_context &ctxt, const std::vector< plugins_manager::event > &queue)
Standard logging facilities (interface).
static int impl_context_accessor(lua_State *L, boost::shared_ptr< lua_context_backend > backend, plugins_context::accessor_function func)
LUA_API lua_State * lua_newthread(lua_State *L)
Definition: lstate.cpp:236
static int intf_describe_plugins(lua_State *L)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
std::string name_
Definition: context.hpp:82
lua_State * mState
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:477
LUA_API void lua_rawget(lua_State *L, int idx)
Definition: lapi.cpp:633
GLsizei const GLcharARB ** string
Definition: glew.h:4503
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:507
#define LUA_ERRRUN
Definition: lua.h:46
thread * load_script_from_file(const std::string &)
LUA_API const char * lua_typename(lua_State *L, int t)
Definition: lapi.cpp:249
static plugins_manager * get()
Definition: manager.cpp:61