The Battle for Wesnoth  1.19.8+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, const 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, const 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(const std::string& prog, const std::string& name, const error_handler& e_h, bool allow_unsafe)
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.c_str(), prog.size(), name.empty() ? prog.c_str() : name.c_str(), allow_unsafe ? "tb" : "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 
1105 config luaW_serialize_function(lua_State* L, int func)
1106 {
1107  if(lua_iscfunction(L, func)) {
1108  throw luafunc_serialize_error("cannot serialize C function");
1109  }
1110  if(!lua_isfunction(L, func)) {
1111  throw luafunc_serialize_error("cannot serialize callable non-function");
1112  }
1113  config data;
1114  lua_Debug info;
1115  lua_pushvalue(L, func); // push copy of function because lua_getinfo will pop it
1116  lua_getinfo(L, ">u", &info);
1117  data["params"] = info.nparams;
1118  luaW_getglobal(L, "string", "dump");
1119  lua_pushvalue(L, func);
1120  lua_call(L, 1, 1);
1121  data["code"] = lua_check<std::string>(L, -1);
1122  lua_pop(L, 1);
1123  config upvalues;
1124  for(int i = 1; i <= info.nups; i++, lua_pop(L, 1)) {
1125  std::string_view name = lua_getupvalue(L, func, i);
1126  if(name == "_ENV") {
1127  upvalues.add_child(name)["upvalue_type"] = "_ENV";
1128  continue;
1129  }
1130  int idx = lua_absindex(L, -1);
1131  switch(lua_type(L, idx)) {
1132  case LUA_TBOOLEAN: case LUA_TNUMBER: case LUA_TSTRING:
1133  luaW_toscalar(L, idx, upvalues[name]);
1134  break;
1135  case LUA_TFUNCTION:
1136  upvalues.add_child(name, luaW_serialize_function(L, idx))["upvalue_type"] = "function";
1137  break;
1138  case LUA_TNIL:
1139  upvalues.add_child(name, config{"upvalue_type", "nil"});
1140  break;
1141  case LUA_TTABLE:
1142  if(std::vector<std::string> names = luaW_to_namedtuple(L, idx); !names.empty()) {
1143  for(size_t i = 1; i <= lua_rawlen(L, -1); i++, lua_pop(L, 1)) {
1144  lua_rawgeti(L, idx, i);
1145  config& cfg = upvalues.add_child(name);
1146  luaW_toscalar(L, -1, cfg["value"]);
1147  cfg["name"] = names[0];
1148  cfg["upvalue_type"] = "named tuple";
1149  names.erase(names.begin());
1150  }
1151  break;
1152  } else if(config cfg; luaW_toconfig(L, idx, cfg)) {
1153  std::vector<std::string> names;
1154  int save_top = lua_gettop(L);
1155  if(luaL_getmetafield(L, idx, "__name") && lua_check<std::string>(L, -1) == "named tuple") {
1156  luaL_getmetafield(L, -2, "__names");
1157  names = lua_check<std::vector<std::string>>(L, -1);
1158  }
1159  lua_settop(L, save_top);
1160  upvalues.add_child(name, cfg)["upvalue_type"] = names.empty() ? "config" : "named tuple";
1161  break;
1162  } else {
1163  for(size_t i = 1; i <= lua_rawlen(L, -1); i++, lua_pop(L, 1)) {
1164  lua_rawgeti(L, idx, i);
1165  config& cfg = upvalues.add_child(name);
1166  luaW_toscalar(L, -1, cfg["value"]);
1167  cfg["upvalue_type"] = "array";
1168  }
1169  bool found_non_array = false;
1170  for(lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
1171  if(lua_type(L, -2) != LUA_TNUMBER) {
1172  found_non_array = true;
1173  break;
1174  }
1175  }
1176  if(!found_non_array) break;
1177  }
1178  [[fallthrough]];
1179  default:
1180  std::ostringstream os;
1181  os << "cannot serialize function with upvalue " << name << " = ";
1182  luaW_getglobal(L, "wesnoth", "as_text");
1183  lua_pushvalue(L, idx);
1184  lua_call(L, 1, 1);
1185  os << luaL_checkstring(L, -1);
1186  lua_pushboolean(L, false);
1187  throw luafunc_serialize_error(os.str());
1188  }
1189  }
1190  if(!upvalues.empty()) data.add_child("upvalues", upvalues);
1191  return data;
1192 }
1193 
1195 {
1196  if(!load_string(cfg["code"].str(), cfg["name"], eh, true)) return false;
1197  if(auto upvalues = cfg.optional_child("upvalues")) {
1198  lua_pushvalue(mState, -1); // duplicate function because lua_getinfo will pop it
1199  lua_Debug info;
1200  lua_getinfo(mState, ">u", &info);
1201  int funcindex = lua_absindex(mState, -1);
1202  for(int i = 1; i <= info.nups; i++) {
1203  std::string_view name = lua_getupvalue(mState, funcindex, i);
1204  lua_pop(mState, 1); // we only want the upvalue's name, not its value
1205  if(name == "_ENV") {
1206  lua_pushglobaltable(mState);
1207  } else if(upvalues->has_attribute(name)) {
1208  luaW_pushscalar(mState, (*upvalues)[name]);
1209  } else if(upvalues->has_child(name)) {
1210  const auto& child = upvalues->mandatory_child(name);
1211  if(child["upvalue_type"] == "array") {
1212  auto children = upvalues->child_range(name);
1213  lua_createtable(mState, children.size(), 0);
1214  for(const auto& cfg : children) {
1215  luaW_pushscalar(mState, cfg["value"]);
1216  lua_rawseti(mState, -2, lua_rawlen(mState, -2) + 1);
1217  }
1218  } else if(child["upvalue_type"] == "config") {
1219  luaW_pushconfig(mState, child);
1220  } else if(child["upvalue_type"] == "function") {
1221  if(!load_binary(child, eh)) return false;
1222  } else if(child["upvalue_type"] == "nil") {
1223  lua_pushnil(mState);
1224  }
1225  } else continue;
1226  lua_setupvalue(mState, funcindex, i);
1227  }
1228  }
1229  return true;
1230 }
1231 
1233 {
1234  int top = lua_gettop(mState);
1235  try {
1236  error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1237  if(load_binary(cfg, eh)) {
1238  lua_pushvalue(mState, -1);
1239  protected_call(0, LUA_MULTRET, eh);
1240  }
1241  } catch (const game::lua_error & e) {
1242  cmd_log_ << e.what() << "\n";
1243  lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
1244  config error;
1245  error["name"] = "execute_error";
1246  error["error"] = e.what();
1247  return error;
1248  }
1249  config result;
1250  result["ref"] = cfg["ref"];
1251  result.add_child("executed") = luaW_serialize_function(mState, top + 1);
1252  lua_remove(mState, top + 1);
1253  result["name"] = "execute_result";
1254  for(int i = top + 1; i < lua_gettop(mState); i++) {
1255  std::string index = std::to_string(i - top);
1256  switch(lua_type(mState, i)) {
1257  case LUA_TNUMBER: case LUA_TBOOLEAN: case LUA_TSTRING:
1258  luaW_toscalar(mState, i, result[index]);
1259  break;
1260  case LUA_TTABLE:
1261  luaW_toconfig(mState, i, result.add_child(index));
1262  break;
1263  }
1264  }
1265  return result;
1266 }
1267 // Call load_string and protected call. Make them throw exceptions.
1268 //
1269 void lua_kernel_base::throwing_run(const char * prog, const std::string& name, int nArgs, bool in_interpreter)
1270 {
1271  cmd_log_ << "$ " << prog << "\n";
1272  error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1273  this->load_string(prog, name, eh);
1274  if(in_interpreter) {
1275  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1276  if(lua_setupvalue(mState, -2, 1) == nullptr)
1277  lua_pop(mState, 1);
1278  }
1279  lua_insert(mState, -nArgs - 1);
1280  this->protected_call(nArgs, in_interpreter ? LUA_MULTRET : 0, eh);
1281 }
1282 
1283 // Do a throwing run, but if we catch a lua_error, reformat it with signature for this function and log it.
1284 void lua_kernel_base::run(const char * prog, const std::string& name, int nArgs)
1285 {
1286  try {
1287  this->throwing_run(prog, name, nArgs);
1288  } catch (const game::lua_error & e) {
1289  cmd_log_ << e.what() << "\n";
1290  lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
1291  }
1292 }
1293 
1294 // Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it normally. Throws exceptions.
1295 void lua_kernel_base::interactive_run(char const * prog) {
1296  std::string experiment = "return ";
1297  experiment += prog;
1298  int top = lua_gettop(mState);
1299 
1300  error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1301  luaW_getglobal(mState, "ilua", "_pretty_print");
1302 
1303  try {
1304  // Try to load the experiment without syntax errors
1305  this->load_string(experiment.c_str(), "interactive", eh);
1306  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1307  if(lua_setupvalue(mState, -2, 1) == nullptr)
1308  lua_pop(mState, 1);
1309  } catch (const game::lua_error &) {
1310  this->throwing_run(prog, "interactive", 0, true); // Since it failed, fall back to the usual throwing_run, on the original input.
1311  if(lua_gettop(mState) == top + 1) {
1312  // Didn't return anything
1313  lua_settop(mState, top);
1314  return;
1315  } else goto PRINT;
1316  }
1317  // experiment succeeded, now run but log normally.
1318  cmd_log_ << "$ " << prog << "\n";
1319  this->protected_call(0, LUA_MULTRET, eh);
1320 PRINT:
1321  int nRets = lua_gettop(mState) - top - 1;
1322  {
1323  // Assign first result to _
1324  lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1325  int env_idx = lua_gettop(mState);
1326  lua_pushvalue(mState, top + 2);
1327  lua_setfield(mState, -2, "_");
1328  // Now duplicate EVERY result and pass it to table.pack, assigning to _all
1329  luaW_getglobal(mState, "table", "pack");
1330  for(int i = top + 2; i < env_idx; i++)
1331  lua_pushvalue(mState, i);
1332  this->protected_call(nRets, 1, eh);
1333  lua_setfield(mState, -2, "_all");
1334  lua_pop(mState, 1);
1335  }
1336  // stack is now ilua._pretty_print followed by any results of prog
1337  this->protected_call(lua_gettop(mState) - top - 1, 0, eh);
1338 }
1339 /**
1340  * Loads and executes a Lua file.
1341  * - Arg 1: string containing the file name.
1342  * - Ret *: values returned by executing the file body.
1343  */
1345 {
1346  luaL_checkstring(L, 1);
1347  lua_rotate(L, 1, -1);
1348  if (lua_fileops::load_file(L) != 1) return 0;
1349  //^ should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1350  lua_rotate(L, 1, 1);
1351  // Using a non-protected call here appears to fix an issue in plugins.
1352  // The protected call isn't technically necessary anyway, because this function is called from Lua code,
1353  // which should already be in a protected environment.
1354  lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
1355  return lua_gettop(L);
1356 }
1357 
1358 /**
1359  * Loads and executes a Lua file, if there is no corresponding entry in wesnoth.package.
1360  * Stores the result of the script in wesnoth.package and returns it.
1361  * - Arg 1: string containing the file name.
1362  * - Ret 1: value returned by the script.
1363  */
1365 {
1366  const char * m = luaL_checkstring(L, 1);
1367  if(!m) {
1368  return luaL_argerror(L, 1, "found a null string argument to wesnoth require");
1369  }
1370 
1371  // Check if there is already an entry.
1372 
1373  lua_getglobal(L, "wesnoth");
1374  lua_pushstring(L, "package");
1375  lua_rawget(L, -2);
1376  lua_pushvalue(L, 1);
1377  lua_rawget(L, -2);
1378  if(!lua_isnil(L, -1) && !game_config::debug_lua) {
1379  return 1;
1380  }
1381  lua_pop(L, 1);
1382  lua_pushvalue(L, 1);
1383  // stack is now [packagename] [wesnoth] [package] [packagename]
1384 
1385  if(lua_fileops::load_file(L) != 1) {
1386  // should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1387  // stack is now [packagename] [wesnoth] [package] [chunk]
1388  return 0;
1389  }
1390  DBG_LUA << "require: loaded a file, now calling it";
1391 
1392  if (!this->protected_call(L, 0, 1, std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2))) {
1393  // historically if wesnoth.require fails it just yields nil and some logging messages, not a lua error
1394  return 0;
1395  }
1396  // stack is now [packagename] [wesnoth] [package] [results]
1397 
1398  lua_pushvalue(L, 1);
1399  lua_pushvalue(L, -2);
1400  // stack is now [packagename] [wesnoth] [package] [results] [packagename] [results]
1401  // Add the return value to the table.
1402 
1403  lua_settable(L, -4);
1404  // stack is now [packagename] [wesnoth] [package] [results]
1405  return 1;
1406 }
1408 {
1409  lua_push(L, my_name());
1410  return 1;
1411 }
1412 static void push_color_palette(lua_State* L, const std::vector<color_t>& palette) {
1413  lua_createtable(L, palette.size(), 1);
1414  lua_rotate(L, -2, 1); // swap new table with previous element on stack
1415  lua_setfield(L, -2, "name");
1416  for(size_t i = 0; i < palette.size(); i++) {
1417  luaW_push_namedtuple(L, {"r", "g", "b", "a"});
1418  lua_pushinteger(L, palette[i].r);
1419  lua_rawseti(L, -2, 1);
1420  lua_pushinteger(L, palette[i].g);
1421  lua_rawseti(L, -2, 2);
1422  lua_pushinteger(L, palette[i].b);
1423  lua_rawseti(L, -2, 3);
1424  lua_pushinteger(L, palette[i].a);
1425  lua_rawseti(L, -2, 4);
1426  lua_rawseti(L, -2, i);
1427  }
1428 }
1429 static int impl_palette_get(lua_State* L)
1430 {
1431  char const *m = luaL_checkstring(L, 2);
1432  lua_pushvalue(L, 2);
1434  return 1;
1435 }
1436 
1437 // suppress missing prototype warning (not static because game_lua_kernel referenes it);
1440  static luaW_Registry gameConfigReg{"game config"};
1441  return gameConfigReg;
1442 }
1443 static auto& dummy = gameConfigReg(); // just to ensure it's constructed.
1444 
1445 #define GAME_CONFIG_SIMPLE_GETTER(name) \
1446 GAME_CONFIG_GETTER(#name, decltype(game_config::name), lua_kernel_base) { \
1447  (void) k; \
1448  return game_config::name; \
1449 }
1450 
1451 namespace {
1464 
1466  (void)k;
1467  lua_newtable(L);
1468  if(luaL_newmetatable(L, "color palettes")) {
1469  lua_pushcfunction(L, impl_palette_get);
1470  lua_setfield(L, -2, "__index");
1471  }
1472  lua_setmetatable(L, -2);
1473  return lua_index_raw(L);
1474 }
1475 
1476 GAME_CONFIG_GETTER("red_green_scale", lua_index_raw, lua_kernel_base) {
1477  (void)k;
1478  lua_pushstring(L, "red_green_scale");
1480  return lua_index_raw(L);
1481 }
1482 
1483 GAME_CONFIG_GETTER("red_green_scale_text", lua_index_raw, lua_kernel_base) {
1484  (void)k;
1485  lua_pushstring(L, "red_green_scale_text");
1487  return lua_index_raw(L);
1488 }
1489 
1490 GAME_CONFIG_GETTER("blue_white_scale", lua_index_raw, lua_kernel_base) {
1491  (void)k;
1492  lua_pushstring(L, "blue_white_scale");
1494  return lua_index_raw(L);
1495 }
1496 
1497 GAME_CONFIG_GETTER("blue_white_scale_text", lua_index_raw, lua_kernel_base) {
1498  (void)k;
1499  lua_pushstring(L, "blue_white_scale_text");
1501  return lua_index_raw(L);
1502 }
1503 }
1504 
1505 /**
1506  * Gets some game_config data (__index metamethod).
1507  * - Arg 1: userdata (ignored).
1508  * - Arg 2: string containing the name of the property.
1509  * - Ret 1: something containing the attribute.
1510  */
1512 {
1513  return gameConfigReg().get(L);
1514 }
1515 /**
1516  * Sets some game_config data (__newindex metamethod).
1517  * - Arg 1: userdata (ignored).
1518  * - Arg 2: string containing the name of the property.
1519  * - Arg 3: something containing the attribute.
1520  */
1522 {
1523  return gameConfigReg().set(L);
1524 }
1525 /**
1526  * Gets a list of game_config data (__dir metamethod).
1527  */
1529 {
1530  return gameConfigReg().dir(L);
1531 }
1532 /**
1533  * Loads the "package" package into the Lua environment.
1534  * This action is inherently unsafe, as Lua scripts will now be able to
1535  * load C libraries on their own, hence granting them the same privileges
1536  * as the Wesnoth binary itself.
1537  */
1539 {
1540  lua_State *L = mState;
1541  lua_pushcfunction(L, luaopen_package);
1542  lua_pushstring(L, "package");
1543  lua_call(L, 1, 0);
1544 }
1545 
1547 {
1548  lua_State* L = mState;
1549  lua_settop(L, 0);
1550  cmd_log_ << "Loading core...\n";
1551  luaW_getglobal(L, "wesnoth", "require");
1552  lua_pushstring(L, "lua/core");
1553  if(!protected_call(1, 1)) {
1554  cmd_log_ << "Error: Failed to load core.\n";
1555  }
1556  lua_settop(L, 0);
1557 }
1558 
1559 /**
1560  * Gets all the global variable names in the Lua environment. This is useful for tab completion.
1561  */
1562 std::vector<std::string> lua_kernel_base::get_global_var_names()
1563 {
1564  std::vector<std::string> ret;
1565 
1566  lua_State *L = mState;
1567 
1568  int idx = lua_gettop(L);
1569  lua_getglobal(L, "_G");
1570  lua_pushnil(L);
1571 
1572  while (lua_next(L, idx+1) != 0) {
1573  if (lua_isstring(L, -2)) {
1574  ret.push_back(lua_tostring(L,-2));
1575  }
1576  lua_pop(L,1);
1577  }
1578  lua_settop(L, idx);
1579  return ret;
1580 }
1581 
1582 /**
1583  * Gets all attribute names of an extended variable name. This is useful for tab completion.
1584  */
1585 std::vector<std::string> lua_kernel_base::get_attribute_names(const std::string & input)
1586 {
1587  std::vector<std::string> ret;
1588  std::string base_path = input;
1589  std::size_t last_dot = base_path.find_last_of('.');
1590  std::string partial_name = base_path.substr(last_dot + 1);
1591  base_path.erase(last_dot);
1592  std::string load = "return " + base_path;
1593 
1594  lua_State* L = mState;
1595  int save_stack = lua_gettop(L);
1596  int result = luaL_loadstring(L, load.c_str());
1597  if(result != LUA_OK) {
1598  // 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.
1599  LOG_LUA << "Error when attempting tab completion:";
1600  LOG_LUA << luaL_checkstring(L, -1);
1601  // Just return an empty list; no matches were found
1602  lua_settop(L, save_stack);
1603  return ret;
1604  }
1605 
1606  luaW_pcall(L, 0, 1);
1607  if(lua_istable(L, -1) || lua_isuserdata(L, -1)) {
1608  int top = lua_gettop(L);
1609  int obj = lua_absindex(L, -1);
1610  if(luaL_getmetafield(L, obj, "__tab_enum") == LUA_TFUNCTION) {
1611  lua_pushvalue(L, obj);
1612  lua_pushlstring(L, partial_name.c_str(), partial_name.size());
1613  luaW_pcall(L, 2, 1);
1614  ret = lua_check<std::vector<std::string>>(L, -1);
1615  } else if(lua_type(L, -1) != LUA_TTABLE) {
1616  LOG_LUA << "Userdata missing __tab_enum meta-function for tab completion";
1617  lua_settop(L, save_stack);
1618  return ret;
1619  } else {
1620  lua_settop(L, top);
1621  // Metafunction not found, so use lua_next to enumerate the table
1622  for(lua_pushnil(L); lua_next(L, obj); lua_pop(L, 1)) {
1623  if(lua_type(L, -2) == LUA_TSTRING) {
1624  std::string attr = lua_tostring(L, -2);
1625  if(attr.empty()) {
1626  continue;
1627  }
1628  if(!isalpha(attr[0]) && attr[0] != '_') {
1629  continue;
1630  }
1631  if(std::any_of(attr.begin(), attr.end(), [](char c){
1632  return !isalpha(c) && !isdigit(c) && c != '_';
1633  })) {
1634  continue;
1635  }
1636  if(attr.substr(0, partial_name.size()) == partial_name) {
1637  ret.push_back(base_path + "." + attr);
1638  }
1639  }
1640  }
1641  }
1642  }
1643  lua_settop(L, save_stack);
1644  return ret;
1645 }
1646 
1648 {
1649  #ifdef __GNUC__
1650  #pragma GCC diagnostic push
1651  #pragma GCC diagnostic ignored "-Wold-style-cast"
1652  #endif
1653  return *reinterpret_cast<lua_kernel_base**>(lua_getextraspace(L));
1654  #ifdef __GNUC__
1655  #pragma GCC diagnostic pop
1656  #endif
1657 }
1658 
1660 {
1661  return seed_rng::next_seed();
1662 }
map_location loc
Definition: move.cpp:172
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:158
bool empty() const
Definition: config.cpp:849
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:384
config & add_child(config_key_type key)
Definition: config.cpp:440
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, const 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(const std::string &prog, const std::string &name, const error_handler &, bool allow_unsafe=false)
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.
bool load_binary(const config &func, const error_handler &)
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.
config run_binary_lua_tag(const config &cfg)
Runs a binary [lua] tag.
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:762
void read(const std::string &shroud_data)
Definition: team.cpp:877
void set_enabled(bool enabled)
Definition: team.hpp:61
int width() const
Definition: team.cpp:757
bool value(int x, int y) const
Definition: team.cpp:820
std::string write() const
Definition: team.cpp:861
bool clear(int x, int y)
Definition: team.cpp:770
const std::string & str() const
Definition: tstring.hpp:199
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:1029
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:279
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:905
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:763
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:888
bool luaW_toboolean(lua_State *L, int n)
int luaW_type_error(lua_State *L, int narg, const char *tname)
bool luaW_toscalar(lua_State *L, int index, config::attribute_value &v)
Converts the value at the top of the stack to an attribute value.
Definition: lua_common.cpp:583
void luaW_pushscalar(lua_State *L, const config::attribute_value &v)
Converts an attribute value into a Lua object pushed at the top of the stack.
Definition: lua_common.cpp:578
std::vector< std::string > luaW_to_namedtuple(lua_State *L, int idx)
Get the keys of a "named tuple" from the stack.
Definition: lua_common.cpp:795
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.
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:934
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:876
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.
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:266
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:277
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:297
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)
config luaW_serialize_function(lua_State *L, int func)
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:187
int rest_heal_amount
Definition: game_config.cpp:48
int village_income
Definition: game_config.cpp:41
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:44
int combat_experience
Definition: game_config.cpp:45
std::vector< color_t > red_green_scale
std::vector< color_t > blue_white_scale_text
int village_support
Definition: game_config.cpp:42
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:37
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:307
logger & warn()
Definition: log.cpp:313
logger & info()
Definition: log.cpp:319
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:397
int show_lua_console(lua_State *, lua_kernel_base *lk)
Definition: lua_gui2.cpp:250
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:212
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::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto transform
Definition: ranges.hpp:41
constexpr auto keys
Definition: ranges.hpp:39
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:95
#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.
int wml_y() const
Definition: location.hpp:184
int wml_x() const
Definition: location.hpp:183
mock_char c
mock_party p
static map_location::direction n
#define e
#define b