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