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