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