Lua on each mapgen thread (#13092)

This commit is contained in:
sfan5 2024-02-13 22:47:30 +01:00 committed by GitHub
parent d4b107e2e8
commit 3cac17d23e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1329 additions and 193 deletions

@ -17,6 +17,7 @@ read_globals = {
"VoxelArea",
"profiler",
"Settings",
"PerlinNoise", "PerlinNoiseMap",
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn", "indexof", "insert_all"}},

61
builtin/emerge/env.lua Normal file

@ -0,0 +1,61 @@
-- Reimplementations of some environment function on vmanips, since this is
-- what the emerge environment operates on
-- core.vmanip = <VoxelManip> -- set by C++
function core.set_node(pos, node)
return core.vmanip:set_node_at(pos, node)
end
function core.bulk_set_node(pos_list, node)
local vm = core.vmanip
local set_node_at = vm.set_node_at
for _, pos in ipairs(pos_list) do
if not set_node_at(vm, pos, node) then
return false
end
end
return true
end
core.add_node = core.set_node
-- we don't deal with metadata currently
core.swap_node = core.set_node
function core.remove_node(pos)
return core.vmanip:set_node_at(pos, {name="air"})
end
function core.get_node(pos)
return core.vmanip:get_node_at(pos)
end
function core.get_node_or_nil(pos)
local node = core.vmanip:get_node_at(pos)
return node.name ~= "ignore" and node
end
function core.get_perlin(seed, octaves, persist, spread)
local params
if type(seed) == "table" then
params = table.copy(seed)
else
assert(type(seed) == "number")
params = {
seed = seed,
octaves = octaves,
persist = persist,
spread = {x=spread, y=spread, z=spread},
}
end
params.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoise(params)
end
function core.get_perlin_map(params, size)
local params2 = table.copy(params)
params2.seed = core.get_seed(params.seed) -- add mapgen seed
return PerlinNoiseMap(params2, size)
end

21
builtin/emerge/init.lua Normal file

@ -0,0 +1,21 @@
local gamepath = core.get_builtin_path() .. "game" .. DIR_DELIM
local commonpath = core.get_builtin_path() .. "common" .. DIR_DELIM
local epath = core.get_builtin_path() .. "emerge" .. DIR_DELIM
local builtin_shared = {}
-- Import parts shared with "game" environment
dofile(gamepath .. "constants.lua")
assert(loadfile(commonpath .. "item_s.lua"))(builtin_shared)
dofile(gamepath .. "misc_s.lua")
dofile(gamepath .. "features.lua")
dofile(gamepath .. "voxelarea.lua")
-- Now for our own stuff
assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(epath .. "register.lua"))(builtin_shared)
dofile(epath .. "env.lua")
builtin_shared.cache_content_ids()
core.log("info", "Initialized emerge Lua environment")

@ -0,0 +1,54 @@
local builtin_shared = ...
-- Copy all the registration tables over
do
local all = assert(core.transferred_globals)
core.transferred_globals = nil
all.registered_nodes = {}
all.registered_craftitems = {}
all.registered_tools = {}
for k, v in pairs(all.registered_items) do
-- Disable further modification
setmetatable(v, {__newindex = {}})
-- Reassemble the other tables
if v.type == "node" then
getmetatable(v).__index = all.nodedef_default
all.registered_nodes[k] = v
elseif v.type == "craft" then
getmetatable(v).__index = all.craftitemdef_default
all.registered_craftitems[k] = v
elseif v.type == "tool" then
getmetatable(v).__index = all.tooldef_default
all.registered_tools[k] = v
else
getmetatable(v).__index = all.noneitemdef_default
end
end
for k, v in pairs(all) do
core[k] = v
end
end
-- For tables that are indexed by item name:
-- If table[X] does not exist, default to table[core.registered_aliases[X]]
local alias_metatable = {
__index = function(t, name)
return rawget(t, core.registered_aliases[name])
end
}
setmetatable(core.registered_items, alias_metatable)
setmetatable(core.registered_nodes, alias_metatable)
setmetatable(core.registered_craftitems, alias_metatable)
setmetatable(core.registered_tools, alias_metatable)
--
-- Callbacks
--
local make_registration = builtin_shared.make_registration
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
core.registered_on_generateds, core.register_on_generated = make_registration()
core.registered_on_shutdown, core.register_on_shutdown = make_registration()

@ -237,8 +237,8 @@ end
core.dynamic_media_callbacks = {}
-- Transfer of certain globals into async environment
-- see builtin/async/game.lua for the other side
-- Transfer of certain globals into seconday Lua environments
-- see builtin/async/game.lua or builtin/emerge/register.lua for the unpacking
local function copy_filtering(t, seen)
if type(t) == "userdata" or type(t) == "function" then
@ -261,6 +261,9 @@ function core.get_globals_to_transfer()
local all = {
registered_items = copy_filtering(core.registered_items),
registered_aliases = core.registered_aliases,
registered_biomes = core.registered_biomes,
registered_ores = core.registered_ores,
registered_decorations = core.registered_decorations,
nodedef_default = copy_filtering(core.nodedef_default),
craftitemdef_default = copy_filtering(core.craftitemdef_default),

@ -31,8 +31,6 @@ minetest = core
-- Load other files
local scriptdir = core.get_builtin_path()
local gamepath = scriptdir .. "game" .. DIR_DELIM
local clientpath = scriptdir .. "client" .. DIR_DELIM
local commonpath = scriptdir .. "common" .. DIR_DELIM
local asyncpath = scriptdir .. "async" .. DIR_DELIM
@ -42,7 +40,7 @@ dofile(commonpath .. "serialize.lua")
dofile(commonpath .. "misc_helpers.lua")
if INIT == "game" then
dofile(gamepath .. "init.lua")
dofile(scriptdir .. "game" .. DIR_DELIM .. "init.lua")
assert(not core.get_http_api)
elseif INIT == "mainmenu" then
local mm_script = core.settings:get("main_menu_script")
@ -67,7 +65,9 @@ elseif INIT == "async" then
elseif INIT == "async_game" then
dofile(asyncpath .. "game.lua")
elseif INIT == "client" then
dofile(clientpath .. "init.lua")
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
elseif INIT == "emerge" then
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
end

@ -4679,6 +4679,7 @@ differences:
into it; it's not necessary to call `VoxelManip:read_from_map()`.
Note that the region of map it has loaded is NOT THE SAME as the `minp`, `maxp`
parameters of `on_generated()`. Refer to `minetest.get_mapgen_object` docs.
Once you're done you still need to call `VoxelManip:write_to_map()`
* The `on_generated()` callbacks of some mods may place individual nodes in the
generated area using non-VoxelManip map modification methods. Because the
@ -4875,10 +4876,10 @@ Mapgen objects
==============
A mapgen object is a construct used in map generation. Mapgen objects can be
used by an `on_generate` callback to speed up operations by avoiding
used by an `on_generated` callback to speed up operations by avoiding
unnecessary recalculations, these can be retrieved using the
`minetest.get_mapgen_object()` function. If the requested Mapgen object is
unavailable, or `get_mapgen_object()` was called outside of an `on_generate()`
unavailable, or `get_mapgen_object()` was called outside of an `on_generated`
callback, `nil` is returned.
The following Mapgen objects are currently available:
@ -4910,12 +4911,14 @@ generated chunk by the current mapgen.
### `gennotify`
Returns a table mapping requested generation notification types to arrays of
positions at which the corresponding generated structures are located within
the current chunk. To enable the capture of positions of interest to be recorded
call `minetest.set_gen_notify()` first.
Returns a table. You need to announce your interest in a specific
field by calling `minetest.set_gen_notify()` *before* map generation happens.
Possible fields of the returned table are:
* key = string: generation notification type
* value = list of positions (usually)
* Exceptions are denoted in the listing below.
Available generation notification types:
* `dungeon`: bottom center position of dungeon rooms
* `temple`: as above but for desert temples (mgv6 only)
@ -4923,7 +4926,12 @@ Possible fields of the returned table are:
* `cave_end`
* `large_cave_begin`
* `large_cave_end`
* `decoration#id` (see below)
* `custom`: data originating from [Mapgen environment] (Lua API)
* This is a table.
* key = user-defined ID (string)
* value = arbitrary Lua value
* `decoration#id`: decorations
* (see below)
Decorations have a key in the format of `"decoration#id"`, where `id` is the
numeric unique decoration ID as returned by `minetest.get_decoration_id()`.
@ -5587,8 +5595,10 @@ Call these functions only at load time!
* `minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing))`
* Called when a node is punched
* `minetest.register_on_generated(function(minp, maxp, blockseed))`
* Called after generating a piece of world. Modifying nodes inside the area
is a bit faster than usual.
* Called after generating a piece of world between `minp` and `maxp`.
* **Avoid using this** whenever possible. As with other callbacks this blocks
the main thread and introduces noticable latency.
Consider [Mapgen environment] for an alternative.
* `minetest.register_on_newplayer(function(ObjectRef))`
* Called when a new player enters the world for the first time
* `minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage))`
@ -6004,20 +6014,18 @@ Environment access
* `minetest.get_voxel_manip([pos1, pos2])`
* Return voxel manipulator object.
* Loads the manipulator from the map if positions are passed.
* `minetest.set_gen_notify(flags, {deco_ids})`
* `minetest.set_gen_notify(flags, [deco_ids], [custom_ids])`
* Set the types of on-generate notifications that should be collected.
* `flags` is a flag field with the available flags:
* dungeon
* temple
* cave_begin
* cave_end
* large_cave_begin
* large_cave_end
* decoration
* The second parameter is a list of IDs of decorations which notification
* `flags`: flag field, see [`gennotify`] for available generation notification types.
* The following parameters are optional:
* `deco_ids` is a list of IDs of decorations which notification
is requested for.
* `custom_ids` is a list of user-defined IDs (strings) which are
requested. By convention these should be the mod name with an optional
colon and specifier added, e.g. `"default"` or `"default:dungeon_loot"`
* `minetest.get_gen_notify()`
* Returns a flagstring and a table with the `deco_id`s.
* Returns a flagstring, a table with the `deco_id`s and a table with
user-defined IDs.
* `minetest.get_decoration_id(decoration_name)`
* Returns the decoration ID number for the provided decoration name string,
or `nil` on failure.
@ -6573,6 +6581,86 @@ Variables:
* with all functions and userdata values replaced by `true`, calling any
callbacks here is obviously not possible
Mapgen environment
------------------
The engine runs the map generator on separate threads, each of these also has
a Lua environment. Its primary purpose is to allow mods to operate on newly
generated parts of the map to e.g. generate custom structures.
Internally it is referred to as "emerge environment".
Refer to [Async environment] for the usual disclaimer on what environment isolation entails.
The map generator threads, which also contain the above mentioned Lua environment,
are initialized after all mods have been loaded by the server. After that the
registered scripts (not all mods!) - see below - are run during initialization of
the mapgen environment. After that only callbacks happen. The mapgen env
does not have a global step or timer.
* `minetest.register_mapgen_script(path)`:
* Register a path to a Lua file to be imported when a mapgen environment
is initialized. Run in order of registration.
### List of APIs exclusive to the mapgen env
* `minetest.register_on_generated(function(vmanip, minp, maxp, blockseed))`
* Called after the engine mapgen finishes a chunk but before it is written to
the map.
* Chunk data resides in `vmanip`. Other parts of the map are not accessible.
The area of the chunk if comprised of `minp` and `maxp`, note that is smaller
than the emerged area of the VoxelManip.
Note: calling `read_from_map()` or `write_to_map()` on the VoxelManipulator object
is not necessary and is disallowed.
* `blockseed`: 64-bit seed number used for this chunk
* `minetest.save_gen_notify(id, data)`
* Saves data for retrieval using the gennotify mechanism (see [Mapgen objects]).
* Data is bound to the chunk that is currently being processed, so this function
only makes sense inside the `on_generated` callback.
* `id`: user-defined ID (a string)
By convention these should be the mod name with an optional
colon and specifier added, e.g. `"default"` or `"default:dungeon_loot"`
* `data`: any Lua object (will be serialized, no userdata allowed)
* returns `true` if the data was remembered. That is if `minetest.set_gen_notify`
was called with the same user-defined ID before.
### List of APIs available in the mapgen env
Classes:
* `AreaStore`
* `ItemStack`
* `PerlinNoise`
* `PerlinNoiseMap`
* `PseudoRandom`
* `PcgRandom`
* `SecureRandom`
* `VoxelArea`
* `VoxelManip`
* only given by callbacks; cannot access rest of map
* `Settings`
Functions:
* Standalone helpers such as logging, filesystem, encoding,
hashing or compression APIs
* `minetest.request_insecure_environment` (same restrictions apply)
* `minetest.get_biome_id`, `get_biome_name`, `get_heat`, `get_humidity`,
`get_biome_data`, `get_mapgen_object`, `get_mapgen_params`, `get_mapgen_edges`,
`get_mapgen_setting`, `get_noiseparams`, `get_decoration_id` and more
* `minetest.get_node`, `set_node`, `find_node_near`, `find_nodes_in_area`,
`spawn_tree` and similar
* these only operate on the current chunk (if inside a callback)
Variables:
* `minetest.settings`
* `minetest.registered_items`, `registered_nodes`, `registered_tools`,
`registered_craftitems` and `registered_aliases`
* with all functions and userdata values replaced by `true`, calling any
callbacks here is obviously not possible
* `minetest.registered_biomes`, `registered_ores`, `registered_decorations`
Note that node metadata does not exist in the mapgen env, we suggest deferring
setting any metadata you need to the `on_generated` callback in the regular env.
You can use the gennotify mechanism to transfer this information.
Server
------
@ -7081,10 +7169,6 @@ Global tables
* Map of registered decoration definitions, indexed by the `name` field.
* If `name` is nil, the key is the object handle returned by
`minetest.register_decoration`.
* `minetest.registered_schematics`
* Map of registered schematic definitions, indexed by the `name` field.
* If `name` is nil, the key is the object handle returned by
`minetest.register_schematic`.
* `minetest.registered_chatcommands`
* Map of registered chat command definitions, indexed by name
* `minetest.registered_privileges`

@ -0,0 +1,32 @@
core.log("info", "Hello World")
local function do_tests()
assert(core == minetest)
-- stuff that should not be here
assert(not core.get_player_by_name)
assert(not core.object_refs)
-- stuff that should be here
assert(core.register_on_generated)
assert(core.get_node)
assert(core.spawn_tree)
assert(ItemStack)
local meta = ItemStack():get_meta()
assert(type(meta) == "userdata")
assert(type(meta.set_tool_capabilities) == "function")
assert(core.registered_items[""])
assert(core.save_gen_notify)
-- alias handling
assert(core.registered_items["unittests:steel_ingot_alias"].name ==
"unittests:steel_ingot")
-- fallback to item defaults
assert(core.registered_items["unittests:description_test"].on_place == true)
end
-- there's no (usable) communcation path between mapgen and the regular env
-- so we just run the test unconditionally
do_tests()
core.register_on_generated(function(vm, pos1, pos2, blockseed)
local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1
assert(pos2:subtract(pos1) == vector.new(n, n, n))
end)

@ -1,3 +1,6 @@
core.register_mapgen_script(core.get_modpath(core.get_current_modname()) ..
DIR_DELIM .. "inside_mapgen_env.lua")
local function test_pseudo_random()
-- We have comprehensive unit tests in C++, this is just to make sure the API code isn't messing up
local gen1 = PseudoRandom(13)
@ -204,3 +207,30 @@ local function test_on_mapblocks_changed(cb, player, pos)
end
end
unittests.register("test_on_mapblocks_changed", test_on_mapblocks_changed, {map=true, async=true})
local function test_gennotify_api()
local DECO_ID = 123
local UD_ID = "unittests:dummy"
-- the engine doesn't check if the id is actually valid, maybe it should
core.set_gen_notify({decoration=true}, {DECO_ID})
core.set_gen_notify({custom=true}, nil, {UD_ID})
local flags, deco, custom = core.get_gen_notify()
local function ff(flag)
return (" " .. flags .. " "):match("[ ,]" .. flag .. "[ ,]") ~= nil
end
assert(ff("decoration"), "'decoration' flag missing")
assert(ff("custom"), "'custom' flag missing")
assert(table.indexof(deco, DECO_ID) > 0)
assert(table.indexof(custom, UD_ID) > 0)
core.set_gen_notify({decoration=false, custom=false})
flags, deco, custom = core.get_gen_notify()
assert(not ff("decoration") and not ff("custom"))
assert(#deco == 0, "deco ids not empty")
assert(#custom == 0, "custom ids not empty")
end
unittests.register("test_gennotify_api", test_gennotify_api)

@ -19,19 +19,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "emerge.h"
#include "emerge_internal.h"
#include <iostream>
#include <queue>
#include "util/container.h"
#include "util/thread.h"
#include "threading/event.h"
#include "config.h"
#include "constants.h"
#include "environment.h"
#include "irrlicht_changes/printing.h"
#include "filesys.h"
#include "log.h"
#include "map.h"
#include "mapblock.h"
@ -42,76 +39,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "profiler.h"
#include "scripting_server.h"
#include "scripting_emerge.h"
#include "server.h"
#include "settings.h"
#include "voxel.h"
class EmergeThread : public Thread {
public:
bool enable_mapgen_debug_info;
int id;
EmergeThread(Server *server, int ethreadid);
~EmergeThread() = default;
void *run();
void signal();
// Requires queue mutex held
bool pushBlock(const v3s16 &pos);
void cancelPendingItems();
protected:
void runCompletionCallbacks(
const v3s16 &pos, EmergeAction action,
const EmergeCallbackList &callbacks);
private:
Server *m_server;
ServerMap *m_map;
EmergeManager *m_emerge;
Mapgen *m_mapgen;
Event m_queue_event;
std::queue<v3s16> m_block_queue;
bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata);
EmergeAction getBlockOrStartGen(
const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata,
std::map<v3s16, MapBlock *> *modified_blocks);
friend class EmergeManager;
};
class MapEditEventAreaIgnorer
{
public:
MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
m_ignorevariable(ignorevariable)
{
if(m_ignorevariable->getVolume() == 0)
*m_ignorevariable = a;
else
m_ignorevariable = NULL;
}
~MapEditEventAreaIgnorer()
{
if(m_ignorevariable)
{
assert(m_ignorevariable->getVolume() != 0);
*m_ignorevariable = VoxelArea();
}
}
private:
VoxelArea *m_ignorevariable;
};
EmergeParams::~EmergeParams()
{
infostream << "EmergeParams: destroying " << this << std::endl;
@ -131,6 +63,7 @@ EmergeParams::EmergeParams(EmergeManager *parent, const BiomeGen *biomegen,
enable_mapgen_debug_info(parent->enable_mapgen_debug_info),
gen_notify_on(parent->gen_notify_on),
gen_notify_on_deco_ids(&parent->gen_notify_on_deco_ids),
gen_notify_on_custom(&parent->gen_notify_on_custom),
biomemgr(biomemgr->clone()), oremgr(oremgr->clone()),
decomgr(decomgr->clone()), schemmgr(schemmgr->clone())
{
@ -518,9 +451,10 @@ EmergeThread::EmergeThread(Server *server, int ethreadid) :
enable_mapgen_debug_info(false),
id(ethreadid),
m_server(server),
m_map(NULL),
m_emerge(NULL),
m_mapgen(NULL)
m_map(nullptr),
m_emerge(nullptr),
m_mapgen(nullptr),
m_trans_liquid(nullptr)
{
m_name = "Emerge-" + itos(ethreadid);
}
@ -641,13 +575,13 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
// Ignore map edit events, they will not need to be sent
// to anybody because the block hasn't been sent to anybody
// to anyone because the block hasn't been sent yet.
MapEditEventAreaIgnorer ign(
&m_server->m_ignore_map_edit_events_area,
VoxelArea(minp, maxp));
/*
Run Lua on_generated callbacks
Run Lua on_generated callbacks in the server environment
*/
try {
m_server->getScriptIface()->environment_OnGenerated(
@ -674,6 +608,36 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
}
bool EmergeThread::initScripting()
{
m_script = std::make_unique<EmergeScripting>(this);
try {
m_script->loadMod(Server::getBuiltinLuaPath() + DIR_DELIM + "init.lua",
BUILTIN_MOD_NAME);
m_script->checkSetByBuiltin();
} catch (const ModError &e) {
errorstream << "Execution of mapgen base environment failed." << std::endl;
m_server->setAsyncFatalError(e.what());
return false;
}
const auto &list = m_server->m_mapgen_init_files;
try {
for (auto &it : list)
m_script->loadMod(it.second, it.first);
m_script->on_mods_loaded();
} catch (const ModError &e) {
errorstream << "Failed to load mod script inside mapgen environment." << std::endl;
m_server->setAsyncFatalError(e.what());
return false;
}
return true;
}
void *EmergeThread::run()
{
BEGIN_DEBUG_EXCEPTION_HANDLER
@ -686,6 +650,11 @@ void *EmergeThread::run()
m_mapgen = m_emerge->m_mapgens[id];
enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
if (!initScripting()) {
m_script.reset();
stop(); // do not enter main loop
}
try {
while (!stopRequested()) {
BlockEmergeData bedata;
@ -706,6 +675,9 @@ void *EmergeThread::run()
action = getBlockOrStartGen(pos, allow_gen, &block, &bmdata);
if (action == EMERGE_GENERATED) {
bool error = false;
m_trans_liquid = &bmdata.transforming_liquid;
{
ScopeProfiler sp(g_profiler,
"EmergeThread: Mapgen::makeChunk", SPT_AVG);
@ -713,9 +685,24 @@ void *EmergeThread::run()
m_mapgen->makeChunk(&bmdata);
}
block = finishGen(pos, &bmdata, &modified_blocks);
if (!block)
{
ScopeProfiler sp(g_profiler,
"EmergeThread: Lua on_generated", SPT_AVG);
try {
m_script->on_generated(&bmdata);
} catch (const LuaError &e) {
m_server->setAsyncFatalError(e);
error = true;
}
}
if (!error)
block = finishGen(pos, &bmdata, &modified_blocks);
if (!block || error)
action = EMERGE_ERRORED;
m_trans_liquid = nullptr;
}
runCompletionCallbacks(pos, action, bedata.callbacks);
@ -752,6 +739,13 @@ void *EmergeThread::run()
m_server->setAsyncFatalError(err.str());
}
try {
if (m_script)
m_script->on_shutdown();
} catch (const ModError &e) {
m_server->setAsyncFatalError(e.what());
}
cancelPendingItems();
END_DEBUG_EXCEPTION_HANDLER

@ -107,6 +107,7 @@ public:
u32 gen_notify_on;
const std::set<u32> *gen_notify_on_deco_ids; // shared
const std::set<std::string> *gen_notify_on_custom; // shared
BiomeGen *biomegen;
BiomeManager *biomemgr;
@ -114,6 +115,11 @@ public:
DecorationManager *decomgr;
SchematicManager *schemmgr;
inline GenerateNotifier createNotifier() const {
return GenerateNotifier(gen_notify_on, gen_notify_on_deco_ids,
gen_notify_on_custom);
}
private:
EmergeParams(EmergeManager *parent, const BiomeGen *biomegen,
const BiomeManager *biomemgr,
@ -134,6 +140,7 @@ public:
// Generation Notify
u32 gen_notify_on = 0;
std::set<u32> gen_notify_on_deco_ids;
std::set<std::string> gen_notify_on_custom;
// Parameters passed to mapgens owned by ServerMap
// TODO(hmmmm): Remove this after mapgen helper methods using them

115
src/emerge_internal.h Normal file

@ -0,0 +1,115 @@
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
/******************************************************************/
/* may only be included by emerge.cpp or emerge scripting related */
/******************************************************************/
#include "emerge.h"
#include <queue>
#include "util/thread.h"
#include "threading/event.h"
class Server;
class ServerMap;
class Mapgen;
class EmergeManager;
class EmergeScripting;
class EmergeThread : public Thread {
public:
bool enable_mapgen_debug_info;
int id;
EmergeThread(Server *server, int ethreadid);
~EmergeThread() = default;
void *run();
void signal();
// Requires queue mutex held
bool pushBlock(const v3s16 &pos);
void cancelPendingItems();
EmergeManager *getEmergeManager() { return m_emerge; }
Mapgen *getMapgen() { return m_mapgen; }
protected:
void runCompletionCallbacks(
const v3s16 &pos, EmergeAction action,
const EmergeCallbackList &callbacks);
private:
Server *m_server;
ServerMap *m_map;
EmergeManager *m_emerge;
Mapgen *m_mapgen;
std::unique_ptr<EmergeScripting> m_script;
// read from scripting:
UniqueQueue<v3s16> *m_trans_liquid; //< non-null only when generating a mapblock
Event m_queue_event;
std::queue<v3s16> m_block_queue;
bool initScripting();
bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata);
EmergeAction getBlockOrStartGen(
const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata,
std::map<v3s16, MapBlock *> *modified_blocks);
friend class EmergeManager;
friend class EmergeScripting;
friend class ModApiMapgen;
};
// Scoped helper to set Server::m_ignore_map_edit_events_area
class MapEditEventAreaIgnorer
{
public:
MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
m_ignorevariable(ignorevariable)
{
if (m_ignorevariable->getVolume() == 0)
*m_ignorevariable = a;
else
m_ignorevariable = nullptr;
}
~MapEditEventAreaIgnorer()
{
if (m_ignorevariable) {
assert(m_ignorevariable->getVolume() != 0);
*m_ignorevariable = VoxelArea();
}
}
private:
VoxelArea *m_ignorevariable;
};

@ -70,6 +70,7 @@ FlagDesc flagdesc_gennotify[] = {
{"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
{"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
{"decoration", 1 << GENNOTIFY_DECORATION},
{"custom", 1 << GENNOTIFY_CUSTOM},
{NULL, 0}
};
@ -108,7 +109,7 @@ static_assert(
////
Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeParams *emerge) :
gennotify(emerge->gen_notify_on, emerge->gen_notify_on_deco_ids)
gennotify(emerge->createNotifier())
{
id = mapgenid;
water_level = params->water_level;
@ -980,39 +981,67 @@ void MapgenBasic::generateDungeons(s16 max_stone_y)
////
GenerateNotifier::GenerateNotifier(u32 notify_on,
const std::set<u32> *notify_on_deco_ids)
const std::set<u32> *notify_on_deco_ids,
const std::set<std::string> *notify_on_custom)
{
m_notify_on = notify_on;
m_notify_on_deco_ids = notify_on_deco_ids;
m_notify_on_custom = notify_on_custom;
}
bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos)
{
if (!(m_notify_on & (1 << type)))
return false;
if (type == GENNOTIFY_DECORATION &&
m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->cend())
assert(type != GENNOTIFY_DECORATION && type != GENNOTIFY_CUSTOM);
if (!shouldNotifyOn(type))
return false;
GenNotifyEvent gne;
gne.type = type;
gne.pos = pos;
gne.id = id;
m_notify_events.push_back(gne);
m_notify_events.emplace_back(std::move(gne));
return true;
}
bool GenerateNotifier::addDecorationEvent(v3s16 pos, u32 id)
{
if (!shouldNotifyOn(GENNOTIFY_DECORATION))
return false;
// check if data relating to this decoration was requested
assert(m_notify_on_deco_ids);
if (m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->cend())
return false;
GenNotifyEvent gne;
gne.type = GENNOTIFY_DECORATION;
gne.pos = pos;
gne.id = id;
m_notify_events.emplace_back(std::move(gne));
return true;
}
bool GenerateNotifier::setCustom(const std::string &key, const std::string &value)
{
if (!shouldNotifyOn(GENNOTIFY_CUSTOM))
return false;
// check if this key was requested to be saved
assert(m_notify_on_custom);
if (m_notify_on_custom->count(key) == 0)
return false;
m_notify_custom[key] = value;
return true;
}
void GenerateNotifier::getEvents(
std::map<std::string, std::vector<v3s16> > &event_map)
std::map<std::string, std::vector<v3s16>> &event_map) const
{
std::list<GenNotifyEvent>::iterator it;
for (auto &gn : m_notify_events) {
assert(gn.type != GENNOTIFY_CUSTOM); // never stored in this list
for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
GenNotifyEvent &gn = *it;
std::string name = (gn.type == GENNOTIFY_DECORATION) ?
"decoration#"+ itos(gn.id) :
flagdesc_gennotify[gn.type].name;
@ -1025,6 +1054,7 @@ void GenerateNotifier::getEvents(
void GenerateNotifier::clearEvents()
{
m_notify_events.clear();
m_notify_custom.clear();
}

@ -76,29 +76,41 @@ enum GenNotifyType {
GENNOTIFY_LARGECAVE_BEGIN,
GENNOTIFY_LARGECAVE_END,
GENNOTIFY_DECORATION,
GENNOTIFY_CUSTOM, // user-defined data
NUM_GENNOTIFY_TYPES
};
struct GenNotifyEvent {
GenNotifyType type;
v3s16 pos;
u32 id;
};
class GenerateNotifier {
public:
struct GenNotifyEvent {
GenNotifyType type;
v3s16 pos;
u32 id; // for GENNOTIFY_DECORATION
};
// Use only for temporary Mapgen objects with no map generation!
GenerateNotifier() = default;
GenerateNotifier(u32 notify_on, const std::set<u32> *notify_on_deco_ids);
// normal constructor
GenerateNotifier(u32 notify_on, const std::set<u32> *notify_on_deco_ids,
const std::set<std::string> *notify_on_custom);
bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map);
bool addEvent(GenNotifyType type, v3s16 pos);
bool addDecorationEvent(v3s16 pos, u32 deco_id);
bool setCustom(const std::string &key, const std::string &value);
void getEvents(std::map<std::string, std::vector<v3s16>> &map) const;
const StringMap &getCustomData() const { return m_notify_custom; }
void clearEvents();
private:
u32 m_notify_on = 0;
const std::set<u32> *m_notify_on_deco_ids = nullptr;
const std::set<std::string> *m_notify_on_custom = nullptr;
std::list<GenNotifyEvent> m_notify_events;
StringMap m_notify_custom;
inline bool shouldNotifyOn(GenNotifyType type) const {
return m_notify_on & (1 << type);
}
};
// Order must match the order of 'static MapgenDesc g_reg_mapgens[]' in mapgen.cpp

@ -236,8 +236,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
v3s16 pos(x, y, z);
if (generate(mg->vm, &ps, pos, false))
mg->gennotify.addEvent(
GENNOTIFY_DECORATION, pos, index);
mg->gennotify.addDecorationEvent(pos, index);
}
}
@ -249,8 +248,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
v3s16 pos(x, y, z);
if (generate(mg->vm, &ps, pos, true))
mg->gennotify.addEvent(
GENNOTIFY_DECORATION, pos, index);
mg->gennotify.addDecorationEvent(pos, index);
}
}
} else { // Heightmap decorations
@ -273,7 +271,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
v3s16 pos(x, y, z);
if (generate(mg->vm, &ps, pos, false))
mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, index);
mg->gennotify.addDecorationEvent(pos, index);
}
}
}

@ -5,6 +5,7 @@ add_subdirectory(lua_api)
# Used by server and client
set(common_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_server.cpp
${CMAKE_CURRENT_SOURCE_DIR}/scripting_emerge.cpp
${common_SCRIPT_COMMON_SRCS}
${common_SCRIPT_CPP_API_SRCS}
${common_SCRIPT_LUA_API_SRCS}

@ -5,6 +5,7 @@ set(common_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_inventory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_item.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_mapgen.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_modchannels.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp

@ -61,13 +61,15 @@ enum class ScriptingType: u8 {
Async, // either mainmenu (client) or ingame (server)
Client,
MainMenu,
Server
Server,
Emerge
};
class Server;
#ifndef SERVER
class Client;
#endif
class EmergeThread;
class IGameDef;
class Environment;
class GUIEngine;
@ -158,6 +160,9 @@ protected:
void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; }
#endif
EmergeThread* getEmergeThread() { return m_emerge; }
void setEmergeThread(EmergeThread *emerge) { m_emerge = emerge; }
void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj);
void pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeReason& reason);
@ -180,5 +185,7 @@ private:
#ifndef SERVER
GUIEngine *m_guiengine = nullptr;
#endif
EmergeThread *m_emerge = nullptr;
ScriptingType m_type;
};

@ -0,0 +1,76 @@
/*
Minetest
Copyright (C) 2022 sfan5 <sfan5@live.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "cpp_api/s_mapgen.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "lua_api/l_vmanip.h"
#include "emerge.h"
void ScriptApiMapgen::on_mods_loaded()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_mods_loaded");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiMapgen::on_shutdown()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiMapgen::on_generated(BlockMakeData *bmdata)
{
SCRIPTAPI_PRECHECKHEADER
v3s16 minp = bmdata->blockpos_min * MAP_BLOCKSIZE;
v3s16 maxp = bmdata->blockpos_max * MAP_BLOCKSIZE +
v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
LuaVoxelManip::create(L, bmdata->vmanip, true);
const int vmanip = lua_gettop(L);
// Store vmanip globally (used by helpers)
lua_getglobal(L, "core");
lua_pushvalue(L, vmanip);
lua_setfield(L, -2, "vmanip");
// Call callbacks
lua_getfield(L, -1, "registered_on_generateds");
lua_pushvalue(L, vmanip);
push_v3s16(L, minp);
push_v3s16(L, maxp);
lua_pushnumber(L, bmdata->seed);
runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
lua_pop(L, 1); // return val
// Unset core.vmanip again
lua_pushnil(L);
lua_setfield(L, -2, "vmanip");
}

@ -0,0 +1,40 @@
/*
Minetest
Copyright (C) 2022 sfan5 <sfan5@live.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "cpp_api/s_base.h"
struct BlockMakeData;
/*
* Note that this is the class defining the functions called inside the emerge
* Lua state, not the server one.
*/
class ScriptApiMapgen : virtual public ScriptApiBase
{
public:
void on_mods_loaded();
void on_shutdown();
// Called after generating a piece of map before writing it to the map
void on_generated(BlockMakeData *bmdata);
};

@ -75,6 +75,11 @@ GUIEngine *ModApiBase::getGuiEngine(lua_State *L)
}
#endif
EmergeThread *ModApiBase::getEmergeThread(lua_State *L)
{
return getScriptApiBase(L)->getEmergeThread();
}
std::string ModApiBase::getCurrentModPath(lua_State *L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);

@ -34,7 +34,7 @@ extern "C" {
class Client;
class GUIEngine;
#endif
class EmergeThread;
class ScriptApiBase;
class Server;
class Environment;
@ -49,6 +49,7 @@ public:
static Client* getClient(lua_State *L);
static GUIEngine* getGuiEngine(lua_State *L);
#endif // !SERVER
static EmergeThread* getEmergeThread(lua_State *L);
static IGameDef* getGameDef(lua_State *L);

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "daynightratio.h"
#include "util/pointedthing.h"
#include "mapgen/treegen.h"
#include "emerge.h"
#include "emerge_internal.h"
#include "pathfinder.h"
#include "face_position_cache.h"
#include "remoteplayer.h"
@ -241,7 +241,7 @@ void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
delete state;
}
// Exported functions
/* Exported functions */
// set_node(pos, node)
// pos = {x=num, y=num, z=num}
@ -1538,3 +1538,189 @@ void ModApiEnv::InitializeClient(lua_State *L, int top)
API_FCT(line_of_sight);
API_FCT(raycast);
}
#define GET_VM_PTR \
MMVManip *vm = getVManip(L); \
if (!vm) \
return 0
// get_node_max_level(pos)
int ModApiEnvVM::l_get_node_max_level(lua_State *L)
{
GET_VM_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = vm->getNodeNoExNoEmerge(pos);
lua_pushnumber(L, n.getMaxLevel(getGameDef(L)->ndef()));
return 1;
}
// get_node_level(pos)
int ModApiEnvVM::l_get_node_level(lua_State *L)
{
GET_VM_PTR;
v3s16 pos = read_v3s16(L, 1);
MapNode n = vm->getNodeNoExNoEmerge(pos);
lua_pushnumber(L, n.getLevel(getGameDef(L)->ndef()));
return 1;
}
// set_node_level(pos, level)
int ModApiEnvVM::l_set_node_level(lua_State *L)
{
GET_VM_PTR;
v3s16 pos = read_v3s16(L, 1);
u8 level = 1;
if (lua_isnumber(L, 2))
level = lua_tonumber(L, 2);
MapNode n = vm->getNodeNoExNoEmerge(pos);
lua_pushnumber(L, n.setLevel(getGameDef(L)->ndef(), level));
vm->setNodeNoEmerge(pos, n);
return 1;
}
// add_node_level(pos, level)
int ModApiEnvVM::l_add_node_level(lua_State *L)
{
GET_VM_PTR;
v3s16 pos = read_v3s16(L, 1);
u8 level = 1;
if (lua_isnumber(L, 2))
level = lua_tonumber(L, 2);
MapNode n = vm->getNodeNoExNoEmerge(pos);
lua_pushnumber(L, n.addLevel(getGameDef(L)->ndef(), level));
vm->setNodeNoEmerge(pos, n);
return 1;
}
// find_node_near(pos, radius, nodenames, [search_center])
int ModApiEnvVM::l_find_node_near(lua_State *L)
{
GET_VM_PTR;
const NodeDefManager *ndef = getGameDef(L)->ndef();
v3s16 pos = read_v3s16(L, 1);
int radius = luaL_checkinteger(L, 2);
std::vector<content_t> filter;
collectNodeIds(L, 3, ndef, filter);
int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
auto getNode = [&vm] (v3s16 p) -> MapNode {
return vm->getNodeNoExNoEmerge(p);
};
return findNodeNear(L, pos, radius, filter, start_radius, getNode);
}
// find_nodes_in_area(minp, maxp, nodenames, [grouped])
int ModApiEnvVM::l_find_nodes_in_area(lua_State *L)
{
GET_VM_PTR;
const NodeDefManager *ndef = getGameDef(L)->ndef();
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
sortBoxVerticies(minp, maxp);
checkArea(minp, maxp);
// avoid the loop going out-of-bounds
{
VoxelArea cropped = VoxelArea(minp, maxp).intersect(vm->m_area);
minp = cropped.MinEdge;
maxp = cropped.MaxEdge;
}
std::vector<content_t> filter;
collectNodeIds(L, 3, ndef, filter);
bool grouped = lua_isboolean(L, 4) && readParam<bool>(L, 4);
auto iterate = [&] (auto callback) {
for (s16 z = minp.Z; z <= maxp.Z; z++)
for (s16 y = minp.Y; y <= maxp.Y; y++) {
u32 vi = vm->m_area.index(minp.X, y, z);
for (s16 x = minp.X; x <= maxp.X; x++) {
v3s16 pos(x, y, z);
MapNode n = vm->m_data[vi];
if (!callback(pos, n))
return;
++vi;
}
}
};
return findNodesInArea(L, ndef, filter, grouped, iterate);
}
// find_nodes_in_area_under_air(minp, maxp, nodenames)
int ModApiEnvVM::l_find_nodes_in_area_under_air(lua_State *L)
{
GET_VM_PTR;
const NodeDefManager *ndef = getGameDef(L)->ndef();
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
sortBoxVerticies(minp, maxp);
checkArea(minp, maxp);
std::vector<content_t> filter;
collectNodeIds(L, 3, ndef, filter);
auto getNode = [&vm] (v3s16 p) -> MapNode {
return vm->getNodeNoExNoEmerge(p);
};
return findNodesInAreaUnderAir(L, minp, maxp, filter, getNode);
}
// spawn_tree(pos, treedef)
int ModApiEnvVM::l_spawn_tree(lua_State *L)
{
GET_VM_PTR;
const NodeDefManager *ndef = getGameDef(L)->ndef();
v3s16 p0 = read_v3s16(L, 1);
treegen::TreeDef tree_def;
if (!read_tree_def(L, 2, ndef, tree_def))
return 0;
treegen::error e;
if ((e = treegen::make_ltree(*vm, p0, ndef, tree_def)) != treegen::SUCCESS) {
if (e == treegen::UNBALANCED_BRACKETS) {
throw LuaError("spawn_tree(): closing ']' has no matching opening bracket");
} else {
throw LuaError("spawn_tree(): unknown error");
}
}
lua_pushboolean(L, true);
return 1;
}
MMVManip *ModApiEnvVM::getVManip(lua_State *L)
{
auto emerge = getEmergeThread(L);
if (emerge)
return emerge->getMapgen()->vm;
return nullptr;
}
void ModApiEnvVM::InitializeEmerge(lua_State *L, int top)
{
// other, more trivial functions are in builtin/emerge/env.lua
API_FCT(get_node_max_level);
API_FCT(get_node_level);
API_FCT(set_node_level);
API_FCT(add_node_level);
API_FCT(find_node_near);
API_FCT(find_nodes_in_area);
API_FCT(find_nodes_in_area_under_air);
API_FCT(spawn_tree);
}
#undef GET_VM_PTR

@ -243,6 +243,44 @@ public:
static void InitializeClient(lua_State *L, int top);
};
/*
* Duplicates of certain env APIs that operate not on the global
* map but on a VoxelManipulator. This is for emerge scripting.
*/
class ModApiEnvVM : public ModApiEnvBase {
private:
// get_node_max_level(pos)
static int l_get_node_max_level(lua_State *L);
// get_node_level(pos)
static int l_get_node_level(lua_State *L);
// set_node_level(pos)
static int l_set_node_level(lua_State *L);
// add_node_level(pos)
static int l_add_node_level(lua_State *L);
// find_node_near(pos, radius, nodenames, [search_center])
static int l_find_node_near(lua_State *L);
// find_nodes_in_area(minp, maxp, nodenames, [grouped])
static int l_find_nodes_in_area(lua_State *L);
// find_surface_nodes_in_area(minp, maxp, nodenames)
static int l_find_nodes_in_area_under_air(lua_State *L);
// spawn_tree(pos, treedef)
static int l_spawn_tree(lua_State *L);
// Helper: get the vmanip we're operating on
static MMVManip *getVManip(lua_State *L);
public:
static void InitializeEmerge(lua_State *L, int top);
};
class LuaABM : public ActiveBlockModifier {
private:
int m_id;

@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#include "server.h"
#include "environment.h"
#include "emerge.h"
#include "emerge_internal.h"
#include "mapgen/mg_biome.h"
#include "mapgen/mg_ore.h"
#include "mapgen/mg_decoration.h"
@ -482,7 +482,7 @@ int ModApiMapgen::l_get_biome_id(lua_State *L)
const char *biome_str = luaL_checkstring(L, 1);
const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager();
const BiomeManager *bmgr = getEmergeManager(L)->getBiomeManager();
if (!bmgr)
return 0;
@ -504,7 +504,7 @@ int ModApiMapgen::l_get_biome_name(lua_State *L)
int biome_id = luaL_checkinteger(L, 1);
const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager();
const BiomeManager *bmgr = getEmergeManager(L)->getBiomeManager();
if (!bmgr)
return 0;
@ -523,8 +523,7 @@ int ModApiMapgen::l_get_heat(lua_State *L)
v3s16 pos = read_v3s16(L, 1);
const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();
const BiomeGen *biomegen = getBiomeGen(L);
if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL)
return 0;
@ -544,8 +543,7 @@ int ModApiMapgen::l_get_humidity(lua_State *L)
v3s16 pos = read_v3s16(L, 1);
const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();
const BiomeGen *biomegen = getBiomeGen(L);
if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL)
return 0;
@ -565,7 +563,7 @@ int ModApiMapgen::l_get_biome_data(lua_State *L)
v3s16 pos = read_v3s16(L, 1);
const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();
const BiomeGen *biomegen = getBiomeGen(L);
if (!biomegen)
return 0;
@ -607,8 +605,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
enum MapgenObject mgobj = (MapgenObject)mgobjint;
EmergeManager *emerge = getServer(L)->getEmergeManager();
Mapgen *mg = emerge->getCurrentMapgen();
Mapgen *mg = getMapgen(L);
if (!mg)
throw LuaError("Must only be called in a mapgen thread!");
@ -683,8 +680,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
return 1;
}
case MGOBJ_GENNOTIFY: {
std::map<std::string, std::vector<v3s16> >event_map;
std::map<std::string, std::vector<v3s16>> event_map;
mg->gennotify.getEvents(event_map);
lua_createtable(L, 0, event_map.size());
@ -699,6 +695,24 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
lua_setfield(L, -2, it->first.c_str());
}
// push user-defined data
auto &custom_map = mg->gennotify.getCustomData();
lua_createtable(L, 0, custom_map.size());
lua_getglobal(L, "core");
lua_getfield(L, -1, "deserialize");
lua_remove(L, -2); // remove 'core'
for (const auto &it : custom_map) {
lua_pushvalue(L, -1); // deserialize func
lua_pushlstring(L, it.second.c_str(), it.second.size());
lua_pushboolean(L, true);
lua_call(L, 2, 1);
lua_setfield(L, -3, it.first.c_str()); // put into table
}
lua_pop(L, 1); // remove func
lua_setfield(L, -2, "custom"); // put into top-level table
return 1;
}
}
@ -728,6 +742,31 @@ int ModApiMapgen::l_get_spawn_level(lua_State *L)
}
// get_seed([add])
int ModApiMapgen::l_get_seed(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
// This exists to
// 1. not duplicate the truncation logic from Mapgen::Mapgen() once more
// 2. because I don't trust myself to do it correctly in Lua
auto *emerge = getEmergeManager(L);
if (!emerge || !emerge->mgparams)
return 0;
int add = 0;
if (lua_isnumber(L, 1))
add = luaL_checkint(L, 1);
s32 seed = (s32)emerge->mgparams->seed;
seed += add;
lua_pushinteger(L, seed);
return 1;
}
int ModApiMapgen::l_get_mapgen_params(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -737,8 +776,8 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L)
std::string value;
MapSettingsManager *settingsmgr =
getServer(L)->getEmergeManager()->map_settings_mgr;
const MapSettingsManager *settingsmgr =
getEmergeManager(L)->map_settings_mgr;
lua_newtable(L);
@ -810,7 +849,8 @@ int ModApiMapgen::l_get_mapgen_edges(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
MapSettingsManager *settingsmgr = getServer(L)->getEmergeManager()->map_settings_mgr;
const MapSettingsManager *settingsmgr =
getEmergeManager(L)->map_settings_mgr;
// MapSettingsManager::makeMapgenParams cannot be used here because it would
// make mapgen settings immutable from then on. Mapgen settings should stay
@ -846,8 +886,8 @@ int ModApiMapgen::l_get_mapgen_setting(lua_State *L)
NO_MAP_LOCK_REQUIRED;
std::string value;
MapSettingsManager *settingsmgr =
getServer(L)->getEmergeManager()->map_settings_mgr;
const MapSettingsManager *settingsmgr =
getEmergeManager(L)->map_settings_mgr;
const char *name = luaL_checkstring(L, 1);
if (!settingsmgr->getMapSetting(name, &value))
@ -863,8 +903,8 @@ int ModApiMapgen::l_get_mapgen_setting_noiseparams(lua_State *L)
NO_MAP_LOCK_REQUIRED;
NoiseParams np;
MapSettingsManager *settingsmgr =
getServer(L)->getEmergeManager()->map_settings_mgr;
const MapSettingsManager *settingsmgr =
getEmergeManager(L)->map_settings_mgr;
const char *name = luaL_checkstring(L, 1);
if (!settingsmgr->getMapSettingNoiseParams(name, &np))
@ -964,7 +1004,7 @@ int ModApiMapgen::l_get_noiseparams(lua_State *L)
}
// set_gen_notify(flags, {deco_id_table})
// set_gen_notify(flags, {deco_ids}, {custom_ids})
int ModApiMapgen::l_set_gen_notify(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -986,11 +1026,26 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L)
}
}
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3)) {
emerge->gen_notify_on_custom.insert(readParam<std::string>(L, -1));
lua_pop(L, 1);
}
}
// Clear sets if relevant flag disabled
if ((emerge->gen_notify_on & (1 << GENNOTIFY_DECORATION)) == 0)
emerge->gen_notify_on_deco_ids.clear();
if ((emerge->gen_notify_on & (1 << GENNOTIFY_CUSTOM)) == 0)
emerge->gen_notify_on_custom.clear();
return 0;
}
// get_gen_notify()
// returns flagstring, {deco_ids}, {custom_ids})
int ModApiMapgen::l_get_gen_notify(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -999,13 +1054,43 @@ int ModApiMapgen::l_get_gen_notify(lua_State *L)
push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on,
emerge->gen_notify_on);
lua_newtable(L);
lua_createtable(L, emerge->gen_notify_on_deco_ids.size(), 0);
int i = 1;
for (u32 gen_notify_on_deco_id : emerge->gen_notify_on_deco_ids) {
lua_pushnumber(L, gen_notify_on_deco_id);
for (u32 id : emerge->gen_notify_on_deco_ids) {
lua_pushnumber(L, id);
lua_rawseti(L, -2, i++);
}
return 2;
lua_createtable(L, emerge->gen_notify_on_custom.size(), 0);
int j = 1;
for (const auto &id : emerge->gen_notify_on_custom) {
lua_pushstring(L, id.c_str());
lua_rawseti(L, -2, j++);
}
return 3;
}
// save_gen_notify(custom_id, data) [in emerge thread]
int ModApiMapgen::l_save_gen_notify(lua_State *L)
{
auto *emerge = getEmergeThread(L);
std::string key = readParam<std::string>(L, 1);
lua_getglobal(L, "core");
lua_getfield(L, -1, "serialize");
lua_remove(L, -2); // remove 'core'
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
std::string val = readParam<std::string>(L, -1);
lua_pop(L, 1);
bool set = emerge->getMapgen()->gennotify.setCustom(key, val);
lua_pushboolean(L, set);
return 1;
}
@ -1020,8 +1105,7 @@ int ModApiMapgen::l_get_decoration_id(lua_State *L)
return 0;
const DecorationManager *dmgr =
getServer(L)->getEmergeManager()->getDecorationManager();
getEmergeManager(L)->getDecorationManager();
if (!dmgr)
return 0;
@ -1452,20 +1536,26 @@ int ModApiMapgen::l_clear_registered_schematics(lua_State *L)
}
// generate_ores(vm, p1, p2, [ore_id])
// generate_ores(vm, p1, p2)
int ModApiMapgen::l_generate_ores(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
EmergeManager *emerge = getServer(L)->getEmergeManager();
auto *emerge = getEmergeManager(L);
if (!emerge || !emerge->mgparams)
return 0;
OreManager *oremgr;
if (auto mg = getMapgen(L))
oremgr = mg->m_emerge->oremgr;
else
oremgr = emerge->oremgr;
Mapgen mg;
// Intentionally truncates to s32, see Mapgen::Mapgen()
mg.seed = (s32)emerge->mgparams->seed;
mg.vm = checkObject<LuaVoxelManip>(L, 1)->vm;
mg.ndef = getServer(L)->getNodeDefManager();
mg.ndef = emerge->ndef;
v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
@ -1475,26 +1565,32 @@ int ModApiMapgen::l_generate_ores(lua_State *L)
u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);
emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax);
oremgr->placeAllOres(&mg, blockseed, pmin, pmax);
return 0;
}
// generate_decorations(vm, p1, p2, [deco_id])
// generate_decorations(vm, p1, p2)
int ModApiMapgen::l_generate_decorations(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
EmergeManager *emerge = getServer(L)->getEmergeManager();
auto *emerge = getEmergeManager(L);
if (!emerge || !emerge->mgparams)
return 0;
DecorationManager *decomgr;
if (auto mg = getMapgen(L))
decomgr = mg->m_emerge->decomgr;
else
decomgr = emerge->decomgr;
Mapgen mg;
// Intentionally truncates to s32, see Mapgen::Mapgen()
mg.seed = (s32)emerge->mgparams->seed;
mg.vm = checkObject<LuaVoxelManip>(L, 1)->vm;
mg.ndef = getServer(L)->getNodeDefManager();
mg.ndef = emerge->ndef;
v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
@ -1504,7 +1600,7 @@ int ModApiMapgen::l_generate_decorations(lua_State *L)
u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);
emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax);
decomgr->placeAllDecos(&mg, blockseed, pmin, pmax);
return 0;
}
@ -1629,7 +1725,11 @@ int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
SchematicManager *schemmgr;
if (auto mg = getMapgen(L))
schemmgr = mg->m_emerge->schemmgr;
else
schemmgr = getServer(L)->getEmergeManager()->schemmgr;
//// Read VoxelManip object
MMVManip *vm = checkObject<LuaVoxelManip>(L, 1)->vm;
@ -1677,7 +1777,7 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
const SchematicManager *schemmgr = getServer(L)->getEmergeManager()->getSchematicManager();
const SchematicManager *schemmgr = getEmergeManager(L)->getSchematicManager();
//// Read options
bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false);
@ -1727,8 +1827,7 @@ int ModApiMapgen::l_read_schematic(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
const SchematicManager *schemmgr =
getServer(L)->getEmergeManager()->getSchematicManager();
const SchematicManager *schemmgr = getEmergeManager(L)->getSchematicManager();
const NodeDefManager *ndef = getGameDef(L)->ndef();
//// Read options
@ -1806,17 +1905,22 @@ int ModApiMapgen::l_read_schematic(lua_State *L)
int ModApiMapgen::update_liquids(lua_State *L, MMVManip *vm)
{
GET_ENV_PTR;
UniqueQueue<v3s16> *trans_liquid;
if (auto emerge = getEmergeThread(L)) {
trans_liquid = emerge->m_trans_liquid;
} else {
GET_ENV_PTR;
trans_liquid = &env->getServerMap().m_transforming_liquid;
}
assert(trans_liquid);
ServerMap *map = &(env->getServerMap());
const NodeDefManager *ndef = getServer(L)->getNodeDefManager();
const NodeDefManager *ndef = getGameDef(L)->ndef();
Mapgen mg;
mg.vm = vm;
mg.ndef = ndef;
mg.updateLiquid(&map->m_transforming_liquid,
vm->m_area.MinEdge, vm->m_area.MaxEdge);
mg.updateLiquid(trans_liquid, vm->m_area.MinEdge, vm->m_area.MaxEdge);
return 0;
}
@ -1824,7 +1928,7 @@ int ModApiMapgen::calc_lighting(lua_State *L, MMVManip *vm,
v3s16 pmin, v3s16 pmax, bool propagate_shadow)
{
const NodeDefManager *ndef = getGameDef(L)->ndef();
EmergeManager *emerge = getServer(L)->getEmergeManager();
auto emerge = getEmergeManager(L);
assert(vm->m_area.contains(VoxelArea(pmin, pmax)));
@ -1850,6 +1954,35 @@ int ModApiMapgen::set_lighting(lua_State *L, MMVManip *vm,
return 0;
}
const EmergeManager *ModApiMapgen::getEmergeManager(lua_State *L)
{
auto emerge = getEmergeThread(L);
if (emerge)
return emerge->getEmergeManager();
return getServer(L)->getEmergeManager();
}
const BiomeGen *ModApiMapgen::getBiomeGen(lua_State *L)
{
// path 1: we're in the emerge environment
auto emerge = getEmergeThread(L);
if (emerge)
return emerge->getMapgen()->m_emerge->biomegen;
// path 2: we're in the server environment
auto manager = getServer(L)->getEmergeManager();
return manager->getBiomeGen();
}
Mapgen *ModApiMapgen::getMapgen(lua_State *L)
{
// path 1
auto emerge = getEmergeThread(L);
if (emerge)
return emerge->getMapgen();
// path 2
return getServer(L)->getEmergeManager()->getCurrentMapgen();
}
void ModApiMapgen::Initialize(lua_State *L, int top)
{
API_FCT(get_biome_id);
@ -1891,3 +2024,28 @@ void ModApiMapgen::Initialize(lua_State *L, int top)
API_FCT(serialize_schematic);
API_FCT(read_schematic);
}
void ModApiMapgen::InitializeEmerge(lua_State *L, int top)
{
API_FCT(get_biome_id);
API_FCT(get_biome_name);
API_FCT(get_heat);
API_FCT(get_humidity);
API_FCT(get_biome_data);
API_FCT(get_mapgen_object);
API_FCT(get_seed);
API_FCT(get_mapgen_params);
API_FCT(get_mapgen_edges);
API_FCT(get_mapgen_setting);
API_FCT(get_mapgen_setting_noiseparams);
API_FCT(get_noiseparams);
API_FCT(get_decoration_id);
API_FCT(save_gen_notify);
API_FCT(generate_ores);
API_FCT(generate_decorations);
API_FCT(place_schematic_on_vmanip);
API_FCT(serialize_schematic);
API_FCT(read_schematic);
}

@ -25,6 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include
class MMVManip;
class BiomeManager;
class BiomeGen;
class Mapgen;
class ModApiMapgen : public ModApiBase
{
@ -68,6 +71,9 @@ private:
// get_mapgen_edges([mapgen_limit[, chunksize]])
static int l_get_mapgen_edges(lua_State *L);
// get_seed([add])
static int l_get_seed(lua_State *L);
// get_mapgen_setting(name)
static int l_get_mapgen_setting(lua_State *L);
@ -86,12 +92,15 @@ private:
// get_noiseparam_defaults(name)
static int l_get_noiseparams(lua_State *L);
// set_gen_notify(flags, {deco_id_table})
// set_gen_notify(flags, {deco_ids}, {ud_ids})
static int l_set_gen_notify(lua_State *L);
// get_gen_notify()
static int l_get_gen_notify(lua_State *L);
// save_gen_notify(ud_id, data)
static int l_save_gen_notify(lua_State *L);
// get_decoration_id(decoration_name)
// returns the decoration ID as used in gennotify
static int l_get_decoration_id(lua_State *L);
@ -158,8 +167,18 @@ private:
static int set_lighting(lua_State *L, MMVManip *vm,
v3s16 pmin, v3s16 pmax, u8 light);
// Helpers
// get a read-only(!) EmergeManager
static const EmergeManager *getEmergeManager(lua_State *L);
// get the thread-local or global BiomeGen (still read-only)
static const BiomeGen *getBiomeGen(lua_State *L);
// get the thread-local mapgen
static Mapgen *getMapgen(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
static void InitializeEmerge(lua_State *L, int top);
static struct EnumString es_BiomeTerrainType[];
static struct EnumString es_DecorationType[];

@ -667,6 +667,25 @@ int ModApiServer::l_register_async_dofile(lua_State *L)
return 1;
}
// register_mapgen_script(path)
int ModApiServer::l_register_mapgen_script(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
std::string path = readParam<std::string>(L, 1);
CHECK_SECURE_PATH(L, path.c_str(), false);
// Find currently running mod name (only at init time)
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
if (!lua_isstring(L, -1))
return 0;
std::string modname = readParam<std::string>(L, -1);
getServer(L)->m_mapgen_init_files.emplace_back(modname, path);
lua_pushboolean(L, true);
return 1;
}
// serialize_roundtrip(value)
// Meant for unit testing the packer from Lua
int ModApiServer::l_serialize_roundtrip(lua_State *L)
@ -730,6 +749,8 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(do_async_callback);
API_FCT(register_async_dofile);
API_FCT(serialize_roundtrip);
API_FCT(register_mapgen_script);
}
void ModApiServer::InitializeAsync(lua_State *L, int top)

@ -118,6 +118,9 @@ private:
// register_async_dofile(path)
static int l_register_async_dofile(lua_State *L);
// register_mapgen_script(path)
static int l_register_mapgen_script(lua_State *L);
// serialize_roundtrip(obj)
static int l_serialize_roundtrip(lua_State *L);

@ -48,6 +48,9 @@ int LuaVoxelManip::l_read_from_map(lua_State *L)
if (vm->isOrphan())
return 0;
if (getEmergeThread(L))
throw LuaError("VoxelManip:read_from_map called in mapgen environment");
v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2));
v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3));
sortBoxVerticies(bp1, bp2);
@ -110,14 +113,18 @@ int LuaVoxelManip::l_set_data(lua_State *L)
int LuaVoxelManip::l_write_to_map(lua_State *L)
{
GET_ENV_PTR;
LuaVoxelManip *o = checkObject<LuaVoxelManip>(L, 1);
bool update_light = !lua_isboolean(L, 2) || readParam<bool>(L, 2);
if (o->vm->isOrphan())
return 0;
// This wouldn't work anyway as we have no env ptr, but it's still unsafe.
if (getEmergeThread(L))
throw LuaError("VoxelManip:write_to_map called in mapgen environment");
GET_ENV_PTR;
ServerMap *map = &(env->getServerMap());
std::map<v3s16, MapBlock*> modified_blocks;
@ -154,9 +161,8 @@ int LuaVoxelManip::l_set_node_at(lua_State *L)
v3s16 pos = check_v3s16(L, 2);
MapNode n = readnode(L, 3);
o->vm->setNodeNoEmerge(pos, n);
return 0;
lua_pushboolean(L, o->vm->setNodeNoEmerge(pos, n));
return 1;
}
int LuaVoxelManip::l_update_liquids(lua_State *L)
@ -193,8 +199,8 @@ int LuaVoxelManip::l_set_lighting(lua_State *L)
{
LuaVoxelManip *o = checkObject<LuaVoxelManip>(L, 1);
if (!o->is_mapgen_vm) {
warningstream << "VoxelManip:set_lighting called for a non-mapgen "
"VoxelManip object" << std::endl;
log_deprecated(L, "set_lighting called for a non-mapgen "
"VoxelManip object");
return 0;
}

@ -0,0 +1,93 @@
/*
Minetest
Copyright (C) 2022 sfan5 <sfan5@live.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "scripting_emerge.h"
#include "emerge_internal.h"
#include "server.h"
#include "settings.h"
#include "cpp_api/s_internal.h"
#include "common/c_packer.h"
#include "lua_api/l_areastore.h"
#include "lua_api/l_base.h"
#include "lua_api/l_craft.h"
#include "lua_api/l_env.h"
#include "lua_api/l_item.h"
#include "lua_api/l_itemstackmeta.h"
#include "lua_api/l_mapgen.h"
#include "lua_api/l_noise.h"
#include "lua_api/l_server.h"
#include "lua_api/l_util.h"
#include "lua_api/l_vmanip.h"
#include "lua_api/l_settings.h"
extern "C" {
#include <lualib.h>
}
EmergeScripting::EmergeScripting(EmergeThread *parent):
ScriptApiBase(ScriptingType::Emerge)
{
setGameDef(parent->m_server);
setEmergeThread(parent);
SCRIPTAPI_PRECHECKHEADER
if (g_settings->getBool("secure.enable_security"))
initializeSecurity();
lua_getglobal(L, "core");
int top = lua_gettop(L);
InitializeModApi(L, top);
auto *data = ModApiBase::getServer(L)->m_lua_globals_data.get();
assert(data);
script_unpack(L, data);
lua_setfield(L, top, "transferred_globals");
lua_pop(L, 1);
// Push builtin initialization type
lua_pushstring(L, "emerge");
lua_setglobal(L, "INIT");
}
void EmergeScripting::InitializeModApi(lua_State *L, int top)
{
// Register reference classes (userdata)
ItemStackMetaRef::Register(L);
LuaAreaStore::Register(L);
LuaItemStack::Register(L);
LuaPerlinNoise::Register(L);
LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L);
LuaPcgRandom::Register(L);
LuaSecureRandom::Register(L);
LuaVoxelManip::Register(L);
LuaSettings::Register(L);
// Initialize mod api modules
ModApiCraft::InitializeAsync(L, top);
ModApiEnvVM::InitializeEmerge(L, top);
ModApiItem::InitializeAsync(L, top);
ModApiMapgen::InitializeEmerge(L, top);
ModApiServer::InitializeAsync(L, top);
ModApiUtil::InitializeAsync(L, top);
// TODO ^ these should also be renamed to InitializeRO or such
}

@ -0,0 +1,37 @@
/*
Minetest
Copyright (C) 2022 sfan5 <sfan5@live.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "cpp_api/s_mapgen.h"
#include "cpp_api/s_security.h"
class EmergeThread;
class EmergeScripting:
virtual public ScriptApiBase,
public ScriptApiMapgen,
public ScriptApiSecurity
{
public:
EmergeScripting(EmergeThread *parent);
private:
void InitializeModApi(lua_State *L, int top);
};

@ -418,6 +418,8 @@ public:
// Lua files registered for init of async env, pair of modname + path
std::vector<std::pair<std::string, std::string>> m_async_init_files;
// Identical but for mapgen env
std::vector<std::pair<std::string, std::string>> m_mapgen_init_files;
// Data transferred into other Lua envs at init time
std::unique_ptr<PackedValue> m_lua_globals_data;