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