The Battle for Wesnoth  1.15.2+dev
application_lua_kernel.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project https://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 "config.hpp"
29 #include "game_errors.hpp"
30 #include "log.hpp"
31 #include "scripting/lua_common.hpp"
38 
39 #ifdef DEBUG_LUA
40 #include "scripting/debug_lua.hpp"
41 #endif
42 
43 #include <map>
44 #include <sstream>
45 #include <string>
46 #include <utility>
47 
48 #include "utils/functional.hpp"
49 #include <boost/range/adaptors.hpp>
50 #include <SDL2/SDL.h>
51 
52 #include "lua/lauxlib.h"
53 #include "lua/lua.h"
54 #include "lua/luaconf.h"
55 
56 struct lua_State;
57 
58 static lg::log_domain log_scripting_lua("scripting/lua");
59 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
60 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
61 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
62 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
63 
65 {
66  std::cerr << "describe plugins (" << plugins_manager::get()->size() << "):\n";
67  lua_getglobal(L, "print");
68  for (std::size_t i = 0; i < plugins_manager::get()->size(); ++i) {
69  lua_pushvalue(L,-1); //duplicate the print
70 
71  std::stringstream line;
72  line << i
73  << ":\t"
74  << plugins_manager::get()->get_status(i)
75  << "\t\t"
77  << "\n";
78 
79  DBG_LUA << line.str();
80 
81  lua_pushstring(L, line.str().c_str());
82  lua_call(L, 1, 0);
83  }
84  if (!plugins_manager::get()->size()) {
85  lua_pushstring(L, "No plugins available.\n");
86  lua_call(L, 1, 0);
87  }
88  return 0;
89 }
90 
91 static int intf_delay(lua_State* L)
92 {
93  unsigned int delay = static_cast<unsigned int>(luaL_checkinteger(L, 1));
94  SDL_Delay(delay);
95  return 0;
96 }
97 
99  : lua_kernel_base()
100 {
101  lua_getglobal(mState, "wesnoth");
103  lua_setfield(mState, -2, "delay");
104 
105  lua_settop(mState, 0);
106 
108  lua_setglobal(mState, "describe_plugins");
109  lua_settop(mState, 0);
110 
111  // Create the preferences table.
113 }
114 
115 application_lua_kernel::thread::thread(lua_State * T) : T_(T), started_(false) {}
116 
118 {
119  if (!started_) {
120  if (lua_status(T_) == LUA_OK) {
121  return "not started";
122  } else {
123  return "load error";
124  }
125  }
126  switch (lua_status(T_)) {
127  case LUA_OK:
128  return "dead";
129  case LUA_YIELD:
130  return "started";
131  default:
132  return "error";
133  }
134 }
135 
137  return started_ ? (lua_status(T_) == LUA_YIELD) : (lua_status(T_) == LUA_OK);
138 }
139 
140 static char * v_threadtableKey = 0;
141 static void * const threadtableKey = static_cast<void *> (& v_threadtableKey);
142 
144 {
146  lua_pushvalue(L,1); // duplicate script key, since we need to store later
147  // stack is now [script key] [script key]
148 
149  lua_rawget(L, LUA_REGISTRYINDEX); // get the script table from the registry, on the top of the stack
150  if (!lua_istable(L,-1)) { // if it doesn't exist create it
151  lua_pop(L,1);
152  lua_newtable(L);
153  } // stack is now [script key] [table]
154 
155  lua_pushinteger(L, lua_rawlen(L, -1) + 1); // push #table + 1 onto the stack
156 
157  lua_State * T = lua_newthread(L); // create new thread T
158  // stack is now [script key] [table] [#table + 1] [thread]
159  lua_rawset(L, -3); // store the new thread at #table +1 index of the table.
160  // stack is now [script key] [table]
162  // stack L is now empty
163  return T; // now we can set up T's stack appropriately
164 }
165 
167 {
169  // now we are operating on T's stack, leaving a compiled C function on it.
170 
171  DBG_LUA << "created thread: status = " << lua_status(T) << (lua_status(T) == LUA_OK ? " == OK" : " == ?") << "\n";
172  DBG_LUA << "loading script from string:\n<<\n" << prog << "\n>>\n";
173 
174  // note: this is unsafe for umc as it allows loading lua baytecode, but umc cannot add application lua kernel scipts.
175  int errcode = luaL_loadstring(T, prog.c_str());
176  if (errcode != LUA_OK) {
177  const char * err_str = lua_tostring(T, -1);
178  std::string msg = err_str ? err_str : "null string";
179 
180  std::string context = "When parsing a string to a lua thread, ";
181 
182  if (errcode == LUA_ERRSYNTAX) {
183  context += " a syntax error";
184  } else if(errcode == LUA_ERRMEM){
185  context += " a memory error";
186  } else if(errcode == LUA_ERRGCMM) {
187  context += " an error in garbage collection metamethod";
188  } else {
189  context += " an unknown error";
190  }
191 
192  throw game::lua_error(msg, context);
193  }
194  if (!lua_kernel_base::protected_call(T, 0, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
195  throw game::lua_error("Error when executing a script to make a lua thread.");
196  }
197  if (!lua_isfunction(T, -1)) {
198  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)) );
199  }
200 
201  return new application_lua_kernel::thread(T);
202 }
203 
205 {
207  // now we are operating on T's stack, leaving a compiled C function on it.
208 
209  lua_pushstring(T, file.c_str());
211  if (!lua_kernel_base::protected_call(T, 0, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) {
212  throw game::lua_error("Error when executing a file to make a lua thread.");
213  }
214  if (!lua_isfunction(T, -1)) {
215  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)) );
216  }
217 
218  return new application_lua_kernel::thread(T);
219 }
220 
222  std::vector<plugins_manager::event> requests;
223  bool valid;
224 
226  : requests()
227  , valid(true)
228  {}
229 };
230 
231 static int impl_context_backend(lua_State * L, std::shared_ptr<lua_context_backend> backend, std::string req_name)
232 {
233  if (!backend->valid) {
234  luaL_error(L , "Error, you tried to use an invalid context object in a lua thread");
235  }
236 
238  evt.name = req_name;
239  evt.data = luaW_checkconfig(L, -1);
240 
241  backend->requests.push_back(evt);
242  return 0;
243 }
244 
245 static int impl_context_accessor(lua_State * L, std::shared_ptr<lua_context_backend> backend, plugins_context::accessor_function func)
246 {
247  if (!backend->valid) {
248  luaL_error(L , "Error, you tried to use an invalid context object in a lua thread");
249  }
250 
251  if(lua_gettop(L)) {
252  config temp;
253  if(!luaW_toconfig(L, 1, temp)) {
254  luaL_argerror(L, 1, "Error, tried to parse a config but some fields were invalid");
255  }
256  luaW_pushconfig(L, func(temp));
257  return 1;
258  } else {
259  luaW_pushconfig(L, func(config()));
260  return 1;
261  }
262 }
263 
264 application_lua_kernel::request_list application_lua_kernel::thread::run_script(const plugins_context & ctxt, const std::vector<plugins_manager::event> & queue)
265 {
266  // There are two possibilities: (1) this is the first execution, and the C function is the only thing on the stack
267  // (2) this is a subsequent execution, and there is nothing on the stack.
268  // Either way we push the arguments to the function and call resume.
269 
270  // First we have to create the event table, by concatenating the event queue into a table.
271  lua_newtable(T_); //this will be the event table
272  for (std::size_t i = 0; i < queue.size(); ++i) {
273  lua_newtable(T_);
274  lua_pushstring(T_, queue[i].name.c_str());
275  lua_rawseti(T_, -2, 1);
276  luaW_pushconfig(T_, queue[i].data);
277  lua_rawseti(T_, -2, 2);
278  lua_rawseti(T_, -2, i+1);
279  }
280 
281  // Now we have to create the context object. It is arranged as a table of boost functions.
282  std::shared_ptr<lua_context_backend> this_context_backend = std::make_shared<lua_context_backend> (lua_context_backend());
283  lua_newtable(T_); // this will be the context table
284  for (const std::string & key : ctxt.callbacks_ | boost::adaptors::map_keys ) {
285  lua_pushstring(T_, key.c_str());
286  lua_cpp::push_function(T_, std::bind(&impl_context_backend, _1, this_context_backend, key));
287  lua_settable(T_, -3);
288  }
289 
290  // Now we have to create the info object (context accessors). It is arranged as a table of boost functions.
291  lua_newtable(T_); // this will be the info table
292  lua_pushstring(T_, "name");
293  lua_pushstring(T_, ctxt.name_.c_str());
294  lua_settable(T_, -3);
295  for (const plugins_context::accessor_list::value_type & v : ctxt.accessors_) {
296  const std::string & key = v.first;
297  const plugins_context::accessor_function & func = v.second;
298  lua_pushstring(T_, key.c_str());
299  lua_cpp::push_function(T_, std::bind(&impl_context_accessor, _1, this_context_backend, func));
300  lua_settable(T_, -3);
301  }
302 
303  // Now we resume the function, calling the coroutine with the three arguments (events, context, info).
304  lua_resume(T_, nullptr, 3);
305 
306  started_ = true;
307 
308  this_context_backend->valid = false; //invalidate the context object for lua
309 
310  if (lua_status(T_) != LUA_YIELD) {
311  LOG_LUA << "Thread status = '" << lua_status(T_) << "'\n";
312  if (lua_status(T_) != LUA_OK) {
313  std::stringstream ss;
314  ss << "encountered a";
315  switch(lua_status(T_)) {
316  case LUA_ERRSYNTAX:
317  ss << " syntax ";
318  break;
319  case LUA_ERRRUN:
320  ss << " runtime ";
321  break;
322  case LUA_ERRERR:
323  ss << " error-handler ";
324  break;
325  default:
326  ss << " ";
327  break;
328  }
329  ss << "error:\n" << lua_tostring(T_, -1) << "\n";
330  ERR_LUA << ss.str() << std::endl;
331  }
332  }
333 
335 
336  for (const plugins_manager::event & req : this_context_backend->requests) {
337  results.push_back(std::bind(ctxt.callbacks_.find(req.name)->second, req.data));
338  //results.emplace_back(ctxt.callbacks_.find(req.name)->second, req.data);
339  }
340  return results;
341 }
LUA_API void lua_pushlightuserdata(lua_State *L, void *p)
Definition: lapi.cpp:565
#define lua_pushcfunction(L, f)
Definition: lua.h:350
thread(const thread &)=delete
callback_list callbacks_
Definition: context.hpp:66
static void *const threadtableKey
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:723
LUA_API void lua_settop(lua_State *L, int idx)
Definition: lapi.cpp:172
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:251
LUALIB_API int luaL_loadstring(lua_State *L, const char *s)
Definition: lauxlib.cpp:764
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:817
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:167
LUA_API int lua_getglobal(lua_State *L, const char *name)
Definition: lapi.cpp:605
LUA_API void lua_settable(lua_State *L, int idx)
Definition: lapi.cpp:766
static int impl_context_backend(lua_State *L, std::shared_ptr< lua_context_backend > backend, std::string req_name)
LUA_API int lua_rawget(lua_State *L, int idx)
Definition: lapi.cpp:647
static int impl_context_accessor(lua_State *L, std::shared_ptr< lua_context_backend > backend, plugins_context::accessor_function func)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
#define LOG_LUA
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:344
static lg::log_domain log_scripting_lua("scripting/lua")
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
static int intf_delay(lua_State *L)
std::string get_name(std::size_t idx)
Definition: manager.cpp:94
std::vector< std::function< bool(void)> > request_list
#define ERR_LUA
#define LUA_YIELD
Definition: lua.h:48
static char * v_threadtableKey
std::vector< plugins_manager::event > requests
static lua_State * get_new_thread(lua_State *L)
bool protected_call(int nArgs, int nRets, error_handler)
LUA_API void lua_setglobal(lua_State *L, const char *name)
Definition: lapi.cpp:759
LUA_API int lua_status(lua_State *L)
Definition: lapi.cpp:1031
#define lua_newtable(L)
Definition: lua.h:346
#define DBG_LUA
#define LUA_ERRGCMM
Definition: lua.h:52
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:735
#define LUA_ERRERR
Definition: lua.h:53
std::size_t i
Definition: function.cpp:933
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
LUA_API void lua_rawset(lua_State *L, int idx)
Definition: lapi.cpp:801
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:810
std::string name
Definition: manager.hpp:65
accessor_list accessors_
Definition: context.hpp:67
#define lua_isfunction(L, n)
Definition: lua.h:352
#define LUA_ERRSYNTAX
Definition: lua.h:50
#define lua_tostring(L, i)
Definition: lua.h:366
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
#define LUA_ERRMEM
Definition: lua.h:51
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:36
LUA_API int lua_resume(lua_State *L, lua_State *from, int nargs)
Definition: ldo.cpp:648
virtual void log_error(char const *msg, char const *context="Lua error")
Error reporting mechanisms, used by virtual methods protected_call and load_string.
#define lua_call(L, n, r)
Definition: lua.h:274
LUALIB_API int luaL_error(lua_State *L, const char *fmt,...)
Definition: lauxlib.cpp:223
std::string register_table(lua_State *L)
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:392
command_log cmd_log_
#define LUA_REGISTRYINDEX
Definition: lua.h:42
LUA_API int lua_error(lua_State *L)
Definition: lapi.cpp:1114
#define LUA_OK
Definition: lua.h:47
#define lua_istable(L, n)
Definition: lua.h:353
request_list run_script(const plugins_context &ctxt, const std::vector< plugins_manager::event > &queue)
Standard logging facilities (interface).
LUA_API lua_State * lua_newthread(lua_State *L)
Definition: lstate.cpp:255
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:68
std::string name_
Definition: context.hpp:68
lua_State * mState
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
#define LUA_ERRRUN
Definition: lua.h:49
thread * load_script_from_file(const std::string &)
LUA_API const char * lua_typename(lua_State *L, int t)
Definition: lapi.cpp:257
static plugins_manager * get()
Definition: manager.cpp:58