The Battle for Wesnoth  1.19.0-dev
mapgen_lua_kernel.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 "config.hpp"
19 #include "game_errors.hpp"
20 #include "log.hpp"
21 #include "scripting/lua_common.hpp"
25 #include "deprecation.hpp"
26 #include "game_version.hpp"
27 
28 #include <string>
29 #include <functional>
30 
31 #include "scripting/push_check.hpp"
33 
34 static lg::log_domain log_mapgen("mapgen");
35 #define ERR_NG LOG_STREAM(err, log_mapgen)
36 #define LOG_NG LOG_STREAM(info, log_mapgen)
37 #define DBG_NG LOG_STREAM(debug, log_mapgen)
38 
39 
40 
41 // Template which allows to push member functions to the lua kernel into lua as C functions, using a shim
42 typedef int (mapgen_lua_kernel::*member_callback)(lua_State *);
43 
44 template <member_callback method>
45 int dispatch(lua_State *L) {
46  return ((lua_kernel_base::get_lua_kernel<mapgen_lua_kernel>(L)).*method)(L);
47 }
48 
49 /**
50  * Returns a random number, same interface as math.random.
51  */
52 static int intf_random(lua_State *L)
53 {
54  std::mt19937& rng = lua_kernel_base::get_lua_kernel<mapgen_lua_kernel>(L).get_default_rng();
55  if(lua_isnoneornil(L, 1)) {
56  double r = double (rng());
57  double r_max = double (rng.max());
58  lua_push(L, r / (r_max + 1));
59  return 1;
60  }
61  else {
62  int32_t min;
63  int32_t max;
64  if(lua_isnumber(L, 2)) {
65  min = lua_check<int32_t>(L, 1);
66  max = lua_check<int32_t>(L, 2);
67  }
68  else {
69  min = 1;
70  max = lua_check<int32_t>(L, 1);
71  }
72  if(min > max) {
73  return luaL_argerror(L, 1, "min > max");
74  }
75  lua_push(L, min + static_cast<int>(rng() % (max - min + 1)));
76  return 1;
77  }
78 }
79 
80 /**
81  * calls the default mapgenerator.
82  */
83 static int intf_default_generate(lua_State *L)
84 {
85  std::mt19937& rng = lua_kernel_base::get_lua_kernel<mapgen_lua_kernel>(L).get_default_rng();
86 
87  int width = luaL_checkinteger(L, 1);
88  int height = luaL_checkinteger(L, 2);
89 
90  config cfg = luaW_checkconfig(L, 3);
91 
92  generator_data arg;
93  arg.width = width;
94  arg.height = height;
95  arg.nplayers = cfg["nplayers"].to_int(2);
96  arg.nvillages = cfg["nvillages"].to_int(0);
97  arg.iterations = cfg["iterations"].to_int(0);
98  arg.hill_size = cfg["hill_size"].to_int(0);
99  arg.castle_size = cfg["castle_size"].to_int(0);
100  arg.island_size = cfg["island_size"].to_int(0);
101  arg.island_off_center = cfg["island_off_center"].to_int(0);
102  arg.max_lakes = cfg["max_lakes"].to_int(0);
103  arg.link_castles = cfg["link_castles"].to_bool();
104  arg.show_labels = cfg["show_labels"].to_bool(0);
105 
106  uint32_t seed = cfg["seed"].to_int(0);
107  if(!cfg.has_attribute("seed")) {
108  seed = rng();
109  }
110 
111  default_map_generator_job job(seed);
112  std::string res = job.default_generate_map(arg, nullptr, cfg);
113 
114  lua_push(L, res);
115  return 1;
116 }
117 
118 /**
119  * calls the default mapgenerator.
120  */
121 static int intf_default_generate_height_map(lua_State *L)
122 {
123  std::mt19937& rng = lua_kernel_base::get_lua_kernel<mapgen_lua_kernel>(L).get_default_rng();
124 
125  int width = luaL_checkinteger(L, 1);
126  int height = luaL_checkinteger(L, 2);
127 
128  config cfg = luaW_checkconfig(L, 3);
129 
130  if(!cfg.has_attribute("location_set")) {
131  deprecated_message("generate_height_map(..., {location_set=false})", DEP_LEVEL::PREEMPTIVE, "1.17", "The default value of this option will be changed to true in 1.17.");
132  }
133 
134  int iterations = cfg["iterations"].to_int(1);
135  int hill_size = cfg["hill_size"].to_int(1);
136  int island_size = cfg["island_size"].to_int(width/2);
137  int center_x = cfg["center_x"].to_int(width/2);
138  int center_y = cfg["center_y"].to_int(height/2);
139  bool flip_layout = cfg["flip_format"].to_bool();
140  bool as_locset = cfg["location_set"].to_bool(false);
141  uint32_t seed = cfg["seed"].to_int(0);
142 
143  if(!cfg.has_attribute("seed")) {
144  seed = rng();
145  }
146  default_map_generator_job job(seed);
147  default_map_generator_job::height_map res = job.generate_height_map(width, height, iterations, hill_size, island_size, center_x, center_y);
148  lua_createtable (L, width * height, 0);
149  assert(int(res.size()) == width);
150  assert((width == 0 || int(res[0].size()) == height));
151  std::hash<map_location> loc_hash;
152  for(int x = 0; x != width; ++x) {
153  for(int y = 0; y != height; ++y) {
154  int h = res[x][y];
155  lua_pushinteger (L, h);
156  if(as_locset) {
157  map_location loc(flip_layout ? y : x, flip_layout ? x : y, wml_loc());
158  lua_rawseti(L, -2, loc_hash(loc));
159  } else {
160  int i = flip_layout ? (y + x * height) : (x + y * width);
161  lua_rawseti(L, -2, i);
162  }
163  }
164  }
165  return 1;
166 }
167 /**
168  * Finds a path between two locations.
169  * - Args 1: source location.
170  * - Args 2: destination.
171  * - Arg 3: cost function
172  * - Args 4,5 size of map.
173  * - Arg 6 include border.
174  * OR
175  * - Arg 3: options table containing calculate, width, height, (optional) include_borders
176  * - Ret 1: array of pairs containing path steps.
177  * - Ret 2: path cost.
178  */
179 static int intf_find_path(lua_State *L)
180 {
181  int arg = 1;
182  map_location src = luaW_checklocation(L, 1), dst = luaW_checklocation(L, 2);
183  if(lua_isfunction(L, arg)) {
184  const char *msg = lua_pushfstring(L, "%s expected, got %s", lua_typename(L, LUA_TFUNCTION), luaL_typename(L, 3));
185  return luaL_argerror(L, 3, msg);
186  }
187  std::optional<lua_pathfind_cost_calculator> calc;
188  int width, height;
189  bool border = false;
190  if(lua_istable(L, 3)) {
191  if(luaW_tableget(L, 3, "calculate")) {
192  calc = lua_pathfind_cost_calculator(L, lua_gettop(L));
193  } else {
194  return luaL_argerror(L, 3, "missing key: calculate");
195  }
196  if(!luaW_tableget(L, 3, "width")) {
197  width = luaL_checkinteger(L, -1);
198  } else {
199  return luaL_argerror(L, 3, "missing key: width");
200  }
201  if(!luaW_tableget(L, 3, "height")) {
202  height = luaL_checkinteger(L, -1);
203  } else {
204  return luaL_argerror(L, 3, "missing key: height");
205  }
206  if(!luaW_tableget(L, 3, "include_borders")) {
207  border = luaW_toboolean(L, -1);
208  }
209  } else {
210  calc = lua_pathfind_cost_calculator(L, 3);
211  width = luaL_checkinteger(L, 4);
212  height = luaL_checkinteger(L, 5);
213  if(lua_isboolean(L, 6)) {
214  border = luaW_toboolean(L, 6);
215  }
216  }
217  pathfind::plain_route res = pathfind::a_star_search(src, dst, 10000, *calc, width, height, nullptr, border);
218 
219  int nb = res.steps.size();
220  lua_createtable(L, nb, 0);
221  for (int i = 0; i < nb; ++i)
222  {
223  lua_createtable(L, 2, 0);
224  lua_pushinteger(L, res.steps[i].wml_x());
225  lua_rawseti(L, -2, 1);
226  lua_pushinteger(L, res.steps[i].wml_y());
227  lua_rawseti(L, -2, 2);
228  lua_rawseti(L, -2, i + 1);
229  }
230  lua_pushinteger(L, res.move_cost);
231 
232  return 2;
233 }
234 
235 
237  : lua_kernel_base()
238  , random_seed_()
239  , default_rng_()
240  , vars_(vars)
241 {
242  lua_State *L = mState;
243 
244  // Overwrite mathx.random. This guarantees that the mapgen_lua_kernel version
245  // of mathx.random overrides the lua_kernel_base version.
246  lua_getglobal(L, "mathx");
247  lua_pushcfunction(L, &intf_random);
248  lua_setfield(L, -2, "random");
249 
250  lua_settop(L, 0);
251 
252  static luaL_Reg const map_callbacks[] {
253  // Map methods
254  { "find", &intf_mg_get_locations },
255  { "find_in_radius", &intf_mg_get_tiles_radius },
256  // Static functions
257  { "filter", &intf_terrainfilter_create },
258  { "create", &intf_terrainmap_create },
259  { "generate_height_map", &intf_default_generate_height_map },
260  { "generate", &intf_default_generate },
261  { nullptr, nullptr }
262  };
263 
264  luaW_getglobal(L, "wesnoth", "map");
265  assert(lua_istable(L,-1));
266  luaL_setfuncs(L, map_callbacks, 0);
267  lua_pop(L, 1);
268  assert(lua_gettop(L) == 0);
269 
270  // Create the paths module
271  cmd_log_ << "Adding paths module...\n";
272  static luaL_Reg const path_callbacks[] {
273  { "find_path", &intf_find_path },
274  { nullptr, nullptr }
275  };
276  lua_getglobal(L, "wesnoth");
277  lua_newtable(L);
278  luaL_setfuncs(L, path_callbacks, 0);
279  lua_setfield(L, -2, "paths");
280  lua_pop(L, 1);
281 
282  // Add functions to the WML module
283  lua_getglobal(L, "wml");
284  static luaL_Reg const wml_callbacks[] {
285  {"tovconfig", &lua_common::intf_tovconfig},
286  // These aren't actually part of the API - they're used internally by the variable metatable.
287  { "get_variable", &dispatch<&mapgen_lua_kernel::intf_get_variable>},
288  { "get_all_vars", &dispatch<&mapgen_lua_kernel::intf_get_all_vars>},
289  { nullptr, nullptr }
290  };
291  luaL_setfuncs(L, wml_callbacks, 0);
292  lua_pop(L, 1);
293 
296 }
297 
298 void mapgen_lua_kernel::run_generator(const char * prog, const config & generator)
299 {
300  load_string(prog, "", std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2));
302  protected_call(1, 1, std::bind(&lua_kernel_base::throw_exception, this, std::placeholders::_1, std::placeholders::_2));
303 }
304 
305 void mapgen_lua_kernel::user_config(const char * prog, const config & generator)
306 {
307  run_generator(prog, generator);
308 }
309 
311 {
312  char const *m = luaL_checkstring(L, 1);
313  if(vars_) {
315  return luaW_pushvariable(L, v) ? 1 : 0;
316  }
317  return 0;
318 }
319 
321  luaW_pushconfig(L, vars_ ? *vars_ : config());
322  return 1;
323 }
324 
325 std::string mapgen_lua_kernel::create_map(const char * prog, const config & generator, std::optional<uint32_t> seed) // throws game::lua_error
326 {
327  random_seed_ = seed;
328  default_rng_ = std::mt19937(get_random_seed());
329  run_generator(prog, generator);
330 
331  if (!lua_isstring(mState,-1)) {
332  std::string msg = "expected a string, found a ";
333  msg += lua_typename(mState, lua_type(mState, -1));
334  lua_pop(mState, 1);
335  throw game::lua_error(msg.c_str(),"bad return value");
336  }
337 
338  return lua_tostring(mState, -1);
339 }
340 
341 config mapgen_lua_kernel::create_scenario(const char * prog, const config & generator, std::optional<uint32_t> seed) // throws game::lua_error
342 {
343  random_seed_ = seed;
344  default_rng_ = std::mt19937(get_random_seed());
345  run_generator(prog, generator);
346 
347  if (!lua_istable(mState, -1)) {
348  std::string msg = "expected a config (table), found a ";
349  msg += lua_typename(mState, lua_type(mState, -1));
350  lua_pop(mState, 1);
351  throw game::lua_error(msg.c_str(),"bad return value");
352  }
353  config result;
354  if (!luaW_toconfig(mState, -1, result)) {
355  std::string msg = "expected a config, but it is malformed ";
356  lua_pop(mState, 1);
357  throw game::lua_error(msg.c_str(),"bad return value");
358  }
359  return result;
360 }
361 
363 {
364  if(random_seed_) {
365  return (*random_seed_)++;
366  }
367  else {
369  }
370 }
371 
373 {
374  if(!default_rng_) {
375  default_rng_ = std::mt19937(get_random_seed());
376  }
377  return *default_rng_;
378 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
height_map generate_height_map(size_t width, size_t height, size_t iterations, size_t hill_size, size_t island_size, size_t island_off_center)
Generate a height-map.
std::string default_generate_map(generator_data data, std::map< map_location, std::string > *labels, const config &cfg)
Generate the map.
std::vector< std::vector< int > > height_map
command_log cmd_log_
lua_State * mState
bool protected_call(int nArgs, int nRets, error_handler)
bool load_string(char const *prog, const std::string &name, error_handler)
virtual uint32_t get_random_seed()
virtual void throw_exception(char const *msg, char const *context="Lua error")
int intf_get_variable(lua_State *L)
virtual uint32_t get_random_seed()
void run_generator(const char *prog, const config &generator)
void user_config(const char *prog, const config &generator)
std::optional< std::mt19937 > default_rng_
mapgen_lua_kernel(const config *vars)
std::mt19937 & get_default_rng()
std::optional< uint32_t > random_seed_
std::string create_map(const char *prog, const config &generator, std::optional< uint32_t > seed)
int intf_get_all_vars(lua_State *L)
const config * vars_
config create_scenario(const char *prog, const config &generator, std::optional< uint32_t > seed)
Information on a WML variable.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
std::size_t i
Definition: function.cpp:968
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
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:827
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:917
bool luaW_pushvariable(lua_State *L, variable_access_const &v)
Definition: lua_common.cpp:993
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:988
bool luaW_tableget(lua_State *L, int index, const char *key)
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:839
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
Definition: lua_common.cpp:790
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
Definition: lua_common.cpp:969
int intf_mg_get_tiles_radius(lua_State *L)
int intf_terrainfilter_create(lua_State *L)
Create a filter.
int intf_mg_get_locations(lua_State *L)
int intf_terrainmap_create(lua_State *L)
Create a map.
int dispatch(lua_State *L)
static int intf_default_generate_height_map(lua_State *L)
calls the default mapgenerator.
static lg::log_domain log_mapgen("mapgen")
static int intf_find_path(lua_State *L)
Finds a path between two locations.
static int intf_random(lua_State *L)
Returns a random number, same interface as math.random.
int(mapgen_lua_kernel::* member_callback)(lua_State *)
static int intf_default_generate(lua_State *L)
calls the default mapgenerator.
int intf_tovconfig(lua_State *L)
Creates a vconfig containing the WML table.
Definition: lua_common.cpp:411
std::string register_metatables(lua_State *L)
std::string register_metatables(lua_State *L)
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:373
Error used to report an error in a lua script or in the lua interpreter.
Definition: game_errors.hpp:54
Cost function object relying on a Lua function.
Encapsulates the map of the game.
Definition: location.hpp:38
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
std::vector< map_location > steps
Definition: pathfind.hpp:135
int move_cost
Movement cost for reaching the end of the route.
Definition: pathfind.hpp:137
#define h