1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-15 18:57:08 +00:00

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

View file

@ -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

View file

@ -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
View 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;
};

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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}

View file

@ -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

View file

@ -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;
};

View file

@ -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");
}

View file

@ -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);
};

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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[];

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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
}

View file

@ -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);
};

View file

@ -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;