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