The Battle for Wesnoth  1.19.2+dev
lua_kernel_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
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 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
18 #include "game_config.hpp"
19 #include "game_errors.hpp"
20 #include "gui/core/gui_definition.hpp" // for remove_single_widget_definition
21 #include "log.hpp"
22 #include "lua_jailbreak_exception.hpp" // for lua_jailbreak_exception
23 #include "seed_rng.hpp"
24 #include "deprecation.hpp"
25 #include "language.hpp" // for get_language
26 #include "team.hpp" // for shroud_map
27 
28 #ifdef DEBUG_LUA
29 #include "scripting/debug_lua.hpp"
30 #endif
31 
32 #include "scripting/lua_color.hpp"
33 #include "scripting/lua_common.hpp"
37 #include "scripting/lua_gui2.hpp"
38 #include "scripting/lua_wml.hpp"
41 #include "scripting/lua_mathx.hpp"
42 #include "scripting/lua_rng.hpp"
43 #include "scripting/lua_widget.hpp"
44 #include "scripting/push_check.hpp"
45 
46 #include "game_version.hpp" // for do_version_check, etc
47 
48 #include <functional>
49 #include "utils/name_generator.hpp"
52 #include "utils/scope_exit.hpp"
53 
54 #include <cstring>
55 #include <string>
56 #include <sstream>
57 #include <vector>
58 #include <numeric>
59 
60 #include "lua/wrapper_lualib.h"
61 
62 static lg::log_domain log_scripting_lua("scripting/lua");
63 static lg::log_domain log_user("scripting/lua/user");
64 #define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
65 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
66 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
67 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
68 
69 // Registry key for metatable
70 static const char * Gen = "name generator";
71 static const char * Version = "version";
72 // Registry key for lua interpreter environment
73 static const char * Interp = "lua interpreter";
74 
75 // Callback implementations
76 
77 /**
78  * Compares two versions.
79  * - Args 1,2: version strings
80  * - Ret 1: comparison result
81  */
82 template<VERSION_COMP_OP vop>
83 static int impl_version_compare(lua_State* L)
84 {
85  version_info& v1 = *static_cast<version_info*>(luaL_checkudata(L, 1, Version));
86  version_info& v2 = *static_cast<version_info*>(luaL_checkudata(L, 2, Version));
87  const bool result = do_version_check(v1, vop, v2);
88  lua_pushboolean(L, result);
89  return 1;
90 }
91 
92 /**
93  * Decomposes a version into its component parts
94  */
95 static int impl_version_get(lua_State* L)
96 {
97  version_info& vers = *static_cast<version_info*>(luaL_checkudata(L, 1, Version));
98  if(lua_isinteger(L, 2)) {
99  int n = lua_tointeger(L, 2) - 1;
100  auto& components = vers.components();
101  if(n >= 0 && size_t(n) < components.size()) {
102  lua_pushinteger(L, vers.components()[n]);
103  } else {
104  lua_pushnil(L);
105  }
106  return 1;
107  }
108  char const *m = luaL_checkstring(L, 2);
109  return_int_attrib("major", vers.major_version());
110  return_int_attrib("minor", vers.minor_version());
111  return_int_attrib("revision", vers.revision_level());
112  return_bool_attrib("is_canonical", vers.is_canonical());
113  return_string_attrib("special", vers.special_version());
114  if(char sep = vers.special_version_separator()) {
115  return_string_attrib("sep", std::string(1, sep));
116  } else if(strcmp(m, "sep") == 0) {
117  lua_pushnil(L);
118  return 1;
119  }
120  return 0;
121 }
122 
123 static int impl_version_dir(lua_State* L)
124 {
125  static const std::vector<std::string> fields{"major", "minor", "revision", "is_canonical", "special", "sep"};
126  lua_push(L, fields);
127  return 1;
128 }
129 
130 /**
131  * Destroy a version
132  */
133 static int impl_version_finalize(lua_State* L)
134 {
135  version_info* vers = static_cast<version_info*>(luaL_checkudata(L, 1, Version));
136  vers->~version_info();
137  return 0;
138 }
139 
140 /**
141  * Convert a version to string form
142  */
143 static int impl_version_tostring(lua_State* L)
144 {
145  version_info& vers = *static_cast<version_info*>(luaL_checkudata(L, 1, Version));
146  lua_push(L, vers.str());
147  return 1;
148 }
149 
150 /**
151  * Builds a version from its component parts, or parses it from a string
152  */
153 static int intf_make_version(lua_State* L)
154 {
155  // If passed a version, just return it unchanged
156  if(luaL_testudata(L, 1, Version)) {
157  lua_settop(L, 1);
158  return 1;
159  }
160  // If it's a string, parse it; otherwise build from components
161  // The components method only supports canonical versions
162  if(lua_type(L, 1) == LUA_TSTRING) {
163  new(L) version_info(lua_check<std::string>(L, 1));
164  } else {
165  int major = luaL_checkinteger(L, 1), minor = luaL_optinteger(L, 2, 0), rev = luaL_optinteger(L, 3, 0);
166  std::string sep, special;
167  if(lua_type(L, -1) == LUA_TSTRING) {
168  special = lua_tostring(L, -1);
169  if(!special.empty() && std::isalpha(special[0])) {
170  sep.push_back('+');
171  } else {
172  sep.push_back(special[0]);
173  special = special.substr(1);
174  }
175  } else {
176  sep.push_back(0);
177  }
178  new(L) version_info(major, minor, rev, sep[0], special);
179  }
180  if(luaL_newmetatable(L, Version)) {
181  static const luaL_Reg metafuncs[] {
182  { "__index", &impl_version_get },
183  { "__dir", &impl_version_dir },
184  { "__tostring", &impl_version_tostring },
185  { "__lt", &impl_version_compare<VERSION_COMP_OP::OP_LESS> },
186  { "__le", &impl_version_compare<VERSION_COMP_OP::OP_LESS_OR_EQUAL> },
187  { "__eq", &impl_version_compare<VERSION_COMP_OP::OP_EQUAL> },
188  { "__gc", &impl_version_finalize },
189  { nullptr, nullptr }
190  };
191  luaL_setfuncs(L, metafuncs, 0);
192  luaW_table_set<std::string>(L, -1, "__metatable", Version);
193  }
194  lua_setmetatable(L, -2);
195  return 1;
196 }
197 
198 /**
199  * Returns the current Wesnoth version
200  */
201 static int intf_current_version(lua_State* L) {
202  lua_settop(L, 0);
205  return 1;
206 }
207 
208 /**
209  * Replacement print function -- instead of printing to std::cout, print to the command log.
210  * Intended to be bound to this' command_log at registration time.
211  */
213 {
214  DBG_LUA << "intf_print called:";
215  std::size_t nargs = lua_gettop(L);
216 
217  lua_getglobal(L, "tostring");
218  for (std::size_t i = 1; i <= nargs; ++i) {
219  lua_pushvalue(L, -1); // function to call: "tostring"
220  lua_pushvalue(L, i); // value to pass through tostring() before printing
221  lua_call(L, 1, 1);
222  const char * str = lua_tostring(L, -1);
223  if (!str) {
224  LOG_LUA << "'tostring' must return a value to 'print'";
225  str = "";
226  }
227  if (i > 1) {
228  cmd_log_ << "\t"; //separate multiple args with tab character
229  }
230  cmd_log_ << str;
231  DBG_LUA << "'" << str << "'";
232  lua_pop(L, 1); // Pop the output of tostrring()
233  }
234  lua_pop(L, 1); // Pop 'tostring' global
235 
236  cmd_log_ << "\n";
237  DBG_LUA;
238 
239  return 0;
240 }
241 
242 static void impl_warn(void* p, const char* msg, int tocont) {
243  static const char*const prefix = "Warning:\n ";
244  static std::ostringstream warning(prefix);
245  warning.seekp(0, std::ios::end);
246  warning << msg << ' ';
247  if(!tocont) {
248  auto L = reinterpret_cast<lua_State*>(p);
249  luaW_getglobal(L, "debug", "traceback");
250  lua_push(L, warning.str());
251  lua_pushinteger(L, 2);
252  lua_call(L, 2, 1);
253  auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
254  lk.add_log_to_console(luaL_checkstring(L, -1));
255  warning.str(prefix);
256  }
257 }
258 
259 void lua_kernel_base::add_log_to_console(const std::string& msg) {
260  cmd_log_ << msg << "\n";
261  DBG_LUA << "'" << msg << "'";
262 }
263 
264 /**
265  * Replacement load function. Mostly the same as regular load, but disallows loading binary chunks
266  * due to CVE-2018-1999023.
267  */
268 static int intf_load(lua_State* L)
269 {
270  std::string chunk = luaL_checkstring(L, 1);
271  const char* name = luaL_optstring(L, 2, chunk.c_str());
272  std::string mode = luaL_optstring(L, 3, "t");
273  bool override_env = !lua_isnone(L, 4);
274 
275  if(mode != "t") {
276  return luaL_argerror(L, 3, "binary chunks are not allowed for security reasons");
277  }
278 
279  int result = luaL_loadbufferx(L, chunk.data(), chunk.length(), name, "t");
280  if(result != LUA_OK) {
281  lua_pushnil(L);
282  // Move the nil as the first return value, like Lua's own load() does.
283  lua_insert(L, -2);
284 
285  return 2;
286  }
287 
288  if(override_env) {
289  // Copy "env" to the top of the stack.
290  lua_pushvalue(L, 4);
291  // Set "env" as the first upvalue.
292  const char* upvalue_name = lua_setupvalue(L, -2, 1);
293  if(upvalue_name == nullptr) {
294  // lua_setupvalue() didn't remove the copy of "env" from the stack, so we need to do it ourselves.
295  lua_pop(L, 1);
296  }
297  }
298 
299  return 1;
300 }
301 
302 /**
303  * Wrapper for pcall and xpcall functions to rethrow jailbreak exceptions
304  */
305 static int intf_pcall(lua_State *L)
306 {
307  lua_CFunction function = lua_tocfunction(L, lua_upvalueindex(1));
308  assert(function); // The upvalue should be Lua's pcall or xpcall, or else something is very wrong.
309 
310  int nRets = function(L);
311 
312  // If a jailbreak exception was stored while running (x)pcall, rethrow it so Lua doesn't continue.
314 
315  return nRets;
316 }
317 
318 // The show lua console callback is similarly a method of lua kernel
320 {
321  if (cmd_log_.external_log_) {
322  std::string message = "There is already an external logger attached to this lua kernel, you cannot open the lua console right now.";
323  log_error(message.c_str());
324  cmd_log_ << message << "\n";
325  return 0;
326  }
327 
328  return lua_gui2::show_lua_console(L, this);
329 }
330 
331 static int impl_name_generator_call(lua_State *L)
332 {
333  name_generator* gen = static_cast<name_generator*>(lua_touserdata(L, 1));
334  lua_pushstring(L, gen->generate().c_str());
335  return 1;
336 }
337 
338 static int impl_name_generator_collect(lua_State *L)
339 {
340  name_generator* gen = static_cast<name_generator*>(lua_touserdata(L, 1));
341  gen->~name_generator();
342  return 0;
343 }
344 
345 static int intf_name_generator(lua_State *L)
346 {
347  std::string type = luaL_checkstring(L, 1);
348  name_generator* gen = nullptr;
349  try {
350  if(type == "markov" || type == "markov_chain") {
351  std::vector<std::string> input;
352  if(lua_istable(L, 2)) {
353  input = lua_check<std::vector<std::string>>(L, 2);
354  } else {
355  input = utils::parenthetical_split(luaW_checktstring(L, 2).str(), ',');
356  }
357  int chain_sz = luaL_optinteger(L, 3, 2);
358  int max_len = luaL_optinteger(L, 4, 12);
359  gen = new(L) markov_generator(input, chain_sz, max_len);
360  // Ensure the pointer didn't change when cast
361  assert(static_cast<void*>(gen) == dynamic_cast<markov_generator*>(gen));
362  } else if(type == "context_free" || type == "cfg" || type == "CFG") {
363  if(lua_istable(L, 2)) {
364  std::map<std::string, std::vector<std::string>> data;
365  for(lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
366  if(lua_type(L, -2) != LUA_TSTRING) {
367  lua_pushstring(L, "CFG generator: invalid nonterminal name (must be a string)");
368  return lua_error(L);
369  }
370  if(lua_isstring(L, -1)) {
371  auto& productions = data[lua_tostring(L,-2)] = utils::split(luaW_checktstring(L,-1).str(), '|');
372  if(productions.size() > 1) {
373  deprecated_message("wesnoth.name_generator('cfg', {nonterminal = 'a|b'})", DEP_LEVEL::INDEFINITE, "1.17", "Non-terminals should now be assigned an array of productions instead of a single string containing productions separated by | - but a single string is fine if it's only one production");
374  }
375  } else if(lua_istable(L, -1)) {
376  const auto& split = lua_check<std::vector<t_string>>(L, -1);
377  auto& productions = data[lua_tostring(L,-2)];
378  std::transform(split.begin(), split.end(), std::back_inserter(productions), std::mem_fn(&t_string::str));
379  } else {
380  lua_pushstring(L, "CFG generator: invalid nonterminal value (must be a string or list of strings)");
381  return lua_error(L);
382  }
383  }
384  if(!data.empty()) {
385  gen = new(L) context_free_grammar_generator(data);
386  }
387  } else {
389  }
390  if(gen) {
391  assert(static_cast<void*>(gen) == dynamic_cast<context_free_grammar_generator*>(gen));
392  }
393  } else {
394  return luaL_argerror(L, 1, "should be either 'markov_chain' or 'context_free'");
395  }
396  }
397  catch (const name_generator_invalid_exception& ex) {
398  lua_pushstring(L, ex.what());
399  return lua_error(L);
400  }
401 
402  // We set the metatable now, even if the generator is invalid, so that it
403  // will be properly collected if it was invalid.
404  luaL_getmetatable(L, Gen);
405  lua_setmetatable(L, -2);
406 
407  return 1;
408 }
409 
410 /**
411 * Logs a message
412 * Arg 1: (optional) Logger
413 * Arg 2: Message
414 */
415 static int intf_log(lua_State *L) {
416  const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : "";
417  std::string msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
418  if(msg.empty() || msg.back() != '\n') {
419  msg += '\n';
420  }
421 
422  if(logger == "err" || logger == "error") {
423  LOG_STREAM(err, log_user) << msg;
424  } else if(logger == "warn" || logger == "wrn" || logger == "warning") {
426  } else if((logger == "debug" || logger == "dbg")) {
428  } else {
430  }
431  return 0;
432 }
433 
434 /**
435  * Logs a deprecation message. See deprecation.cpp for details
436  * Arg 1: Element to be deprecated.
437  * Arg 2: Deprecation level.
438  * Arg 3: Version when element may be removed.
439  * Arg 4: Additional detail message.
440  */
441 static int intf_deprecated_message(lua_State* L) {
442  const std::string elem = luaL_checkstring(L, 1);
443  // This could produce an invalid deprecation level, but that possibility is handled in deprecated_message()
444  const DEP_LEVEL level = DEP_LEVEL(luaL_checkinteger(L, 2));
445  const std::string ver_str = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
446  const std::string detail = luaW_checktstring(L, 4);
447  const version_info ver = ver_str.empty() ? game_config::wesnoth_version.str() : ver_str;
448  const std::string msg = deprecated_message(elem, level, ver, detail);
449  if(level < DEP_LEVEL::INDEFINITE || level >= DEP_LEVEL::REMOVED) {
450  // Invalid deprecation level or level 4 deprecation should raise an interpreter error
451  lua_push(L, msg);
452  return lua_error(L);
453  }
454  lua_warning(L, msg.c_str(), false);
455  return 0;
456 }
457 
458 /**
459  * Converts a Lua array to a named tuple.
460  * Arg 1: A Lua array
461  * Arg 2: An array of strings
462  * Ret: A copy of arg 1 that's now a named tuple with the names in arg 2.
463  * The copy will only include the array portion of the input array.
464  * Any non-integer keys or non-consecutive keys will be gone.
465  * Note: This exists so that wml.tag can use it but is not really intended as a public API.
466  */
467 static int intf_named_tuple(lua_State* L)
468 {
469  if(!lua_istable(L, 1)) {
470  return luaW_type_error(L, 1, lua_typename(L, LUA_TTABLE));
471  }
472  auto names = lua_check<std::vector<std::string>>(L, 2);
473  lua_len(L, 1);
474  int len = luaL_checkinteger(L, -1);
476  for(int i = 1; i <= std::max<int>(len, names.size()); i++) {
477  lua_geti(L, 1, i);
478  lua_seti(L, -2, i);
479  }
480  return 1;
481 }
482 
483 static int intf_parse_shroud_bitmap(lua_State* L)
484 {
485  shroud_map temp;
486  temp.set_enabled(true);
487  temp.read(luaL_checkstring(L, 1));
488  std::set<map_location> locs;
489  for(int x = 1; x <= temp.width(); x++) {
490  for(int y = 1; y <= temp.height(); y++) {
491  if(!temp.value(x, y)) {
492  locs.emplace(x, y, wml_loc());
493  }
494  }
495  }
496  luaW_push_locationset(L, locs);
497  return 1;
498 }
499 
500 static int intf_make_shroud_bitmap(lua_State* L)
501 {
502  shroud_map temp;
503  temp.set_enabled(true);
504  auto locs = luaW_check_locationset(L, 1);
505  for(const auto& loc : locs) {
506  temp.clear(loc.wml_x(), loc.wml_y());
507  }
508  lua_push(L, temp.write());
509  return 1;
510 }
511 
512 /**
513 * Returns the time stamp, exactly as [set_variable] time=stamp does.
514 * - Ret 1: integer
515 */
516 static int intf_ms_since_init(lua_State *L) {
517  lua_pushinteger(L, SDL_GetTicks());
518  return 1;
519 }
520 
521 static int intf_get_language(lua_State* L)
522 {
523  lua_push(L, get_language().localename);
524  return 1;
525 }
526 
527 static void dir_meta_helper(lua_State* L, std::vector<std::string>& keys)
528 {
529  switch(luaL_getmetafield(L, -1, "__dir")) {
530  case LUA_TFUNCTION:
531  lua_pushvalue(L, 1);
532  lua_push(L, keys);
533  if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
534  keys = lua_check<std::vector<std::string>>(L, -1);
535  } else {
536  lua_warning(L, "wesnoth.print_attributes: __dir metamethod raised an error", false);
537  }
538  break;
539  case LUA_TTABLE:
540  auto dir_keys = lua_check<std::vector<std::string>>(L, -1);
541  std::copy(dir_keys.begin(), dir_keys.end(), std::back_inserter(keys));
542  break;
543  }
544  lua_pop(L, 1);
545 }
546 
547 // This is a separate function so I can use a protected call on it to catch errors.
548 static int impl_is_deprecated(lua_State* L)
549 {
550  auto key = luaL_checkstring(L, 2);
551  auto type = lua_getfield(L, 1, key);
552  if(type == LUA_TTABLE) {
553  lua_pushliteral(L, "__deprecated");
554  if(lua_rawget(L, -2) == LUA_TBOOLEAN) {
555  auto deprecated = luaW_toboolean(L, -1);
556  lua_pushboolean(L, deprecated);
557  return 1;
558  }
559  lua_pop(L, 1);
560  }
561  lua_pushboolean(L, false);
562  return 1;
563 }
564 
565 // This is also a separate function so I can use a protected call on it to catch errors.
566 static int impl_get_dir_suffix(lua_State*L)
567 {
568  auto key = luaL_checkstring(L, 2);
569  std::string suffix = " ";
570  auto type = lua_getfield(L, 1, key);
571  if(type == LUA_TTABLE) {
572  suffix = "†";
573  } else if(type == LUA_TFUNCTION) {
574  suffix = "ƒ";
575  } else if(type == LUA_TUSERDATA) {
576  lua_getglobal(L, "getmetatable");
577  lua_pushvalue(L, -2);
578  lua_call(L, 1, 1);
579  if(lua_type(L, -1) == LUA_TSTRING) {
580  auto meta = lua_check<std::string>(L, -1);
581  if(meta == "function") {
582  suffix = "ƒ";
583  }
584  }
585  }
586  suffix = " " + suffix;
587  lua_pushlstring(L, suffix.c_str(), suffix.size());
588  return 1;
589 }
590 
591 /**
592  * This function does the actual work of grabbing all the attribute names.
593  * It's a separate function so that it can be used by tab-completion as well.
594  */
595 std::vector<std::string> luaW_get_attributes(lua_State* L, int idx)
596 {
597  if(idx < 0 && idx >= -lua_gettop(L)) {
598  idx = lua_absindex(L, idx);
599  }
600  std::vector<std::string> keys;
601  if(lua_istable(L, idx)) {
602  // Walk the metatable chain (as long as __index is a table)...
603  // If we reach an __index that's a function, check for a __dir metafunction.
604  int save_top = lua_gettop(L);
605  lua_pushvalue(L, idx);
606  ON_SCOPE_EXIT(&) {
607  lua_settop(L, save_top);
608  };
609  do {
610  int table_idx = lua_absindex(L, -1);
611  for(lua_pushnil(L); lua_next(L, table_idx); lua_pop(L, 1)) {
612  if(lua_type(L, -2) == LUA_TSTRING) {
613  keys.push_back(lua_tostring(L,-2));
614  }
615  }
616  // Two possible exit cases:
617  // 1. getmetafield returns TNIL because there is no __index
618  // In this case, the stack is unchanged, so the while condition is still true.
619  // 2. The __index is not a table
620  // In this case, obviously the while condition fails
621  if(luaL_getmetafield(L, table_idx, "__index") == LUA_TNIL) break;
622  } while(lua_istable(L, -1));
623  if(lua_isfunction(L, -1)) {
624  lua_pop(L, 1);
625  dir_meta_helper(L, keys);
626  }
627  } else if(lua_isuserdata(L, idx) && !lua_islightuserdata(L, idx)) {
628  lua_pushvalue(L, idx);
629  dir_meta_helper(L, keys);
630  lua_pop(L, 1);
631  }
632  // Sort and remove any duplicates
633  std::sort(keys.begin(), keys.end());
634  auto new_end = std::unique(keys.begin(), keys.end());
635  new_end = std::remove_if(keys.begin(), new_end, [L, idx](const std::string& key) {
636  if(key.compare(0, 2, "__") == 0) {
637  return true;
638  }
639  int save_top = lua_gettop(L);
640  ON_SCOPE_EXIT(&) {
641  lua_settop(L, save_top);
642  };
643  // Exclude deprecated elements
644  // Some keys may be write-only, which would raise an exception here
645  // In that case we just ignore it and assume not deprecated
646  // (the __dir metamethod would be responsible for excluding deprecated write-only keys)
647  lua_pushcfunction(L, impl_is_deprecated);
648  lua_pushvalue(L, idx);
649  lua_push(L, key);
650  if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
651  return luaW_toboolean(L, -1);
652  }
653  return false;
654  });
655  keys.erase(new_end, keys.end());
656  return keys;
657 }
658 
659 /**
660  * Prints out a list of keys available in an object.
661  * A list of keys is gathered from the following sources:
662  * - For a table, all keys defined in the table
663  * - Any keys accessible through the metatable chain (if __index on the metatable is a table)
664  * - The output of the __dir metafunction
665  * - Filtering out any keys beginning with two underscores
666  * - Filtering out any keys for which object[key].__deprecated exists and is true
667  * The list is then sorted alphabetically and formatted into columns.
668  * - Arg 1: Any object
669  * - Arg 2: (optional) Function to use for output; defaults to _G.print
670  */
671 static int intf_object_dir(lua_State* L)
672 {
673  if(lua_isnil(L, 1)) return luaL_argerror(L, 1, "Can't dir() nil");
674  if(!lua_isfunction(L, 2)) {
675  luaW_getglobal(L, "print");
676  }
677  int fcn_idx = lua_gettop(L);
678  auto keys = luaW_get_attributes(L, 1);
679  size_t max_len = std::accumulate(keys.begin(), keys.end(), 0, [](size_t max, const std::string& next) {
680  return std::max(max, next.size());
681  });
682  // Let's limit to about 80 characters of total width with minimum 3 characters padding between columns
683  static const size_t MAX_WIDTH = 80, COL_PADDING = 3, SUFFIX_PADDING = 2;
684  size_t col_width = max_len + COL_PADDING + SUFFIX_PADDING;
685  size_t n_cols = (MAX_WIDTH + COL_PADDING) / col_width;
686  size_t n_rows = ceil(keys.size() / double(n_cols));
687  for(size_t i = 0; i < n_rows; i++) {
688  std::ostringstream line;
689  line.fill(' ');
690  line.setf(std::ios::left);
691  for(size_t j = 0; j < n_cols && j + (i * n_cols) < keys.size(); j++) {
692  int save_top = lua_gettop(L);
693  ON_SCOPE_EXIT(&) {
694  lua_settop(L, save_top);
695  };
696  lua_pushcfunction(L, impl_get_dir_suffix);
697  lua_pushvalue(L, 1);
698  const auto& key = keys[j + i * n_cols];
699  lua_pushlstring(L, key.c_str(), key.size());
700  std::string suffix = " !"; // Exclamation mark to indicate an error
701  if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
702  suffix = luaL_checkstring(L, -1);
703  }
704  // This weird calculation is because width counts in bytes, not code points
705  // Since the suffix is a Unicode character, that messes up the alignment
706  line.width(col_width - SUFFIX_PADDING + suffix.size());
707  // Concatenate key and suffix beforehand so they share the same field width.
708  line << (key + suffix) << std::flush;
709  }
710  lua_pushvalue(L, fcn_idx);
711  lua_push(L, line.str());
712  lua_call(L, 1, 0);
713  }
714  return 0;
715 }
716 
717 // End Callback implementations
718 
719 // Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
720 typedef int (lua_kernel_base::*member_callback)(lua_State *L);
721 
722 template <member_callback method>
723 int dispatch(lua_State *L) {
724  return ((lua_kernel_base::get_lua_kernel<lua_kernel_base>(L)).*method)(L);
725 }
726 
727 // Ctor, initialization
729  : mState(luaL_newstate())
730  , cmd_log_()
731 {
733  lua_State *L = mState;
734 
735  cmd_log_ << "Initializing " << my_name() << "...\n";
736 
737  // Define the CPP_function metatable ( so we can override print to point to a C++ member function, add certain functions for this kernel, etc. )
738  // Do it first of all in case C++ functions are ever used in the core Wesnoth libs loaded in the next step
739  cmd_log_ << "Adding boost function proxy...\n";
740 
742 
743  // Open safe libraries.
744  // Debug and OS are not, but most of their functions will be disabled below.
745  cmd_log_ << "Adding standard libs...\n";
746 
747  static const luaL_Reg safe_libs[] {
748  { "", luaopen_base },
749  { "table", luaopen_table },
750  { "string", luaopen_string },
751  { "math", luaopen_math },
752  { "coroutine", luaopen_coroutine },
753  { "debug", luaopen_debug },
754  { "os", luaopen_os },
755  { "utf8", luaopen_utf8 }, // added in Lua 5.3
756  // Wesnoth libraries
757  { "stringx",lua_stringx::luaW_open },
758  { "mathx", lua_mathx::luaW_open },
759  { "wml", lua_wml::luaW_open },
760  { "gui", lua_gui2::luaW_open },
761  { "filesystem", lua_fileops::luaW_open },
762  { nullptr, nullptr }
763  };
764  for (luaL_Reg const *lib = safe_libs; lib->func; ++lib)
765  {
766  luaL_requiref(L, lib->name, lib->func, strlen(lib->name));
767  lua_pop(L, 1); /* remove lib */
768  }
769 
770  // Disable functions from os which we don't want.
771  lua_getglobal(L, "os");
772  lua_pushnil(L);
773  while(lua_next(L, -2) != 0) {
774  lua_pop(L, 1);
775  char const* function = lua_tostring(L, -1);
776  if(strcmp(function, "clock") == 0 || strcmp(function, "date") == 0
777  || strcmp(function, "time") == 0 || strcmp(function, "difftime") == 0) continue;
778  lua_pushnil(L);
779  lua_setfield(L, -3, function);
780  }
781  lua_pop(L, 1);
782 
783  // Delete dofile and loadfile.
784  lua_pushnil(L);
785  lua_setglobal(L, "dofile");
786  lua_pushnil(L);
787  lua_setglobal(L, "loadfile");
788 
789  // Store the error handler.
790  cmd_log_ << "Adding error handler...\n";
792 
793 
794  lua_settop(L, 0);
795 
796  // Add some callback from the wesnoth lib
797  cmd_log_ << "Registering basic wesnoth API...\n";
798 
799  static luaL_Reg const callbacks[] {
800  { "deprecated_message", &intf_deprecated_message },
801  { "textdomain", &lua_common::intf_textdomain },
802  { "dofile", &dispatch<&lua_kernel_base::intf_dofile> },
803  { "require", &dispatch<&lua_kernel_base::intf_require> },
804  { "kernel_type", &dispatch<&lua_kernel_base::intf_kernel_type> },
805  { "compile_formula", &lua_formula_bridge::intf_compile_formula},
806  { "eval_formula", &lua_formula_bridge::intf_eval_formula},
807  { "name_generator", &intf_name_generator },
808  { "named_tuple", &intf_named_tuple },
809  { "log", &intf_log },
810  { "ms_since_init", &intf_ms_since_init },
811  { "get_language", &intf_get_language },
812  { "version", &intf_make_version },
813  { "current_version", &intf_current_version },
814  { "print_attributes", &intf_object_dir },
815  { nullptr, nullptr }
816  };
817 
818  lua_getglobal(L, "wesnoth");
819  if (!lua_istable(L,-1)) {
820  lua_newtable(L);
821  }
822  luaL_setfuncs(L, callbacks, 0);
823  //lua_cpp::set_functions(L, cpp_callbacks, 0);
824  lua_setglobal(L, "wesnoth");
825 
826  // Create the gettext metatable.
828  // Create the tstring metatable.
830 
832 
833  // Override the print function
834  cmd_log_ << "Redirecting print function...\n";
835 
836  lua_getglobal(L, "print");
837  lua_setglobal(L, "std_print"); //storing original impl as 'std_print'
838  lua_settop(L, 0); //clear stack, just to be sure
839 
840  lua_setwarnf(L, &::impl_warn, L);
841  lua_pushcfunction(L, &dispatch<&lua_kernel_base::intf_print>);
842  lua_setglobal(L, "print");
843 
844  lua_pushcfunction(L, intf_load);
845  lua_setglobal(L, "load");
846  lua_pushnil(L);
847  lua_setglobal(L, "loadstring");
848 
849  // Wrap the pcall and xpcall functions
850  cmd_log_ << "Wrapping pcall and xpcall functions...\n";
851  lua_getglobal(L, "pcall");
852  lua_pushcclosure(L, intf_pcall, 1);
853  lua_setglobal(L, "pcall");
854  lua_getglobal(L, "xpcall");
855  lua_pushcclosure(L, intf_pcall, 1);
856  lua_setglobal(L, "xpcall");
857 
858  cmd_log_ << "Initializing package repository...\n";
859  // Create the package table.
860  lua_getglobal(L, "wesnoth");
861  lua_newtable(L);
862  lua_setfield(L, -2, "package");
863  lua_pop(L, 1);
864  lua_settop(L, 0);
865  lua_pushstring(L, "lua/package.lua");
866  int res = intf_require(L);
867  if(res != 1) {
868  cmd_log_ << "Error: Failed to initialize package repository. Falling back to less flexible C++ implementation.\n";
869  }
870 
871  // Get some callbacks for map locations
872  cmd_log_ << "Adding map table...\n";
873 
874  static luaL_Reg const map_callbacks[] {
875  { "get_direction", &lua_map_location::intf_get_direction },
876  { "vector_sum", &lua_map_location::intf_vector_sum },
877  { "vector_diff", &lua_map_location::intf_vector_diff },
878  { "vector_negation", &lua_map_location::intf_vector_negation },
879  { "rotate_right_around_center", &lua_map_location::intf_rotate_right_around_center },
880  { "are_hexes_adjacent", &lua_map_location::intf_tiles_adjacent },
881  { "get_adjacent_hexes", &lua_map_location::intf_get_adjacent_tiles },
882  { "get_hexes_in_radius", &lua_map_location::intf_get_tiles_in_radius },
883  { "distance_between", &lua_map_location::intf_distance_between },
884  { "get_in_basis_N_NE", &lua_map_location::intf_get_in_basis_N_NE },
885  { "get_relative_dir", &lua_map_location::intf_get_relative_dir },
886  // Shroud bitmaps
887  {"parse_bitmap", intf_parse_shroud_bitmap},
888  {"make_bitmap", intf_make_shroud_bitmap},
889  { nullptr, nullptr }
890  };
891 
892  // Create the map_location table.
893  lua_getglobal(L, "wesnoth");
894  lua_newtable(L);
895  luaL_setfuncs(L, map_callbacks, 0);
896  lua_setfield(L, -2, "map");
897  lua_pop(L, 1);
898 
899  // Create the game_config variable with its metatable.
900  cmd_log_ << "Adding game_config table...\n";
901 
902  lua_getglobal(L, "wesnoth");
903  lua_newuserdatauv(L, 0, 0);
904  lua_createtable(L, 0, 3);
905  lua_pushcfunction(L, &dispatch<&lua_kernel_base::impl_game_config_get>);
906  lua_setfield(L, -2, "__index");
907  lua_pushcfunction(L, &dispatch<&lua_kernel_base::impl_game_config_set>);
908  lua_setfield(L, -2, "__newindex");
909  lua_pushstring(L, "game config");
910  lua_setfield(L, -2, "__metatable");
911  lua_setmetatable(L, -2);
912  lua_setfield(L, -2, "game_config");
913  lua_pop(L, 1);
914 
915  // Add mersenne twister rng wrapper
916  cmd_log_ << "Adding rng tables...\n";
918 
919  cmd_log_ << "Adding name generator metatable...\n";
920  luaL_newmetatable(L, Gen);
921  static luaL_Reg const generator[] {
922  { "__call", &impl_name_generator_call},
923  { "__gc", &impl_name_generator_collect},
924  { nullptr, nullptr}
925  };
926  luaL_setfuncs(L, generator, 0);
927 
928  // Create formula bridge metatables
930 
932 
933  // Create the Lua interpreter table
934  cmd_log_ << "Sandboxing Lua interpreter...\nTo make variables visible outside the interpreter, assign to _G.variable.\n";
935  cmd_log_ << "The special variable _ holds the result of the last expression (if any).\n";
936  lua_newtable(L);
937  lua_createtable(L, 0, 1);
938  lua_getglobal(L, "_G");
939  lua_setfield(L, -2, "__index");
940  lua_setmetatable(L, -2);
941  lua_pushcfunction(L, intf_object_dir);
942  lua_setfield(L, -2, "dir");
943  lua_setfield(L, LUA_REGISTRYINDEX, Interp);
944 
945  // Loading ilua:
946  cmd_log_ << "Loading ilua...\n";
947 
948  lua_settop(L, 0);
949  luaW_getglobal(L, "wesnoth", "require");
950  lua_pushstring(L, "lua/ilua.lua");
951  if(protected_call(1, 1)) {
952  //run "ilua.set_strict()"
953  lua_pushstring(L, "set_strict");
954  lua_gettable(L, -2);
955  if (!this->protected_call(0,0, std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2))) {
956  cmd_log_ << "Failed to activate strict mode.\n";
957  } else {
958  cmd_log_ << "Activated strict mode.\n";
959  }
960 
961  lua_setglobal(L, "ilua"); //save ilua table as a global
962  } else {
963  cmd_log_ << "Error: failed to load ilua.\n";
964  }
965  lua_settop(L, 0);
966 
967  // Disable functions from debug which we don't want.
968  // We do this last because ilua needs to be able to use debug.getmetatable
969  lua_getglobal(L, "debug");
970  lua_pushnil(L);
971  while(lua_next(L, -2) != 0) {
972  lua_pop(L, 1);
973  char const* function = lua_tostring(L, -1);
974  if(strcmp(function, "traceback") == 0 || strcmp(function, "getinfo") == 0) continue; //traceback is needed for our error handler
975  lua_pushnil(L); //getinfo is needed for ilua strict mode
976  lua_setfield(L, -3, function);
977  }
978  lua_pop(L, 1);
979 }
980 
982 {
983  for (const auto& pair : this->registered_widget_definitions_) {
984  gui2::remove_single_widget_definition(std::get<0>(pair), std::get<1>(pair));
985  }
986  lua_close(mState);
987 }
988 
989 void lua_kernel_base::log_error(char const * msg, char const * context)
990 {
991  ERR_LUA << context << ": " << msg;
992 }
993 
994 void lua_kernel_base::throw_exception(char const * msg, char const * context)
995 {
996  throw game::lua_error(msg, context);
997 }
998 
999 bool lua_kernel_base::protected_call(int nArgs, int nRets)
1000 {
1001  error_handler eh = std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2 );
1002  return this->protected_call(nArgs, nRets, eh);
1003 }
1004 
1005 bool lua_kernel_base::load_string(char const * prog, const std::string& name)
1006 {
1007  error_handler eh = std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2 );
1008  return this->load_string(prog, name, eh);
1009 }
1010 
1011 bool lua_kernel_base::protected_call(int nArgs, int nRets, error_handler e_h)
1012 {
1013  return this->protected_call(mState, nArgs, nRets, e_h);
1014 }
1015 
1016 bool lua_kernel_base::protected_call(lua_State * L, int nArgs, int nRets, error_handler e_h)
1017 {
1018  int errcode = luaW_pcall_internal(L, nArgs, nRets);
1019 
1020  if (errcode != LUA_OK) {
1021  char const * msg = lua_tostring(L, -1);
1022 
1023  std::string context = "When executing, ";
1024  if (errcode == LUA_ERRRUN) {
1025  context += "Lua runtime error: ";
1026  } else if (errcode == LUA_ERRERR) {
1027  context += "Lua error in attached debugger: ";
1028  } else if (errcode == LUA_ERRMEM) {
1029  context += "Lua out of memory error: ";
1030  } else {
1031  context += "unknown lua error: ";
1032  }
1033  if(lua_isstring(L, -1)) {
1034  context += msg ? msg : "null string";
1035  } else {
1036  context += lua_typename(L, lua_type(L, -1));
1037  }
1038 
1039  lua_pop(L, 1);
1040 
1041  e_h(context.c_str(), "Lua Error");
1042 
1043  return false;
1044  }
1045 
1046  return true;
1047 }
1048 
1049 bool lua_kernel_base::load_string(char const * prog, const std::string& name, error_handler e_h)
1050 {
1051  // pass 't' to prevent loading bytecode which is unsafe and can be used to escape the sandbox.
1052  int errcode = luaL_loadbufferx(mState, prog, strlen(prog), name.empty() ? prog : name.c_str(), "t");
1053  if (errcode != LUA_OK) {
1054  char const * msg = lua_tostring(mState, -1);
1055  std::string message = msg ? msg : "null string";
1056 
1057  std::string context = "When parsing a string to lua, ";
1058 
1059  if (errcode == LUA_ERRSYNTAX) {
1060  context += " a syntax error";
1061  } else if(errcode == LUA_ERRMEM){
1062  context += " a memory error";
1063  } else {
1064  context += " an unknown error";
1065  }
1066 
1067  lua_pop(mState, 1);
1068 
1069  e_h(message.c_str(), context.c_str());
1070 
1071  return false;
1072  }
1073  return true;
1074 }
1075 
1077 {
1078  int nArgs = 0;
1079  if (auto args = cfg.optional_child("args")) {
1080  luaW_pushconfig(this->mState, *args);
1081  ++nArgs;
1082  }
1083  this->run(cfg["code"].str().c_str(), cfg["name"].str(), nArgs);
1084 }
1085 // Call load_string and protected call. Make them throw exceptions.
1086 //
1087 void lua_kernel_base::throwing_run(const char * prog, const std::string& name, int nArgs, bool in_interpreter)
1088 {
1089  cmd_log_ << "$ " << prog << "\n";
1090  error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1091  this->load_string(prog, name, eh);
1092  if(in_interpreter) {
1093  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1094  if(lua_setupvalue(mState, -2, 1) == nullptr)
1095  lua_pop(mState, 1);
1096  }
1097  lua_insert(mState, -nArgs - 1);
1098  this->protected_call(nArgs, in_interpreter ? LUA_MULTRET : 0, eh);
1099 }
1100 
1101 // Do a throwing run, but if we catch a lua_error, reformat it with signature for this function and log it.
1102 void lua_kernel_base::run(const char * prog, const std::string& name, int nArgs)
1103 {
1104  try {
1105  this->throwing_run(prog, name, nArgs);
1106  } catch (const game::lua_error & e) {
1107  cmd_log_ << e.what() << "\n";
1108  lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
1109  }
1110 }
1111 
1112 // Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it normally. Throws exceptions.
1113 void lua_kernel_base::interactive_run(char const * prog) {
1114  std::string experiment = "return ";
1115  experiment += prog;
1116  int top = lua_gettop(mState);
1117 
1118  error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1119  luaW_getglobal(mState, "ilua", "_pretty_print");
1120 
1121  try {
1122  // Try to load the experiment without syntax errors
1123  this->load_string(experiment.c_str(), "interactive", eh);
1124  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1125  if(lua_setupvalue(mState, -2, 1) == nullptr)
1126  lua_pop(mState, 1);
1127  } catch (const game::lua_error &) {
1128  this->throwing_run(prog, "interactive", 0, true); // Since it failed, fall back to the usual throwing_run, on the original input.
1129  if(lua_gettop(mState) == top + 1) {
1130  // Didn't return anything
1131  lua_settop(mState, top);
1132  return;
1133  } else goto PRINT;
1134  }
1135  // experiment succeeded, now run but log normally.
1136  cmd_log_ << "$ " << prog << "\n";
1137  this->protected_call(0, LUA_MULTRET, eh);
1138 PRINT:
1139  int nRets = lua_gettop(mState) - top - 1;
1140  {
1141  // Assign first result to _
1142  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1143  int env_idx = lua_gettop(mState);
1144  lua_pushvalue(mState, top + 2);
1145  lua_setfield(mState, -2, "_");
1146  // Now duplicate EVERY result and pass it to table.pack, assigning to _all
1147  luaW_getglobal(mState, "table", "pack");
1148  for(int i = top + 2; i < env_idx; i++)
1149  lua_pushvalue(mState, i);
1150  this->protected_call(nRets, 1, eh);
1151  lua_setfield(mState, -2, "_all");
1152  lua_pop(mState, 1);
1153  }
1154  // stack is now ilua._pretty_print followed by any results of prog
1155  this->protected_call(lua_gettop(mState) - top - 1, 0, eh);
1156 }
1157 /**
1158  * Loads and executes a Lua file.
1159  * - Arg 1: string containing the file name.
1160  * - Ret *: values returned by executing the file body.
1161  */
1163 {
1164  luaL_checkstring(L, 1);
1165  lua_rotate(L, 1, -1);
1166  if (lua_fileops::load_file(L) != 1) return 0;
1167  //^ should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1168  lua_rotate(L, 1, 1);
1169  // Using a non-protected call here appears to fix an issue in plugins.
1170  // The protected call isn't technically necessary anyway, because this function is called from Lua code,
1171  // which should already be in a protected environment.
1172  lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
1173  return lua_gettop(L);
1174 }
1175 
1176 /**
1177  * Loads and executes a Lua file, if there is no corresponding entry in wesnoth.package.
1178  * Stores the result of the script in wesnoth.package and returns it.
1179  * - Arg 1: string containing the file name.
1180  * - Ret 1: value returned by the script.
1181  */
1183 {
1184  const char * m = luaL_checkstring(L, 1);
1185  if(!m) {
1186  return luaL_argerror(L, 1, "found a null string argument to wesnoth require");
1187  }
1188 
1189  // Check if there is already an entry.
1190 
1191  lua_getglobal(L, "wesnoth");
1192  lua_pushstring(L, "package");
1193  lua_rawget(L, -2);
1194  lua_pushvalue(L, 1);
1195  lua_rawget(L, -2);
1196  if(!lua_isnil(L, -1) && !game_config::debug_lua) {
1197  return 1;
1198  }
1199  lua_pop(L, 1);
1200  lua_pushvalue(L, 1);
1201  // stack is now [packagename] [wesnoth] [package] [packagename]
1202 
1203  if(lua_fileops::load_file(L) != 1) {
1204  // should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1205  // stack is now [packagename] [wesnoth] [package] [chunk]
1206  return 0;
1207  }
1208  DBG_LUA << "require: loaded a file, now calling it";
1209 
1210  if (!this->protected_call(L, 0, 1, std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2))) {
1211  // historically if wesnoth.require fails it just yields nil and some logging messages, not a lua error
1212  return 0;
1213  }
1214  // stack is now [packagename] [wesnoth] [package] [results]
1215 
1216  lua_pushvalue(L, 1);
1217  lua_pushvalue(L, -2);
1218  // stack is now [packagename] [wesnoth] [package] [results] [packagename] [results]
1219  // Add the return value to the table.
1220 
1221  lua_settable(L, -4);
1222  // stack is now [packagename] [wesnoth] [package] [results]
1223  return 1;
1224 }
1226 {
1227  lua_push(L, my_name());
1228  return 1;
1229 }
1231 {
1232  char const *m = luaL_checkstring(L, 2);
1235  return_int_attrib("village_support", game_config::village_support);
1237  return_int_attrib("rest_heal_amount", game_config::rest_heal_amount);
1239  return_int_attrib("kill_experience", game_config::kill_experience);
1240  return_int_attrib("combat_experience", game_config::combat_experience);
1241  return_string_attrib_deprecated("version", "wesnoth.game_config", INDEFINITE, "1.17", "Use wesnoth.current_version() instead", game_config::wesnoth_version.str());
1246  return 0;
1247 }
1249 {
1250  std::string err_msg = "unknown modifiable property of game_config: ";
1251  err_msg += luaL_checkstring(L, 2);
1252  return luaL_argerror(L, 2, err_msg.c_str());
1253 }
1254 /**
1255  * Loads the "package" package into the Lua environment.
1256  * This action is inherently unsafe, as Lua scripts will now be able to
1257  * load C libraries on their own, hence granting them the same privileges
1258  * as the Wesnoth binary itself.
1259  */
1261 {
1262  lua_State *L = mState;
1263  lua_pushcfunction(L, luaopen_package);
1264  lua_pushstring(L, "package");
1265  lua_call(L, 1, 0);
1266 }
1267 
1269 {
1270  lua_State* L = mState;
1271  lua_settop(L, 0);
1272  cmd_log_ << "Loading core...\n";
1273  luaW_getglobal(L, "wesnoth", "require");
1274  lua_pushstring(L, "lua/core");
1275  if(!protected_call(1, 1)) {
1276  cmd_log_ << "Error: Failed to load core.\n";
1277  }
1278  lua_settop(L, 0);
1279 }
1280 
1281 /**
1282  * Gets all the global variable names in the Lua environment. This is useful for tab completion.
1283  */
1284 std::vector<std::string> lua_kernel_base::get_global_var_names()
1285 {
1286  std::vector<std::string> ret;
1287 
1288  lua_State *L = mState;
1289 
1290  int idx = lua_gettop(L);
1291  lua_getglobal(L, "_G");
1292  lua_pushnil(L);
1293 
1294  while (lua_next(L, idx+1) != 0) {
1295  if (lua_isstring(L, -2)) {
1296  ret.push_back(lua_tostring(L,-2));
1297  }
1298  lua_pop(L,1);
1299  }
1300  lua_settop(L, idx);
1301  return ret;
1302 }
1303 
1304 /**
1305  * Gets all attribute names of an extended variable name. This is useful for tab completion.
1306  */
1307 std::vector<std::string> lua_kernel_base::get_attribute_names(const std::string & input)
1308 {
1309  std::vector<std::string> ret;
1310  std::string base_path = input;
1311  std::size_t last_dot = base_path.find_last_of('.');
1312  std::string partial_name = base_path.substr(last_dot + 1);
1313  base_path.erase(last_dot);
1314  std::string load = "return " + base_path;
1315 
1316  lua_State* L = mState;
1317  int save_stack = lua_gettop(L);
1318  int result = luaL_loadstring(L, load.c_str());
1319  if(result != LUA_OK) {
1320  // This isn't at error level because it's a really low priority error; it just means the user tried to tab-complete something that doesn't exist.
1321  LOG_LUA << "Error when attempting tab completion:";
1322  LOG_LUA << luaL_checkstring(L, -1);
1323  // Just return an empty list; no matches were found
1324  lua_settop(L, save_stack);
1325  return ret;
1326  }
1327 
1328  luaW_pcall(L, 0, 1);
1329  if(lua_istable(L, -1) || lua_isuserdata(L, -1)) {
1330  int top = lua_gettop(L);
1331  int obj = lua_absindex(L, -1);
1332  if(luaL_getmetafield(L, obj, "__tab_enum") == LUA_TFUNCTION) {
1333  lua_pushvalue(L, obj);
1334  lua_pushlstring(L, partial_name.c_str(), partial_name.size());
1335  luaW_pcall(L, 2, 1);
1336  ret = lua_check<std::vector<std::string>>(L, -1);
1337  } else if(lua_type(L, -1) != LUA_TTABLE) {
1338  LOG_LUA << "Userdata missing __tab_enum meta-function for tab completion";
1339  lua_settop(L, save_stack);
1340  return ret;
1341  } else {
1342  lua_settop(L, top);
1343  // Metafunction not found, so use lua_next to enumerate the table
1344  for(lua_pushnil(L); lua_next(L, obj); lua_pop(L, 1)) {
1345  if(lua_type(L, -2) == LUA_TSTRING) {
1346  std::string attr = lua_tostring(L, -2);
1347  if(attr.empty()) {
1348  continue;
1349  }
1350  if(!isalpha(attr[0]) && attr[0] != '_') {
1351  continue;
1352  }
1353  if(std::any_of(attr.begin(), attr.end(), [](char c){
1354  return !isalpha(c) && !isdigit(c) && c != '_';
1355  })) {
1356  continue;
1357  }
1358  if(attr.substr(0, partial_name.size()) == partial_name) {
1359  ret.push_back(base_path + "." + attr);
1360  }
1361  }
1362  }
1363  }
1364  }
1365  lua_settop(L, save_stack);
1366  return ret;
1367 }
1368 
1370 {
1371  #ifdef __GNUC__
1372  #pragma GCC diagnostic push
1373  #pragma GCC diagnostic ignored "-Wold-style-cast"
1374  #endif
1375  return *reinterpret_cast<lua_kernel_base**>(lua_getextraspace(L));
1376  #ifdef __GNUC__
1377  #pragma GCC diagnostic pop
1378  #endif
1379 }
1380 
1382 {
1383  return seed_rng::next_seed();
1384 }
#define debug(x)
std::vector< std::string > names
Definition: build_info.cpp:67
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
static void rethrow()
Rethrows the stored exception.
void load_core()
Loads the "core" library into the Lua environment.
void run(char const *prog, const std::string &name, int nArgs=0)
Runs a plain script.
virtual void log_error(char const *msg, char const *context="Lua error")
Error reporting mechanisms, used by virtual methods protected_call and load_string.
int intf_dofile(lua_State *L)
Loads and executes a Lua file.
command_log cmd_log_
virtual int impl_game_config_get(lua_State *L)
int intf_require(lua_State *L)
Loads and executes a Lua file, if there is no corresponding entry in wesnoth.package.
lua_State * mState
void throwing_run(char const *prog, const std::string &name, int nArgs, bool in_interpreter=false)
Runs a plain script, but reports errors by throwing lua_error.
int intf_kernel_type(lua_State *L)
virtual int impl_game_config_set(lua_State *L)
void load_package()
Loads the package library into lua environment.
bool protected_call(int nArgs, int nRets, error_handler)
void add_log_to_console(const std::string &msg)
int intf_show_lua_console(lua_State *L)
bool load_string(char const *prog, const std::string &name, error_handler)
std::vector< std::tuple< std::string, std::string > > registered_widget_definitions_
static lua_kernel_base *& get_lua_kernel_base_ptr(lua_State *L)
std::vector< std::string > get_global_var_names()
Get tab completion strings.
std::function< void(char const *, char const *)> error_handler
void run_lua_tag(const config &cfg)
Runs a [lua] tag.
virtual ~lua_kernel_base()
int intf_print(lua_State *L)
Replacement print function – instead of printing to std::cout, print to the command log.
void interactive_run(char const *prog)
Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it nor...
std::vector< std::string > get_attribute_names(const std::string &var_path)
Gets all attribute names of an extended variable name.
virtual uint32_t get_random_seed()
virtual void throw_exception(char const *msg, char const *context="Lua error")
virtual std::string my_name()
User-visible name of the lua kernel that they are talking to.
const char * what() const noexcept
std::string generate(const std::map< std::string, std::string > &variables) const
virtual ~name_generator()
int height() const
Definition: team.cpp:772
void read(const std::string &shroud_data)
Definition: team.cpp:887
void set_enabled(bool enabled)
Definition: team.hpp:60
int width() const
Definition: team.cpp:767
bool value(int x, int y) const
Definition: team.cpp:830
std::string write() const
Definition: team.cpp:871
bool clear(int x, int y)
Definition: team.cpp:780
const std::string & str() const
Definition: tstring.hpp:190
Represents version numbers.
std::string str() const
Serializes the version number into string form.
unsigned int revision_level() const
Retrieves the revision level (x3 in "x1.x2.x3").
char special_version_separator() const
Retrieves the special version separator (e.g.
const std::string & special_version() const
Retrieves the special version suffix (e.g.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
const std::vector< unsigned int > & components() const
Read-only access to all numeric components.
bool is_canonical() const
Whether the version number is considered canonical for mainline Wesnoth.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
Definition: deprecation.hpp:21
std::size_t i
Definition: function.cpp:968
int(* lua_CFunction)(lua_State *L)
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
Interfaces for manipulating version numbers of engine, add-ons, etc.
const language_def & get_language()
Definition: language.cpp:331
Standard logging facilities (interface).
#define LOG_STREAM(level, domain)
Definition: log.hpp:280
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:824
int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
void push_error_handler(lua_State *L)
void luaW_push_namedtuple(lua_State *L, const std::vector< std::string > &names)
Push an empty "named tuple" onto the stack.
Definition: lua_common.cpp:709
std::set< map_location > luaW_check_locationset(lua_State *L, int idx)
Converts a table of integer pairs to a set of map location objects.
Definition: lua_common.cpp:807
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:985
int luaW_type_error(lua_State *L, int narg, const char *tname)
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:795
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
Definition: lua_common.cpp:966
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:632
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:256
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:267
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:287
#define return_string_attrib_deprecated(name, prefix, level, version, msg, accessor)
Definition: lua_common.hpp:264
static int intf_make_shroud_bitmap(lua_State *L)
static int impl_version_finalize(lua_State *L)
Destroy a version.
int dispatch(lua_State *L)
static int intf_name_generator(lua_State *L)
static lg::log_domain log_user("scripting/lua/user")
#define ERR_LUA
static int intf_current_version(lua_State *L)
Returns the current Wesnoth version.
static lg::log_domain log_scripting_lua("scripting/lua")
static int intf_parse_shroud_bitmap(lua_State *L)
static int intf_make_version(lua_State *L)
Builds a version from its component parts, or parses it from a string.
static int impl_version_dir(lua_State *L)
static int impl_version_get(lua_State *L)
Decomposes a version into its component parts.
static int impl_get_dir_suffix(lua_State *L)
static int impl_version_tostring(lua_State *L)
Convert a version to string form.
static int intf_deprecated_message(lua_State *L)
Logs a deprecation message.
static int intf_pcall(lua_State *L)
Wrapper for pcall and xpcall functions to rethrow jailbreak exceptions.
static int intf_named_tuple(lua_State *L)
Converts a Lua array to a named tuple.
#define LOG_LUA
static void impl_warn(void *p, const char *msg, int tocont)
int(lua_kernel_base::* member_callback)(lua_State *L)
static const char * Version
static int intf_log(lua_State *L)
Logs a message Arg 1: (optional) Logger Arg 2: Message.
static int impl_is_deprecated(lua_State *L)
static const char * Interp
#define DBG_LUA
std::vector< std::string > luaW_get_attributes(lua_State *L, int idx)
This function does the actual work of grabbing all the attribute names.
static int intf_load(lua_State *L)
Replacement load function.
static int intf_ms_since_init(lua_State *L)
Returns the time stamp, exactly as [set_variable] time=stamp does.
static const char * Gen
static int intf_get_language(lua_State *L)
static int impl_name_generator_call(lua_State *L)
static void dir_meta_helper(lua_State *L, std::vector< std::string > &keys)
static int intf_object_dir(lua_State *L)
Prints out a list of keys available in an object.
static int impl_name_generator_collect(lua_State *L)
static int impl_version_compare(lua_State *L)
Compares two versions.
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
int rest_heal_amount
Definition: game_config.cpp:45
int village_income
Definition: game_config.cpp:38
const version_info wesnoth_version(VERSION)
const bool & debug
Definition: game_config.cpp:92
int kill_experience
Definition: game_config.cpp:41
int combat_experience
Definition: game_config.cpp:42
int village_support
Definition: game_config.cpp:39
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
void remove_single_widget_definition(const std::string &widget_type, const std::string &definition_id)
Removes a widget definition from the default GUI.
logger & err()
Definition: log.cpp:304
logger & warn()
Definition: log.cpp:310
logger & info()
Definition: log.cpp:316
std::string register_metatables(lua_State *L)
Definition: lua_color.cpp:151
int intf_textdomain(lua_State *L)
Creates an interface for gettext.
Definition: lua_common.cpp:93
std::string register_gettext_metatable(lua_State *L)
Adds the gettext metatable.
Definition: lua_common.cpp:421
std::string register_tstring_metatable(lua_State *L)
Adds the tstring metatable.
Definition: lua_common.cpp:441
void register_metatable(lua_State *L)
int luaW_open(lua_State *L)
int load_file(lua_State *L)
Loads a Lua file and pushes the contents on the stack.
std::string register_metatables(lua_State *)
int intf_compile_formula(lua_State *)
int intf_eval_formula(lua_State *)
Evaluates a formula in the formula engine.
int luaW_open(lua_State *L)
Definition: lua_gui2.cpp:273
int show_lua_console(lua_State *, lua_kernel_base *lk)
Definition: lua_gui2.cpp:235
int intf_get_relative_dir(lua_State *L)
Expose map_location get_relative_dir.
int intf_vector_negation(lua_State *L)
Expose map_location::vector_negation to lua.
int intf_distance_between(lua_State *L)
Expose map_location distance_between.
int intf_get_in_basis_N_NE(lua_State *L)
Expose map_location get_in_basis_N_NE.
int intf_tiles_adjacent(lua_State *L)
Expose map_location tiles_adjacent.
int intf_vector_diff(lua_State *L)
Expose map_location::vector_difference to lua.
int intf_vector_sum(lua_State *L)
Expose map_location::vector_sum to lua.
int intf_rotate_right_around_center(lua_State *L)
Expose map_location::rotate_right_around_center to lua.
int intf_get_tiles_in_radius(lua_State *L)
Expose map_location get_tiles_in_radius.
int intf_get_adjacent_tiles(lua_State *L)
Expose map_location get_adjacent_tiles.
int intf_get_direction(lua_State *L)
Expose map_location::get_direction function to lua Arg 1: a location Arg 2: a direction Arg 3: (optio...
int luaW_open(lua_State *L)
Definition: lua_mathx.cpp:61
void load_tables(lua_State *L)
Creates the metatable for RNG objects, and adds the Rng table which contains the constructor.
Definition: lua_rng.cpp:79
int luaW_open(lua_State *L)
void register_metatable(lua_State *L)
Definition: lua_widget.cpp:187
int luaW_open(lua_State *L)
Definition: lua_wml.cpp:243
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
uint32_t next_seed()
Definition: seed_rng.cpp:32
std::vector< std::string > parenthetical_split(std::string_view val, const char separator, std::string_view left, std::string_view right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
std::vector< std::string > split(const config_attribute_value &val)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::string_view data
Definition: picture.cpp:194
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:373
static std::string flush(std::ostringstream &s)
Definition: reports.cpp:89
#define ON_SCOPE_EXIT(...)
Run some arbitrary code (a lambda) when the current scope exits The lambda body follows this header,...
Definition: scope_exit.hpp:43
Error used to report an error in a lua script or in the lua interpreter.
Definition: game_errors.hpp:54
mock_char c
mock_party p
static map_location::DIRECTION n
#define e