The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_kernel_base.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
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
30#endif
31
39#include "scripting/lua_wml.hpp"
43#include "scripting/lua_rng.hpp"
46
47#include "game_version.hpp" // for do_version_check, etc
48
49#include <functional>
53#include "utils/scope_exit.hpp"
54
55#include <SDL3/SDL_timer.h>
56
57#include <cstring>
58#include <string>
59#include <sstream>
60#include <vector>
61#include <numeric>
62
63#include "lua/wrapper_lualib.h"
64
65static lg::log_domain log_scripting_lua("scripting/lua");
66static 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
73static const char * Gen = "name generator";
74static const char * Version = "version";
75// Registry key for lua interpreter environment
76static 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 */
85template<VERSION_COMP_OP vop>
86static 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 */
98static 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 && std::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
126static 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 */
136static 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 */
146static 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 */
156static 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 */
204static int intf_current_version(lua_State* L) {
205 lua_settop(L, 0);
208 return 1;
209}
210
211static int intf_safe_tostring(lua_State* L)
212{
213 // tostring requires one argument. Report an error when the stack has no first value.
214 luaL_checkany(L, 1);
215
216 // Shared __tostring methods make Lua's standard conversion safe for functions,
217 // threads, and light userdata. Tables and full userdata use individual metatables,
218 // so values without __tostring are handled below instead of passed to luaL_tolstring.
219
220 // If the value defines __tostring, use it exactly as Lua normally would.
221 // The generic type name below is only for values without their own string representation.
222 const int tostring_type = luaL_getmetafield(L, 1, "__tostring");
223 if(tostring_type != LUA_TNIL
224 || (lua_type(L, 1) != LUA_TTABLE && lua_type(L, 1) != LUA_TUSERDATA)) {
225 if(tostring_type != LUA_TNIL) {
226 lua_pop(L, 1);
227 }
228 luaL_tolstring(L, 1, nullptr);
229 return 1;
230 }
231
232 // Named metatables already provide base descriptions. Reuse them for stringification.
233 const int name_type = luaL_getmetafield(L, 1, "__name");
234 if(name_type == LUA_TSTRING) {
235 return 1;
236 }
237 if(name_type != LUA_TNIL) {
238 lua_pop(L, 1);
239 }
240 lua_pushstring(L, luaL_typename(L, 1));
241 return 1;
242}
243
244static int intf_std_print(lua_State* L)
245{
246 // Lua's print calls luaL_tolstring directly instead of the global
247 // tostring function. Tables and userdata without __tostring would therefore
248 // include memory addresses. Use Wesnoth's replacement without changing the
249 // order in which arguments are converted and written.
250 const int nargs = lua_gettop(L);
251 lua_getglobal(L, "tostring");
252 for(int i = 1; i <= nargs; ++i) {
253 lua_pushvalue(L, -1);
254 lua_pushvalue(L, i);
255 const int status = lua_pcall(L, 1, 1, 0);
256 // A jailbreak exception must not be returned as a normal Lua error.
258 if(status != LUA_OK) {
259 return lua_error(L);
260 }
261
262 std::size_t length = 0;
263 const char* string = luaL_checklstring(L, -1, &length);
264 if(i > 1) {
265 lua_writestring("\t", 1);
266 }
267 lua_writestring(string, length);
268 lua_pop(L, 1);
269 }
270 lua_pop(L, 1);
271 lua_writeline();
272 return 0;
273}
274
275/**
276 * Replacement print function -- instead of printing to std::cout, print to the command log.
277 * Intended to be bound to this' command_log at registration time.
278 */
280{
281 DBG_LUA << "intf_print called:";
282 std::size_t nargs = lua_gettop(L);
283
284 lua_getglobal(L, "tostring");
285 for (std::size_t i = 1; i <= nargs; ++i) {
286 lua_pushvalue(L, -1); // function to call: "tostring"
287 lua_pushvalue(L, i); // value to pass through tostring() before printing
288 lua_call(L, 1, 1);
289 const char * str = lua_tostring(L, -1);
290 if (!str) {
291 LOG_LUA << "'tostring' must return a value to 'print'";
292 str = "";
293 }
294 if (i > 1) {
295 cmd_log_ << "\t"; //separate multiple args with tab character
296 }
297 cmd_log_ << str;
298 DBG_LUA << "'" << str << "'";
299 lua_pop(L, 1); // Pop the output of tostrring()
300 }
301 lua_pop(L, 1); // Pop 'tostring' global
302
303 cmd_log_ << "\n";
304 DBG_LUA;
305
306 return 0;
307}
308
309static void impl_warn(void* p, const char* msg, int tocont) {
310 static const char*const prefix = "Warning:\n ";
311 static std::ostringstream warning(prefix);
312 warning.seekp(0, std::ios::end);
313 warning << msg << ' ';
314 if(!tocont) {
315 auto L = reinterpret_cast<lua_State*>(p);
316 luaW_getglobal(L, "debug", "traceback");
317 lua_push(L, warning.str());
318 lua_pushinteger(L, 2);
319 lua_call(L, 2, 1);
320 auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
321 lk.add_log_to_console(luaL_checkstring(L, -1));
322 warning.str(prefix);
323 }
324}
325
326void lua_kernel_base::add_log_to_console(const std::string& msg) {
327 cmd_log_ << msg << "\n";
328 DBG_LUA << "'" << msg << "'";
329}
330
331/**
332 * Replacement load function. Mostly the same as regular load, but disallows loading binary chunks
333 * due to CVE-2018-1999023.
334 */
335static int intf_load(lua_State* L)
336{
337 std::string chunk = luaL_checkstring(L, 1);
338 const char* name = luaL_optstring(L, 2, chunk.c_str());
339 std::string mode = luaL_optstring(L, 3, "t");
340 bool override_env = !lua_isnone(L, 4);
341
342 if(mode != "t") {
343 return luaL_argerror(L, 3, "binary chunks are not allowed for security reasons");
344 }
345
346 int result = luaL_loadbufferx(L, chunk.data(), chunk.length(), name, "t");
347 if(result != LUA_OK) {
348 lua_pushnil(L);
349 // Move the nil as the first return value, like Lua's own load() does.
350 lua_insert(L, -2);
351
352 return 2;
353 }
354
355 if(override_env) {
356 // Copy "env" to the top of the stack.
357 lua_pushvalue(L, 4);
358 // Set "env" as the first upvalue.
359 const char* upvalue_name = lua_setupvalue(L, -2, 1);
360 if(upvalue_name == nullptr) {
361 // lua_setupvalue() didn't remove the copy of "env" from the stack, so we need to do it ourselves.
362 lua_pop(L, 1);
363 }
364 }
365
366 return 1;
367}
368
369/**
370 * Wrapper for pcall and xpcall functions to rethrow jailbreak exceptions
371 */
372static int intf_pcall(lua_State *L)
373{
374 lua_CFunction function = lua_tocfunction(L, lua_upvalueindex(1));
375 assert(function); // The upvalue should be Lua's pcall or xpcall, or else something is very wrong.
376
377 int nRets = function(L);
378
379 // If a jailbreak exception was stored while running (x)pcall, rethrow it so Lua doesn't continue.
381
382 return nRets;
383}
384
385// The show lua console callback is similarly a method of lua kernel
387{
389 std::string message = "There is already an external logger attached to this lua kernel, you cannot open the lua console right now.";
390 log_error(message.c_str());
391 cmd_log_ << message << "\n";
392 return 0;
393 }
394
395 return lua_gui2::show_lua_console(L, this);
396}
397
398static int impl_name_generator_call(lua_State *L)
399{
400 name_generator* gen = static_cast<name_generator*>(luaL_checkudata(L, 1, Gen));
401 lua_pushstring(L, gen->generate().c_str());
402 return 1;
403}
404
405static int impl_name_generator_tostring(lua_State *L)
406{
407 name_generator* gen = static_cast<name_generator*>(luaL_checkudata(L, 1, Gen));
408 luaW_getglobal(L, "string", "format");
409 lua_pushstring(L, "%s: %s");
410 lua_pushstring(L, Gen);
411 lua_pushstring(L, gen->type().c_str());
412 lua_call(L, 2, 1);
413 return 1;
414}
415
416static int impl_name_generator_collect(lua_State *L)
417{
418 name_generator* gen = static_cast<name_generator*>(luaL_checkudata(L, 1, Gen));
419 gen->~name_generator();
420 return 0;
421}
422
423static int intf_name_generator(lua_State *L)
424{
425 std::string type = luaL_checkstring(L, 1);
426 name_generator* gen = nullptr;
427 try {
428 if(type == "markov" || type == "markov_chain") {
429 std::vector<std::string> input;
430 if(lua_istable(L, 2)) {
431 input = lua_check<std::vector<std::string>>(L, 2);
432 } else {
433 input = utils::parenthetical_split(luaW_checktstring(L, 2).str(), ',');
434 }
435 int chain_sz = luaL_optinteger(L, 3, 2);
436 int max_len = luaL_optinteger(L, 4, 12);
437 gen = new(L) markov_generator(input, chain_sz, max_len);
438 // Ensure the pointer didn't change when cast
439 assert(static_cast<void*>(gen) == dynamic_cast<markov_generator*>(gen));
440 } else if(type == "context_free" || type == "cfg" || type == "CFG") {
441 if(lua_istable(L, 2)) {
442 std::map<std::string, std::vector<std::string>> data;
443 for(lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
444 if(lua_type(L, -2) != LUA_TSTRING) {
445 lua_pushstring(L, "CFG generator: invalid nonterminal name (must be a string)");
446 return lua_error(L);
447 }
448 if(lua_isstring(L, -1)) {
449 auto& productions = data[lua_tostring(L,-2)] = utils::split(luaW_checktstring(L,-1).str(), '|');
450 if(productions.size() > 1) {
451 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");
452 }
453 } else if(lua_istable(L, -1)) {
454 const auto& split = lua_check<std::vector<t_string>>(L, -1);
455 auto& productions = data[lua_tostring(L,-2)];
456 std::transform(split.begin(), split.end(), std::back_inserter(productions), std::mem_fn(&t_string::str));
457 } else {
458 lua_pushstring(L, "CFG generator: invalid nonterminal value (must be a string or list of strings)");
459 return lua_error(L);
460 }
461 }
462 if(!data.empty()) {
464 }
465 } else {
467 }
468 if(gen) {
469 assert(static_cast<void*>(gen) == dynamic_cast<context_free_grammar_generator*>(gen));
470 }
471 } else {
472 return luaL_argerror(L, 1, "should be either 'markov_chain' or 'context_free'");
473 }
474 }
475 catch (const name_generator_invalid_exception& ex) {
476 lua_pushstring(L, ex.what());
477 return lua_error(L);
478 }
479
480 // We set the metatable now, even if the generator is invalid, so that it
481 // will be properly collected if it was invalid.
482 luaL_getmetatable(L, Gen);
483 lua_setmetatable(L, -2);
484
485 return 1;
486}
487
488/**
489* Logs a message
490* Arg 1: (optional) Logger
491* Arg 2: Message
492*/
493static int intf_log(lua_State *L) {
494 const std::string& logger = lua_isstring(L, 2) ? luaL_checkstring(L, 1) : "";
495 std::string msg = lua_isstring(L, 2) ? luaL_checkstring(L, 2) : luaL_checkstring(L, 1);
496 if(msg.empty() || msg.back() != '\n') {
497 msg += '\n';
498 }
499
500 if(logger == "err" || logger == "error") {
501 LOG_STREAM(err, log_user) << msg;
502 } else if(logger == "warn" || logger == "wrn" || logger == "warning") {
503 LOG_STREAM(warn, log_user) << msg;
504 } else if((logger == "debug" || logger == "dbg")) {
505 LOG_STREAM(debug, log_user) << msg;
506 } else {
507 LOG_STREAM(info, log_user) << msg;
508 }
509 return 0;
510}
511
512/**
513 * Logs a deprecation message. See deprecation.cpp for details
514 * Arg 1: Element to be deprecated.
515 * Arg 2: Deprecation level.
516 * Arg 3: Version when element may be removed.
517 * Arg 4: Additional detail message.
518 */
519static int intf_deprecated_message(lua_State* L) {
520 const std::string elem = luaL_checkstring(L, 1);
521 // This could produce an invalid deprecation level, but that possibility is handled in deprecated_message()
522 const DEP_LEVEL level = DEP_LEVEL(luaL_checkinteger(L, 2));
523 const std::string ver_str = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
524 const std::string detail = luaW_checktstring(L, 4);
525 const version_info ver = ver_str.empty() ? game_config::wesnoth_version.str() : ver_str;
526 const std::string msg = deprecated_message(elem, level, ver, detail);
527 if(level < DEP_LEVEL::INDEFINITE || level >= DEP_LEVEL::REMOVED) {
528 // Invalid deprecation level or level 4 deprecation should raise an interpreter error
529 lua_push(L, msg);
530 return lua_error(L);
531 }
532 lua_warning(L, msg.c_str(), false);
533 return 0;
534}
535
536/**
537 * Converts a Lua array to a named tuple.
538 * Arg 1: A Lua array
539 * Arg 2: An array of strings
540 * Ret: A copy of arg 1 that's now a named tuple with the names in arg 2.
541 * The copy will only include the array portion of the input array.
542 * Any non-integer keys or non-consecutive keys will be gone.
543 * Note: This exists so that wml.tag can use it but is not really intended as a public API.
544 */
545static int intf_named_tuple(lua_State* L)
546{
547 if(!lua_istable(L, 1)) {
548 return luaW_type_error(L, 1, lua_typename(L, LUA_TTABLE));
549 }
550 auto names = lua_check<std::vector<std::string>>(L, 2);
551 lua_len(L, 1);
552 int len = luaL_checkinteger(L, -1);
554 for(int i = 1; i <= std::max<int>(len, names.size()); i++) {
555 lua_geti(L, 1, i);
556 lua_seti(L, -2, i);
557 }
558 return 1;
559}
560
561static int intf_parse_shroud_bitmap(lua_State* L)
562{
563 shroud_map temp;
564 temp.set_enabled(true);
565 temp.read(luaL_checkstring(L, 1));
566 std::set<map_location> locs;
567 for(int x = 1; x <= temp.width(); x++) {
568 for(int y = 1; y <= temp.height(); y++) {
569 if(!temp.value(x, y)) {
570 locs.emplace(x, y, wml_loc());
571 }
572 }
573 }
574 luaW_push_locationset(L, locs);
575 return 1;
576}
577
578static int intf_make_shroud_bitmap(lua_State* L)
579{
580 shroud_map temp;
581 temp.set_enabled(true);
582 auto locs = luaW_check_locationset(L, 1);
583 for(const auto& loc : locs) {
584 temp.clear(loc.wml_x(), loc.wml_y());
585 }
586 lua_push(L, temp.write());
587 return 1;
588}
589
590/**
591* Returns the time stamp, exactly as [set_variable] time=stamp does.
592* - Ret 1: integer
593*/
594static int intf_ms_since_init(lua_State *L) {
595 lua_pushinteger(L, SDL_GetTicks());
596 return 1;
597}
598
599static int intf_get_language(lua_State* L)
600{
601 lua_push(L, get_language().localename);
602 return 1;
603}
604
605static void dir_meta_helper(lua_State* L, std::vector<std::string>& keys)
606{
607 switch(luaL_getmetafield(L, -1, "__dir")) {
608 case LUA_TFUNCTION:
609 lua_pushvalue(L, 1);
610 lua_push(L, keys);
611 if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
612 keys = lua_check<std::vector<std::string>>(L, -1);
613 } else {
614 lua_warning(L, "wesnoth.print_attributes: __dir metamethod raised an error", false);
615 }
616 break;
617 case LUA_TTABLE:
618 auto dir_keys = lua_check<std::vector<std::string>>(L, -1);
619 std::copy(dir_keys.begin(), dir_keys.end(), std::back_inserter(keys));
620 break;
621 }
622 lua_pop(L, 1);
623}
624
625// This is a separate function so I can use a protected call on it to catch errors.
626static int impl_is_deprecated(lua_State* L)
627{
628 auto key = luaL_checkstring(L, 2);
629 auto type = lua_getfield(L, 1, key);
630 if(type == LUA_TTABLE) {
631 lua_pushliteral(L, "__deprecated");
632 if(lua_rawget(L, -2) == LUA_TBOOLEAN) {
633 auto deprecated = luaW_toboolean(L, -1);
634 lua_pushboolean(L, deprecated);
635 return 1;
636 }
637 lua_pop(L, 1);
638 }
639 lua_pushboolean(L, false);
640 return 1;
641}
642
643// This is also a separate function so I can use a protected call on it to catch errors.
644static int impl_get_dir_suffix(lua_State*L)
645{
646 auto key = luaL_checkstring(L, 2);
647 std::string suffix = " ";
648 auto type = lua_getfield(L, 1, key);
649 if(type == LUA_TTABLE) {
650 suffix = "†";
651 } else if(type == LUA_TFUNCTION) {
652 suffix = "Æ’";
653 } else if(type == LUA_TUSERDATA) {
654 lua_getglobal(L, "getmetatable");
655 lua_pushvalue(L, -2);
656 lua_call(L, 1, 1);
657 if(lua_type(L, -1) == LUA_TSTRING) {
658 auto meta = lua_check<std::string>(L, -1);
659 if(meta == "function") {
660 suffix = "Æ’";
661 }
662 }
663 lua_pop(L, 1);
664 if(suffix.size() == 1) {
665 // ie, the above block didn't identify it as a function
666 if(auto t = luaL_getmetafield(L, -1, "__dir_tablelike"); t == LUA_TBOOLEAN) {
667 if(luaW_toboolean(L, -1)) {
668 suffix = "†";
669 }
670 lua_pop(L, 1);
671 } else if(t != LUA_TNIL) {
672 lua_pop(L, 1);
673 }
674 }
675 }
676 suffix = " " + suffix;
677 lua_pushlstring(L, suffix.c_str(), suffix.size());
678 return 1;
679}
680
681/**
682 * This function does the actual work of grabbing all the attribute names.
683 * It's a separate function so that it can be used by tab-completion as well.
684 */
685std::vector<std::string> luaW_get_attributes(lua_State* L, int idx)
686{
687 if(idx < 0 && idx >= -lua_gettop(L)) {
688 idx = lua_absindex(L, idx);
689 }
690 std::vector<std::string> keys;
691 if(lua_istable(L, idx)) {
692 // Walk the metatable chain (as long as __index is a table)...
693 // If we reach an __index that's a function, check for a __dir metafunction.
694 int save_top = lua_gettop(L);
695 lua_pushvalue(L, idx);
696 ON_SCOPE_EXIT(&) {
697 lua_settop(L, save_top);
698 };
699 do {
700 int table_idx = lua_absindex(L, -1);
701 for(lua_pushnil(L); lua_next(L, table_idx); lua_pop(L, 1)) {
702 if(lua_type(L, -2) == LUA_TSTRING) {
703 keys.push_back(lua_tostring(L,-2));
704 }
705 }
706 // Two possible exit cases:
707 // 1. getmetafield returns TNIL because there is no __index
708 // In this case, the stack is unchanged, so the while condition is still true.
709 // 2. The __index is not a table
710 // In this case, obviously the while condition fails
711 if(luaL_getmetafield(L, table_idx, "__index") == LUA_TNIL) break;
712 } while(lua_istable(L, -1));
713 if(lua_isfunction(L, -1)) {
714 lua_pop(L, 1);
715 dir_meta_helper(L, keys);
716 }
717 } else if(lua_isuserdata(L, idx) && !lua_islightuserdata(L, idx)) {
718 lua_pushvalue(L, idx);
719 dir_meta_helper(L, keys);
720 lua_pop(L, 1);
721 }
722 // Sort and remove any duplicates
723 std::sort(keys.begin(), keys.end());
724 auto new_end = std::unique(keys.begin(), keys.end());
725 new_end = std::remove_if(keys.begin(), new_end, [L, idx](const std::string& key) {
726 if(key.compare(0, 2, "__") == 0) {
727 return true;
728 }
729 int save_top = lua_gettop(L);
730 ON_SCOPE_EXIT(&) {
731 lua_settop(L, save_top);
732 };
733 // Exclude deprecated elements
734 // Some keys may be write-only, which would raise an exception here
735 // In that case we just ignore it and assume not deprecated
736 // (the __dir metamethod would be responsible for excluding deprecated write-only keys)
737 lua_pushcfunction(L, impl_is_deprecated);
738 lua_pushvalue(L, idx);
739 lua_push(L, key);
740 if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
741 return luaW_toboolean(L, -1);
742 }
743 return false;
744 });
745 keys.erase(new_end, keys.end());
746 return keys;
747}
748
749/**
750 * Prints out a list of keys available in an object.
751 * A list of keys is gathered from the following sources:
752 * - For a table, all keys defined in the table
753 * - Any keys accessible through the metatable chain (if __index on the metatable is a table)
754 * - The output of the __dir metafunction
755 * - Filtering out any keys beginning with two underscores
756 * - Filtering out any keys for which object[key].__deprecated exists and is true
757 * The list is then sorted alphabetically and formatted into columns.
758 * - Arg 1: Any object
759 * - Arg 2: (optional) Function to use for output; defaults to _G.print
760 */
761static int intf_object_dir(lua_State* L)
762{
763 if(lua_isnil(L, 1)) return luaL_argerror(L, 1, "Can't dir() nil");
764 if(!lua_isfunction(L, 2)) {
765 luaW_getglobal(L, "print");
766 }
767 int fcn_idx = lua_gettop(L);
768 auto keys = luaW_get_attributes(L, 1);
769 std::size_t max_len = std::accumulate(keys.begin(), keys.end(), 0, [](std::size_t max, const std::string& next) {
770 return std::max(max, next.size());
771 });
772 // Let's limit to about 80 characters of total width with minimum 3 characters padding between columns
773 static const std::size_t MAX_WIDTH = 80, COL_PADDING = 3, SUFFIX_PADDING = 2;
774 std::size_t col_width = max_len + COL_PADDING + SUFFIX_PADDING;
775 std::size_t n_cols = (MAX_WIDTH + COL_PADDING) / col_width;
776 std::size_t n_rows = ceil(keys.size() / double(n_cols));
777 for(std::size_t i = 0; i < n_rows; i++) {
778 std::ostringstream line;
779 line.fill(' ');
780 line.setf(std::ios::left);
781 for(std::size_t j = 0; j < n_cols && j + (i * n_cols) < keys.size(); j++) {
782 int save_top = lua_gettop(L);
783 ON_SCOPE_EXIT(&) {
784 lua_settop(L, save_top);
785 };
786 lua_pushcfunction(L, impl_get_dir_suffix);
787 lua_pushvalue(L, 1);
788 const auto& key = keys[j + i * n_cols];
789 lua_pushlstring(L, key.c_str(), key.size());
790 std::string suffix = " !"; // Exclamation mark to indicate an error
791 if(lua_pcall(L, 2, 1, 0) == LUA_OK) {
792 suffix = luaL_checkstring(L, -1);
793 }
794 // This weird calculation is because width counts in bytes, not code points
795 // Since the suffix is a Unicode character, that messes up the alignment
796 line.width(col_width - SUFFIX_PADDING + suffix.size());
797 // Concatenate key and suffix beforehand so they share the same field width.
798 line << (key + suffix) << std::flush;
799 }
800 lua_pushvalue(L, fcn_idx);
801 lua_push(L, line.str());
802 lua_call(L, 1, 0);
803 }
804 return 0;
805}
806
807// End Callback implementations
808
809// Template which allows to push member functions to the lua kernel base into lua as C functions, using a shim
810typedef int (lua_kernel_base::*member_callback)(lua_State *L);
811
812template <member_callback method>
813int dispatch(lua_State *L) {
814 return ((lua_kernel_base::get_lua_kernel<lua_kernel_base>(L)).*method)(L);
815}
816
817// Ctor, initialization
819 : mState(luaL_newstate())
820 , cmd_log_()
821{
823 lua_State *L = mState;
824
825 cmd_log_ << "Initializing " << my_name() << "...\n";
826
827 // Define the CPP_function metatable ( so we can override print to point to a C++ member function, add certain functions for this kernel, etc. )
828 // Do it first of all in case C++ functions are ever used in the core Wesnoth libs loaded in the next step
829 cmd_log_ << "Adding boost function proxy...\n";
830
832
833 // Open safe libraries.
834 // Debug and OS are not, but most of their functions will be disabled below.
835 cmd_log_ << "Adding standard libs...\n";
836
837 static const luaL_Reg safe_libs[] {
838 { "", luaopen_base },
839 { "table", luaopen_table },
840 { "string", luaopen_string },
841 { "math", luaopen_math },
842 { "coroutine", luaopen_coroutine },
843 { "debug", luaopen_debug },
844 { "os", luaopen_os },
845 { "utf8", luaopen_utf8 }, // added in Lua 5.3
846 // Wesnoth libraries
847 { "stringx",lua_stringx::luaW_open },
848 { "mathx", lua_mathx::luaW_open },
849 { "wml", lua_wml::luaW_open },
850 { "gui", lua_gui2::luaW_open },
851 { "filesystem", lua_fileops::luaW_open },
852 { nullptr, nullptr }
853 };
854 for (luaL_Reg const *lib = safe_libs; lib->func; ++lib)
855 {
856 luaL_requiref(L, lib->name, lib->func, strlen(lib->name));
857 lua_pop(L, 1); /* remove lib */
858 }
859
860 // luaopen_base installs the standard tostring function, so install the replacement
861 // after library initialization and before any core or scenario Lua code can run.
862 lua_pushcfunction(L, intf_safe_tostring);
863 lua_setglobal(L, "tostring");
864
865 // Disable functions from os which we don't want.
866 lua_getglobal(L, "os");
867 lua_pushnil(L);
868 while(lua_next(L, -2) != 0) {
869 lua_pop(L, 1);
870 char const* function = lua_tostring(L, -1);
871 if(strcmp(function, "clock") == 0 || strcmp(function, "date") == 0
872 || strcmp(function, "time") == 0 || strcmp(function, "difftime") == 0) continue;
873 lua_pushnil(L);
874 lua_setfield(L, -3, function);
875 }
876 lua_pop(L, 1);
877
878 // Delete dofile and loadfile.
879 lua_pushnil(L);
880 lua_setglobal(L, "dofile");
881 lua_pushnil(L);
882 lua_setglobal(L, "loadfile");
883
884 // Store the error handler.
885 cmd_log_ << "Adding error handler...\n";
887
888
889 lua_settop(L, 0);
890
891 // Add some callback from the wesnoth lib
892 cmd_log_ << "Registering basic wesnoth API...\n";
893
894 static luaL_Reg const callbacks[] {
895 { "deprecated_message", &intf_deprecated_message },
896 { "textdomain", &lua_common::intf_textdomain },
897 { "dofile", &dispatch<&lua_kernel_base::intf_dofile> },
898 { "require", &dispatch<&lua_kernel_base::intf_require> },
899 { "kernel_type", &dispatch<&lua_kernel_base::intf_kernel_type> },
900 { "compile_formula", &lua_formula_bridge::intf_compile_formula},
901 { "eval_formula", &lua_formula_bridge::intf_eval_formula},
902 { "name_generator", &intf_name_generator },
903 { "named_tuple", &intf_named_tuple },
904 { "log", &intf_log },
905 { "ms_since_init", &intf_ms_since_init },
906 { "get_language", &intf_get_language },
907 { "version", &intf_make_version },
908 { "current_version", &intf_current_version },
909 { "print_attributes", &intf_object_dir },
910 { nullptr, nullptr }
911 };
912
913 lua_getglobal(L, "wesnoth");
914 if (!lua_istable(L,-1)) {
915 lua_newtable(L);
916 }
917 luaL_setfuncs(L, callbacks, 0);
918 //lua_cpp::set_functions(L, cpp_callbacks, 0);
919 lua_setglobal(L, "wesnoth");
920
921 // Create the gettext metatable.
923 // Create the tstring metatable.
925
927
928 // Override the print function
929 cmd_log_ << "Redirecting print function...\n";
930
931 lua_pushcfunction(L, intf_std_print);
932 lua_setglobal(L, "std_print");
933 lua_settop(L, 0); //clear stack, just to be sure
934
935 lua_setwarnf(L, &::impl_warn, L);
936 lua_pushcfunction(L, &dispatch<&lua_kernel_base::intf_print>);
937 lua_setglobal(L, "print");
938
939 lua_pushcfunction(L, intf_load);
940 lua_setglobal(L, "load");
941 lua_pushnil(L);
942 lua_setglobal(L, "loadstring");
943
944 // Wrap the pcall and xpcall functions
945 cmd_log_ << "Wrapping pcall and xpcall functions...\n";
946 lua_getglobal(L, "pcall");
947 lua_pushcclosure(L, intf_pcall, 1);
948 lua_setglobal(L, "pcall");
949 lua_getglobal(L, "xpcall");
950 lua_pushcclosure(L, intf_pcall, 1);
951 lua_setglobal(L, "xpcall");
952
953 cmd_log_ << "Initializing package repository...\n";
954 // Create the package table.
955 lua_getglobal(L, "wesnoth");
956 lua_newtable(L);
957 lua_setfield(L, -2, "package");
958 lua_pop(L, 1);
959 lua_settop(L, 0);
960 lua_pushstring(L, "lua/package.lua");
961 int res = intf_require(L);
962 if(res != 1) {
963 cmd_log_ << "Error: Failed to initialize package repository. Falling back to less flexible C++ implementation.\n";
964 }
965
966 // Get some callbacks for map locations
967 cmd_log_ << "Adding map table...\n";
968
969 static luaL_Reg const map_callbacks[] {
970 { "get_direction", &lua_map_location::intf_get_direction },
971 { "hex_vector_sum", &lua_map_location::intf_vector_sum },
972 { "hex_vector_diff", &lua_map_location::intf_vector_diff },
973 { "hex_vector_negation", &lua_map_location::intf_vector_negation },
974 { "rotate_right_around_center", &lua_map_location::intf_rotate_right_around_center },
975 { "are_hexes_adjacent", &lua_map_location::intf_tiles_adjacent },
976 { "get_adjacent_hexes", &lua_map_location::intf_get_adjacent_tiles },
977 { "get_hexes_in_radius", &lua_map_location::intf_get_tiles_in_radius },
978 { "get_hexes_at_radius", &lua_map_location::intf_get_tile_ring },
979 { "distance_between", &lua_map_location::intf_distance_between },
980 { "get_cubic", &lua_map_location::intf_get_in_cubic },
981 { "from_cubic", &lua_map_location::intf_get_from_cubic },
982 { "get_relative_dir", &lua_map_location::intf_get_relative_dir },
983 // Shroud bitmaps
984 {"parse_bitmap", intf_parse_shroud_bitmap},
985 {"make_bitmap", intf_make_shroud_bitmap},
986 { nullptr, nullptr }
987 };
988
989 // Create the map_location table.
990 lua_getglobal(L, "wesnoth");
991 lua_newtable(L);
992 luaL_setfuncs(L, map_callbacks, 0);
993 lua_setfield(L, -2, "map");
994 lua_pop(L, 1);
995
996 // Create the game_config variable with its metatable.
997 cmd_log_ << "Adding game_config table...\n";
998
999 lua_getglobal(L, "wesnoth");
1000 lua_newuserdatauv(L, 0, 0);
1001 lua_createtable(L, 0, 3);
1002 lua_pushcfunction(L, &dispatch<&lua_kernel_base::impl_game_config_get>);
1003 lua_setfield(L, -2, "__index");
1004 lua_pushcfunction(L, &dispatch<&lua_kernel_base::impl_game_config_set>);
1005 lua_setfield(L, -2, "__newindex");
1006 lua_pushcfunction(L, &dispatch<&lua_kernel_base::impl_game_config_dir>);
1007 lua_setfield(L, -2, "__dir");
1008 lua_pushboolean(L, true);
1009 lua_setfield(L, -2, "__dir_tablelike");
1010 lua_pushstring(L, "game config");
1011 lua_setfield(L, -2, "__metatable");
1012 lua_setmetatable(L, -2);
1013 lua_setfield(L, -2, "game_config");
1014 lua_pop(L, 1);
1015
1016 // Add mersenne twister rng wrapper
1017 cmd_log_ << "Adding rng tables...\n";
1019
1020 cmd_log_ << "Adding name generator metatable...\n";
1021 luaL_newmetatable(L, Gen);
1022 static luaL_Reg const generator[] {
1023 { "__call", &impl_name_generator_call},
1024 { "__tostring", &impl_name_generator_tostring},
1025 { "__gc", &impl_name_generator_collect},
1026 { nullptr, nullptr}
1027 };
1028 luaL_setfuncs(L, generator, 0);
1029 lua_pushstring(L, Gen);
1030 lua_setfield(L, -2, "__metatable");
1031
1032 // Create formula bridge metatables
1034
1036
1037 // Create the Lua interpreter table
1038 cmd_log_ << "Sandboxing Lua interpreter...\nTo make variables visible outside the interpreter, assign to _G.variable.\n";
1039 cmd_log_ << "The special variable _ holds the result of the last expression (if any).\n";
1040 lua_newtable(L);
1041 lua_createtable(L, 0, 1);
1042 lua_getglobal(L, "_G");
1043 lua_setfield(L, -2, "__index");
1044 lua_setmetatable(L, -2);
1045 lua_pushcfunction(L, intf_object_dir);
1046 lua_setfield(L, -2, "dir");
1047 lua_setfield(L, LUA_REGISTRYINDEX, Interp);
1048
1049 // Loading ilua:
1050 cmd_log_ << "Loading ilua...\n";
1051
1052 lua_settop(L, 0);
1053 luaW_getglobal(L, "wesnoth", "require");
1054 lua_pushstring(L, "lua/ilua.lua");
1055 if(protected_call(1, 1)) {
1056 //run "ilua.set_strict()"
1057 lua_pushstring(L, "set_strict");
1058 lua_gettable(L, -2);
1059 if (!this->protected_call(0,0, std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2))) {
1060 cmd_log_ << "Failed to activate strict mode.\n";
1061 } else {
1062 cmd_log_ << "Activated strict mode.\n";
1063 }
1064
1065 lua_setglobal(L, "ilua"); //save ilua table as a global
1066 } else {
1067 cmd_log_ << "Error: failed to load ilua.\n";
1068 }
1069 lua_settop(L, 0);
1070
1071 // Disable functions from debug which we don't want.
1072 // We do this last because ilua needs to be able to use debug.getmetatable
1073 lua_getglobal(L, "debug");
1074 lua_pushnil(L);
1075 while(lua_next(L, -2) != 0) {
1076 lua_pop(L, 1);
1077 char const* function = lua_tostring(L, -1);
1078 if(strcmp(function, "traceback") == 0 || strcmp(function, "getinfo") == 0) continue; //traceback is needed for our error handler
1079 lua_pushnil(L); //getinfo is needed for ilua strict mode
1080 lua_setfield(L, -3, function);
1081 }
1082 lua_pop(L, 1);
1083}
1084
1086{
1087 for (const auto& pair : this->registered_widget_definitions_) {
1088 gui2::remove_single_widget_definition(std::get<0>(pair), std::get<1>(pair));
1089 }
1090 lua_close(mState);
1091}
1092
1093void lua_kernel_base::log_error(char const * msg, char const * context)
1094{
1095 ERR_LUA << context << ": " << msg;
1096}
1097
1098void lua_kernel_base::throw_exception(char const * msg, char const * context)
1099{
1100 throw game::lua_error(msg, context);
1101}
1102
1103bool lua_kernel_base::protected_call(int nArgs, int nRets)
1104{
1105 error_handler eh = std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2 );
1106 return this->protected_call(nArgs, nRets, eh);
1107}
1108
1109bool lua_kernel_base::load_string(char const * prog, const std::string& name)
1110{
1111 error_handler eh = std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2 );
1112 return this->load_string(prog, name, eh);
1113}
1114
1115bool lua_kernel_base::protected_call(int nArgs, int nRets, const error_handler& e_h)
1116{
1117 return this->protected_call(mState, nArgs, nRets, e_h);
1118}
1119
1120bool lua_kernel_base::protected_call(lua_State * L, int nArgs, int nRets, const error_handler& e_h)
1121{
1122 int errcode = luaW_pcall_internal(L, nArgs, nRets);
1123
1124 if (errcode != LUA_OK) {
1125 char const * msg = lua_tostring(L, -1);
1126
1127 std::string context = "When executing, ";
1128 if (errcode == LUA_ERRRUN) {
1129 context += "Lua runtime error: ";
1130 } else if (errcode == LUA_ERRERR) {
1131 context += "Lua error in attached debugger: ";
1132 } else if (errcode == LUA_ERRMEM) {
1133 context += "Lua out of memory error: ";
1134 } else {
1135 context += "unknown lua error: ";
1136 }
1137 if(lua_isstring(L, -1)) {
1138 context += msg ? msg : "null string";
1139 } else {
1140 context += lua_typename(L, lua_type(L, -1));
1141 }
1142
1143 lua_pop(L, 1);
1144
1145 e_h(context.c_str(), "Lua Error");
1146
1147 return false;
1148 }
1149
1150 return true;
1151}
1152
1153bool lua_kernel_base::load_string(const std::string& prog, const std::string& name, const error_handler& e_h, bool allow_unsafe)
1154{
1155 // pass 't' to prevent loading bytecode which is unsafe and can be used to escape the sandbox.
1156 int errcode = luaL_loadbufferx(mState, prog.c_str(), prog.size(), name.empty() ? prog.c_str() : name.c_str(), allow_unsafe ? "tb" : "t");
1157 if (errcode != LUA_OK) {
1158 char const * msg = lua_tostring(mState, -1);
1159 std::string message = msg ? msg : "null string";
1160
1161 std::string context = "When parsing a string to lua, ";
1162
1163 if (errcode == LUA_ERRSYNTAX) {
1164 context += " a syntax error";
1165 } else if(errcode == LUA_ERRMEM){
1166 context += " a memory error";
1167 } else {
1168 context += " an unknown error";
1169 }
1170
1171 lua_pop(mState, 1);
1172
1173 e_h(message.c_str(), context.c_str());
1174
1175 return false;
1176 }
1177 return true;
1178}
1179
1181{
1182 int nArgs = 0;
1183 if (auto args = cfg.optional_child("args")) {
1184 luaW_pushconfig(this->mState, *args);
1185 ++nArgs;
1186 }
1187 this->run(cfg["code"].str().c_str(), cfg["name"].str(), nArgs);
1188}
1189
1190config luaW_serialize_function(lua_State* L, int func)
1191{
1192 if(lua_iscfunction(L, func)) {
1193 throw luafunc_serialize_error("cannot serialize C function");
1194 }
1195 if(!lua_isfunction(L, func)) {
1196 throw luafunc_serialize_error("cannot serialize callable non-function");
1197 }
1198 config data;
1199 lua_Debug info;
1200 lua_pushvalue(L, func); // push copy of function because lua_getinfo will pop it
1201 lua_getinfo(L, ">u", &info);
1202 data["params"] = info.nparams;
1203 luaW_getglobal(L, "string", "dump");
1204 lua_pushvalue(L, func);
1205 lua_call(L, 1, 1);
1206 data["code"] = lua_check<std::string>(L, -1);
1207 lua_pop(L, 1);
1208 config upvalues;
1209 for(int i = 1; i <= info.nups; i++, lua_pop(L, 1)) {
1210 std::string_view name = lua_getupvalue(L, func, i);
1211 if(name == "_ENV") {
1212 upvalues.add_child(name)["upvalue_type"] = "_ENV";
1213 continue;
1214 }
1215 int idx = lua_absindex(L, -1);
1216 switch(lua_type(L, idx)) {
1217 case LUA_TBOOLEAN: case LUA_TNUMBER: case LUA_TSTRING:
1218 luaW_toscalar(L, idx, upvalues[name]);
1219 break;
1220 case LUA_TFUNCTION:
1221 upvalues.add_child(name, luaW_serialize_function(L, idx))["upvalue_type"] = "function";
1222 break;
1223 case LUA_TNIL:
1224 upvalues.add_child(name, config{"upvalue_type", "nil"});
1225 break;
1226 case LUA_TTABLE:
1227 if(std::vector<std::string> names = luaW_to_namedtuple(L, idx); !names.empty()) {
1228 for(std::size_t i = 1; i <= lua_rawlen(L, -1); i++, lua_pop(L, 1)) {
1229 lua_rawgeti(L, idx, i);
1230 config& cfg = upvalues.add_child(name);
1231 luaW_toscalar(L, -1, cfg["value"]);
1232 cfg["name"] = names[0];
1233 cfg["upvalue_type"] = "named tuple";
1234 names.erase(names.begin());
1235 }
1236 break;
1237 } else if(config cfg; luaW_toconfig(L, idx, cfg)) {
1238 std::vector<std::string> names;
1239 int save_top = lua_gettop(L);
1240 if(luaL_getmetafield(L, idx, "__name") && lua_check<std::string>(L, -1) == "named tuple") {
1241 luaL_getmetafield(L, -2, "__names");
1242 names = lua_check<std::vector<std::string>>(L, -1);
1243 }
1244 lua_settop(L, save_top);
1245 upvalues.add_child(name, cfg)["upvalue_type"] = names.empty() ? "config" : "named tuple";
1246 break;
1247 } else {
1248 for(std::size_t i = 1; i <= lua_rawlen(L, -1); i++, lua_pop(L, 1)) {
1249 lua_rawgeti(L, idx, i);
1250 config& cfg = upvalues.add_child(name);
1251 luaW_toscalar(L, -1, cfg["value"]);
1252 cfg["upvalue_type"] = "array";
1253 }
1254 bool found_non_array = false;
1255 for(lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
1256 if(lua_type(L, -2) != LUA_TNUMBER) {
1257 found_non_array = true;
1258 break;
1259 }
1260 }
1261 if(!found_non_array) break;
1262 }
1263 [[fallthrough]];
1264 default:
1265 std::ostringstream os;
1266 os << "cannot serialize function with upvalue " << name << " = ";
1267 luaW_getglobal(L, "wesnoth", "as_text");
1268 lua_pushvalue(L, idx);
1269 lua_call(L, 1, 1);
1270 os << luaL_checkstring(L, -1);
1271 lua_pushboolean(L, false);
1272 throw luafunc_serialize_error(os.str());
1273 }
1274 }
1275 if(!upvalues.empty()) data.add_child("upvalues", upvalues);
1276 return data;
1277}
1278
1280{
1281 if(!load_string(cfg["code"].str(), cfg["name"], eh, true)) return false;
1282 if(auto upvalues = cfg.optional_child("upvalues")) {
1283 lua_pushvalue(mState, -1); // duplicate function because lua_getinfo will pop it
1284 lua_Debug info;
1285 lua_getinfo(mState, ">u", &info);
1286 int funcindex = lua_absindex(mState, -1);
1287 for(int i = 1; i <= info.nups; i++) {
1288 std::string_view name = lua_getupvalue(mState, funcindex, i);
1289 lua_pop(mState, 1); // we only want the upvalue's name, not its value
1290 if(name == "_ENV") {
1291 lua_pushglobaltable(mState);
1292 } else if(upvalues->has_attribute(name)) {
1293 luaW_pushscalar(mState, (*upvalues)[name]);
1294 } else if(upvalues->has_child(name)) {
1295 const auto& child = upvalues->mandatory_child(name);
1296 if(child["upvalue_type"] == "array") {
1297 auto children = upvalues->child_range(name);
1298 lua_createtable(mState, children.size(), 0);
1299 for(const auto& cfg : children) {
1300 luaW_pushscalar(mState, cfg["value"]);
1301 lua_rawseti(mState, -2, lua_rawlen(mState, -2) + 1);
1302 }
1303 } else if(child["upvalue_type"] == "config") {
1304 luaW_pushconfig(mState, child);
1305 } else if(child["upvalue_type"] == "function") {
1306 if(!load_binary(child, eh)) return false;
1307 } else if(child["upvalue_type"] == "nil") {
1308 lua_pushnil(mState);
1309 }
1310 } else continue;
1311 lua_setupvalue(mState, funcindex, i);
1312 }
1313 }
1314 return true;
1315}
1316
1318{
1319 int top = lua_gettop(mState);
1320 try {
1321 error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1322 if(load_binary(cfg, eh)) {
1323 lua_pushvalue(mState, -1);
1324 protected_call(0, LUA_MULTRET, eh);
1325 }
1326 } catch (const game::lua_error & e) {
1327 cmd_log_ << e.what() << "\n";
1328 lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
1329 config error;
1330 error["name"] = "execute_error";
1331 error["error"] = e.what();
1332 return error;
1333 }
1334 config result;
1335 result["ref"] = cfg["ref"];
1336 result.add_child("executed") = luaW_serialize_function(mState, top + 1);
1337 lua_remove(mState, top + 1);
1338 result["name"] = "execute_result";
1339 for(int i = top + 1; i < lua_gettop(mState); i++) {
1340 std::string index = std::to_string(i - top);
1341 switch(lua_type(mState, i)) {
1342 case LUA_TNUMBER: case LUA_TBOOLEAN: case LUA_TSTRING:
1343 luaW_toscalar(mState, i, result[index]);
1344 break;
1345 case LUA_TTABLE:
1346 luaW_toconfig(mState, i, result.add_child(index));
1347 break;
1348 }
1349 }
1350 return result;
1351}
1352// Call load_string and protected call. Make them throw exceptions.
1353//
1354void lua_kernel_base::throwing_run(const char * prog, const std::string& name, int nArgs, bool in_interpreter)
1355{
1356 cmd_log_ << "$ " << prog << "\n";
1357 error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1358 this->load_string(prog, name, eh);
1359 if(in_interpreter) {
1360 lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1361 if(lua_setupvalue(mState, -2, 1) == nullptr)
1362 lua_pop(mState, 1);
1363 }
1364 lua_insert(mState, -nArgs - 1);
1365 this->protected_call(nArgs, in_interpreter ? LUA_MULTRET : 0, eh);
1366}
1367
1368// Do a throwing run, but if we catch a lua_error, reformat it with signature for this function and log it.
1369void lua_kernel_base::run(const char * prog, const std::string& name, int nArgs)
1370{
1371 try {
1372 this->throwing_run(prog, name, nArgs);
1373 } catch (const game::lua_error & e) {
1374 cmd_log_ << e.what() << "\n";
1375 lua_kernel_base::log_error(e.what(), "In function lua_kernel::run()");
1376 }
1377}
1378
1379// Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it normally. Throws exceptions.
1380void lua_kernel_base::interactive_run(char const * prog) {
1381 std::string experiment = "return ";
1382 experiment += prog;
1383 int top = lua_gettop(mState);
1384
1385 error_handler eh = std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2 );
1386 luaW_getglobal(mState, "ilua", "_pretty_print");
1387
1388 try {
1389 // Try to load the experiment without syntax errors
1390 this->load_string(experiment.c_str(), "interactive", eh);
1391 lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1392 if(lua_setupvalue(mState, -2, 1) == nullptr)
1393 lua_pop(mState, 1);
1394 } catch (const game::lua_error &) {
1395 this->throwing_run(prog, "interactive", 0, true); // Since it failed, fall back to the usual throwing_run, on the original input.
1396 if(lua_gettop(mState) == top + 1) {
1397 // Didn't return anything
1398 lua_settop(mState, top);
1399 return;
1400 } else goto PRINT;
1401 }
1402 // experiment succeeded, now run but log normally.
1403 cmd_log_ << "$ " << prog << "\n";
1404 this->protected_call(0, LUA_MULTRET, eh);
1405PRINT:
1406 int nRets = lua_gettop(mState) - top - 1;
1407 {
1408 // Assign first result to _
1409 lua_getfield(mState, LUA_REGISTRYINDEX, Interp);
1410 int env_idx = lua_gettop(mState);
1411 lua_pushvalue(mState, top + 2);
1412 lua_setfield(mState, -2, "_");
1413 // Now duplicate EVERY result and pass it to table.pack, assigning to _all
1414 luaW_getglobal(mState, "table", "pack");
1415 for(int i = top + 2; i < env_idx; i++)
1416 lua_pushvalue(mState, i);
1417 this->protected_call(nRets, 1, eh);
1418 lua_setfield(mState, -2, "_all");
1419 lua_pop(mState, 1);
1420 }
1421 // stack is now ilua._pretty_print followed by any results of prog
1422 this->protected_call(lua_gettop(mState) - top - 1, 0, eh);
1423}
1424/**
1425 * Loads and executes a Lua file.
1426 * - Arg 1: string containing the file name.
1427 * - Ret *: values returned by executing the file body.
1428 */
1430{
1431 luaL_checkstring(L, 1);
1432 lua_rotate(L, 1, -1);
1433 if (lua_fileops::load_file(L) != 1) return 0;
1434 //^ should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1435 lua_rotate(L, 1, 1);
1436 // Using a non-protected call here appears to fix an issue in plugins.
1437 // The protected call isn't technically necessary anyway, because this function is called from Lua code,
1438 // which should already be in a protected environment.
1439 lua_call(L, lua_gettop(L) - 1, LUA_MULTRET);
1440 return lua_gettop(L);
1441}
1442
1443/**
1444 * Loads and executes a Lua file, if there is no corresponding entry in wesnoth.package.
1445 * Stores the result of the script in wesnoth.package and returns it.
1446 * - Arg 1: string containing the file name.
1447 * - Ret 1: value returned by the script.
1448 */
1450{
1451 const char * m = luaL_checkstring(L, 1);
1452 if(!m) {
1453 return luaL_argerror(L, 1, "found a null string argument to wesnoth require");
1454 }
1455
1456 // Check if there is already an entry.
1457
1458 lua_getglobal(L, "wesnoth");
1459 lua_pushstring(L, "package");
1460 lua_rawget(L, -2);
1461 lua_pushvalue(L, 1);
1462 lua_rawget(L, -2);
1463 if(!lua_isnil(L, -1) && !game_config::debug_lua) {
1464 return 1;
1465 }
1466 lua_pop(L, 1);
1467 lua_pushvalue(L, 1);
1468 // stack is now [packagename] [wesnoth] [package] [packagename]
1469
1470 if(lua_fileops::load_file(L) != 1) {
1471 // should end with the file contents loaded on the stack. actually it will call lua_error otherwise, the return 0 is redundant.
1472 // stack is now [packagename] [wesnoth] [package] [chunk]
1473 return 0;
1474 }
1475 DBG_LUA << "require: loaded a file, now calling it";
1476
1477 if (!this->protected_call(L, 0, 1, std::bind(&lua_kernel_base::log_error, this, std::placeholders::_1, std::placeholders::_2))) {
1478 // historically if wesnoth.require fails it just yields nil and some logging messages, not a lua error
1479 return 0;
1480 }
1481 // stack is now [packagename] [wesnoth] [package] [results]
1482
1483 lua_pushvalue(L, 1);
1484 lua_pushvalue(L, -2);
1485 // stack is now [packagename] [wesnoth] [package] [results] [packagename] [results]
1486 // Add the return value to the table.
1487
1488 lua_settable(L, -4);
1489 // stack is now [packagename] [wesnoth] [package] [results]
1490 return 1;
1491}
1493{
1494 lua_push(L, my_name());
1495 return 1;
1496}
1497static void push_color_palette(lua_State* L, const std::vector<color_t>& palette) {
1498 static const lua_named_tuple_builder tuple_builder{ {"r", "g", "b", "a"} };
1499
1500 lua_createtable(L, palette.size(), 1);
1501 lua_rotate(L, -2, 1); // swap new table with previous element on stack
1502 lua_setfield(L, -2, "name");
1503 for(std::size_t i = 0; i < palette.size(); i++) {
1504 tuple_builder.push(L);
1505 lua_pushinteger(L, palette[i].r);
1506 lua_rawseti(L, -2, 1);
1507 lua_pushinteger(L, palette[i].g);
1508 lua_rawseti(L, -2, 2);
1509 lua_pushinteger(L, palette[i].b);
1510 lua_rawseti(L, -2, 3);
1511 lua_pushinteger(L, palette[i].a);
1512 lua_rawseti(L, -2, 4);
1513 lua_rawseti(L, -2, i);
1514 }
1515}
1516static int impl_palette_get(lua_State* L)
1517{
1518 char const *m = luaL_checkstring(L, 2);
1519 lua_pushvalue(L, 2);
1521 return 1;
1522}
1523
1524// suppress missing prototype warning (not static because game_lua_kernel referenes it);
1527 static luaW_Registry gameConfigReg{"game config"};
1528 return gameConfigReg;
1529}
1530static auto& dummy = gameConfigReg(); // just to ensure it's constructed.
1531
1532#define GAME_CONFIG_SIMPLE_GETTER(name) \
1533GAME_CONFIG_GETTER(#name, decltype(game_config::name), lua_kernel_base) { \
1534 (void) k; \
1535 return game_config::name; \
1536}
1537
1538namespace {
1539GAME_CONFIG_SIMPLE_GETTER(base_income);
1540GAME_CONFIG_SIMPLE_GETTER(village_income);
1541GAME_CONFIG_SIMPLE_GETTER(village_support);
1542GAME_CONFIG_SIMPLE_GETTER(poison_amount);
1543GAME_CONFIG_SIMPLE_GETTER(rest_heal_amount);
1544GAME_CONFIG_SIMPLE_GETTER(recall_cost);
1545GAME_CONFIG_SIMPLE_GETTER(kill_experience);
1546GAME_CONFIG_SIMPLE_GETTER(combat_experience);
1548GAME_CONFIG_SIMPLE_GETTER(debug_lua);
1549GAME_CONFIG_SIMPLE_GETTER(strict_lua);
1551
1553 (void)k;
1554 lua_newtable(L);
1555 if(luaL_newmetatable(L, "color palettes")) {
1556 lua_pushcfunction(L, impl_palette_get);
1557 lua_setfield(L, -2, "__index");
1558 lua_pushstring(L, "color palettes");
1559 lua_setfield(L, -2, "__metatable");
1560 }
1561 lua_setmetatable(L, -2);
1562 return lua_index_raw(L);
1563}
1564
1566 (void)k;
1567 lua_pushstring(L, "red_green_scale");
1569 return lua_index_raw(L);
1570}
1571
1572GAME_CONFIG_GETTER("red_green_scale_text", lua_index_raw, lua_kernel_base) {
1573 (void)k;
1574 lua_pushstring(L, "red_green_scale_text");
1576 return lua_index_raw(L);
1577}
1578
1579GAME_CONFIG_GETTER("blue_white_scale", lua_index_raw, lua_kernel_base) {
1580 (void)k;
1581 lua_pushstring(L, "blue_white_scale");
1583 return lua_index_raw(L);
1584}
1585
1586GAME_CONFIG_GETTER("blue_white_scale_text", lua_index_raw, lua_kernel_base) {
1587 (void)k;
1588 lua_pushstring(L, "blue_white_scale_text");
1590 return lua_index_raw(L);
1591}
1592}
1593
1594/**
1595 * Gets some game_config data (__index metamethod).
1596 * - Arg 1: userdata (ignored).
1597 * - Arg 2: string containing the name of the property.
1598 * - Ret 1: something containing the attribute.
1599 */
1601{
1602 return gameConfigReg().get(L);
1603}
1604/**
1605 * Sets some game_config data (__newindex metamethod).
1606 * - Arg 1: userdata (ignored).
1607 * - Arg 2: string containing the name of the property.
1608 * - Arg 3: something containing the attribute.
1609 */
1611{
1612 return gameConfigReg().set(L);
1613}
1614/**
1615 * Gets a list of game_config data (__dir metamethod).
1616 */
1618{
1619 return gameConfigReg().dir(L);
1620}
1621/**
1622 * Loads the "package" package into the Lua environment.
1623 * This action is inherently unsafe, as Lua scripts will now be able to
1624 * load C libraries on their own, hence granting them the same privileges
1625 * as the Wesnoth binary itself.
1626 */
1628{
1629 lua_State *L = mState;
1630 lua_pushcfunction(L, luaopen_package);
1631 lua_pushstring(L, "package");
1632 lua_call(L, 1, 0);
1633}
1634
1636{
1637 lua_State* L = mState;
1638 lua_settop(L, 0);
1639 cmd_log_ << "Loading core...\n";
1640 luaW_getglobal(L, "wesnoth", "require");
1641 lua_pushstring(L, "lua/core");
1642 if(!protected_call(1, 1)) {
1643 cmd_log_ << "Error: Failed to load core.\n";
1644 }
1645 lua_settop(L, 0);
1646}
1647
1648/**
1649 * Gets all the global variable names in the Lua environment. This is useful for tab completion.
1650 */
1652{
1653 std::vector<std::string> ret;
1654
1655 lua_State *L = mState;
1656
1657 int idx = lua_gettop(L);
1658 lua_getglobal(L, "_G");
1659 lua_pushnil(L);
1660
1661 while (lua_next(L, idx+1) != 0) {
1662 if (lua_isstring(L, -2)) {
1663 ret.push_back(lua_tostring(L,-2));
1664 }
1665 lua_pop(L,1);
1666 }
1667 lua_settop(L, idx);
1668 return ret;
1669}
1670
1671/**
1672 * Gets all attribute names of an extended variable name. This is useful for tab completion.
1673 */
1674std::vector<std::string> lua_kernel_base::get_attribute_names(const std::string & input)
1675{
1676 std::vector<std::string> ret;
1677 std::string base_path = input;
1678 std::size_t last_dot = base_path.find_last_of('.');
1679 std::string partial_name = base_path.substr(last_dot + 1);
1680 base_path.erase(last_dot);
1681 std::string load = "return " + base_path;
1682
1683 lua_State* L = mState;
1684 int save_stack = lua_gettop(L);
1685 int result = luaL_loadstring(L, load.c_str());
1686 if(result != LUA_OK) {
1687 // 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.
1688 LOG_LUA << "Error when attempting tab completion:";
1689 LOG_LUA << luaL_checkstring(L, -1);
1690 // Just return an empty list; no matches were found
1691 lua_settop(L, save_stack);
1692 return ret;
1693 }
1694
1695 luaW_pcall(L, 0, 1);
1696 if(lua_istable(L, -1) || lua_isuserdata(L, -1)) {
1697 int top = lua_gettop(L);
1698 int obj = lua_absindex(L, -1);
1699 if(luaL_getmetafield(L, obj, "__tab_enum") == LUA_TFUNCTION) {
1700 lua_pushvalue(L, obj);
1701 lua_pushlstring(L, partial_name.c_str(), partial_name.size());
1702 luaW_pcall(L, 2, 1);
1703 ret = lua_check<std::vector<std::string>>(L, -1);
1704 } else if(lua_type(L, -1) != LUA_TTABLE) {
1705 LOG_LUA << "Userdata missing __tab_enum meta-function for tab completion";
1706 lua_settop(L, save_stack);
1707 return ret;
1708 } else {
1709 lua_settop(L, top);
1710 // Metafunction not found, so use lua_next to enumerate the table
1711 for(lua_pushnil(L); lua_next(L, obj); lua_pop(L, 1)) {
1712 if(lua_type(L, -2) == LUA_TSTRING) {
1713 std::string attr = lua_tostring(L, -2);
1714 if(attr.empty()) {
1715 continue;
1716 }
1717 if(!isalpha(attr[0]) && attr[0] != '_') {
1718 continue;
1719 }
1720 if(std::any_of(attr.begin(), attr.end(), [](char c){
1721 return !isalpha(c) && !isdigit(c) && c != '_';
1722 })) {
1723 continue;
1724 }
1725 if(attr.substr(0, partial_name.size()) == partial_name) {
1726 ret.push_back(base_path + "." + attr);
1727 }
1728 }
1729 }
1730 }
1731 }
1732 lua_settop(L, save_stack);
1733 return ret;
1734}
1735
1737{
1738 #ifdef __GNUC__
1739 #pragma GCC diagnostic push
1740 #pragma GCC diagnostic ignored "-Wold-style-cast"
1741 #endif
1742 return *reinterpret_cast<lua_kernel_base**>(lua_getextraspace(L));
1743 #ifdef __GNUC__
1744 #pragma GCC diagnostic pop
1745 #endif
1746}
1747
1749{
1750 return seed_rng::next_seed();
1751}
map_location loc
Definition move.cpp:172
luaW_Registry & gameConfigReg()
#define ERR_LUA
#define LOG_LUA
double t
#define debug(x)
std::vector< std::string > names
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
all_children_iterator erase(const all_children_iterator &i)
Definition config.cpp:618
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
bool empty() const
Definition config.cpp:823
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.
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.
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 &)
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.
Efficiently creates new LUA "named tuples" with the specified field names.
void push(lua_State *L) const
Push an empty "named tuple" onto the stack.
const char * what() const noexcept
std::string generate(const std::map< std::string, std::string > &variables) const
virtual ~name_generator()
virtual std::string type() const
int height() const
Definition team.cpp:756
void read(const std::string &shroud_data)
Definition team.cpp:871
void set_enabled(bool enabled)
Definition team.hpp:60
int width() const
Definition team.cpp:751
bool value(int x, int y) const
Definition team.cpp:814
std::string write() const
Definition team.cpp:855
bool clear(int x, int y)
Definition team.cpp:764
const std::string & str() const
Definition tstring.hpp:204
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").
const std::vector< unsigned int > & components() const
Read-only access to all numeric components.
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
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)
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
const config * cfg
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:317
Standard logging facilities (interface).
#define LOG_STREAM(level, domain)
Definition log.hpp:278
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
void push_error_handler(lua_State *L)
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.
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.
std::vector< std::string > luaW_to_namedtuple(lua_State *L, int idx)
Get the keys of a "named tuple" from the stack.
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.
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.
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.
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.
#define return_string_attrib(name, accessor)
#define return_int_attrib(name, accessor)
#define return_bool_attrib(name, accessor)
#define DBG_LUA
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")
static int intf_current_version(lua_State *L)
Returns the current Wesnoth version.
static lg::log_domain log_scripting_lua("scripting/lua")
static int intf_parse_shroud_bitmap(lua_State *L)
static int intf_make_version(lua_State *L)
Builds a version from its component parts, or parses it from a string.
static int impl_version_dir(lua_State *L)
static int impl_version_get(lua_State *L)
Decomposes a version into its component parts.
static int impl_get_dir_suffix(lua_State *L)
static int impl_version_tostring(lua_State *L)
Convert a version to string form.
static int intf_deprecated_message(lua_State *L)
Logs a deprecation message.
static int intf_pcall(lua_State *L)
Wrapper for pcall and xpcall functions to rethrow jailbreak exceptions.
static int intf_named_tuple(lua_State *L)
Converts a Lua array to a named tuple.
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 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
static void push_color_palette(lua_State *L, const std::vector< color_t > &palette)
static int intf_std_print(lua_State *L)
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 int impl_name_generator_tostring(lua_State *L)
luaW_Registry & gameConfigReg()
static void dir_meta_helper(lua_State *L, std::vector< std::string > &keys)
static int intf_object_dir(lua_State *L)
Prints out a list of keys available in an object.
static int intf_safe_tostring(lua_State *L)
#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.
config luaW_serialize_function(lua_State *L, int func)
#define GAME_CONFIG_GETTER(name, type, kernel_type)
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
std::vector< color_t > red_green_scale
std::vector< color_t > blue_white_scale_text
void remove_single_widget_definition(const std::string &widget_type, const std::string &definition_id)
Removes a widget definition from the default GUI.
std::string register_metatables(lua_State *L)
int intf_textdomain(lua_State *L)
Creates an interface for gettext.
std::string register_gettext_metatable(lua_State *L)
Adds the gettext metatable.
std::string register_tstring_metatable(lua_State *L)
Adds the tstring metatable.
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:398
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:97
int luaW_open(lua_State *L)
void register_metatable(lua_State *L)
int luaW_open(lua_State *L)
Definition lua_wml.cpp:240
uint32_t next_seed()
Definition seed_rng.cpp:32
constexpr auto keys
Definition ranges.hpp:43
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)
std::string_view data
Definition picture.cpp:188
void lua_push(lua_State *L, const T &val)
#define ON_SCOPE_EXIT(...)
Run some arbitrary code (a lambda) when the current scope exits The lambda body follows this header,...
Error used to report an error in a lua script or in the lua interpreter.
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:187
int wml_x() const
Definition location.hpp:186
mock_party p
static map_location::direction n
#define i
#define g
#define e
#define c
#define b