#include "common/c_converter.h"
#include "log.h"
#include "environment.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
#include "lua_api/l_env.h"
#include "server.h"
+#include "script/common/c_content.h"
+
void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
- u32 blockseed)
+ u32 blockseed)
{
SCRIPTAPI_PRECHECKHEADER
push_v3s16(L, minp);
push_v3s16(L, maxp);
lua_pushnumber(L, blockseed);
- script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiEnv::environment_Step(float dtime)
{
SCRIPTAPI_PRECHECKHEADER
- //infostream<<"scriptapi_environment_step"<<std::endl;
+ //infostream << "scriptapi_environment_step" << std::endl;
// Get core.registered_globalsteps
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_globalsteps");
// Call callbacks
lua_pushnumber(L, dtime);
- try {
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
- } catch (LuaError &e) {
- getServer()->setAsyncFatalError(e.what());
- }
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
-void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
+void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
{
SCRIPTAPI_PRECHECKHEADER
-
+
if (player == NULL)
return;
lua_getfield(L, -1, "registered_playerevents");
// Call callbacks
- objectrefGetOrCreate(player); // player
+ objectrefGetOrCreate(L, player); // player
lua_pushstring(L,type.c_str()); // event type
- try {
- script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST);
- } catch (LuaError &e) {
- getServer()->setAsyncFatalError(e.what());
- }
-}
-
-void ScriptApiEnv::environment_OnMapgenInit(MapgenParams *mgparams)
-{
- SCRIPTAPI_PRECHECKHEADER
-
- // Get core.registered_on_mapgen_inits
- lua_getglobal(L, "core");
- lua_getfield(L, -1, "registered_on_mapgen_inits");
-
- // Call callbacks
- lua_newtable(L);
-
- lua_pushstring(L, mgparams->mg_name.c_str());
- lua_setfield(L, -2, "mgname");
-
- lua_pushinteger(L, mgparams->seed);
- lua_setfield(L, -2, "seed");
-
- lua_pushinteger(L, mgparams->water_level);
- lua_setfield(L, -2, "water_level");
-
- std::string flagstr = writeFlagString(mgparams->flags,
- flagdesc_mapgen, (u32)-1);
- lua_pushstring(L, flagstr.c_str());
- lua_setfield(L, -2, "flags");
-
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
{
SCRIPTAPI_PRECHECKHEADER
- verbosestream<<"scriptapi_add_environment"<<std::endl;
+ verbosestream << "ScriptApiEnv: Environment initialized" << std::endl;
setEnv(env);
/*
- Add ActiveBlockModifiers to environment
+ Add {Loading,Active}BlockModifiers to environment
*/
// Get core.registered_abms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_abms");
- luaL_checktype(L, -1, LUA_TTABLE);
int registered_abms = lua_gettop(L);
- if(lua_istable(L, registered_abms)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- int id = lua_tonumber(L, -2);
- int current_abm = lua_gettop(L);
-
- std::set<std::string> trigger_contents;
- lua_getfield(L, current_abm, "nodenames");
- if(lua_istable(L, -1)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- luaL_checktype(L, -1, LUA_TSTRING);
- trigger_contents.insert(lua_tostring(L, -1));
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
- }
- } else if(lua_isstring(L, -1)){
- trigger_contents.insert(lua_tostring(L, -1));
+ if (!lua_istable(L, registered_abms)) {
+ lua_pop(L, 1);
+ throw LuaError("core.registered_abms was not a lua table, as expected.");
+ }
+ lua_pushnil(L);
+ while (lua_next(L, registered_abms)) {
+ // key at index -2 and value at index -1
+ int id = lua_tonumber(L, -2);
+ int current_abm = lua_gettop(L);
+
+ std::vector<std::string> trigger_contents;
+ lua_getfield(L, current_abm, "nodenames");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ trigger_contents.emplace_back(readParam<std::string>(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
- lua_pop(L, 1);
-
- std::set<std::string> required_neighbors;
- lua_getfield(L, current_abm, "neighbors");
- if(lua_istable(L, -1)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- luaL_checktype(L, -1, LUA_TSTRING);
- required_neighbors.insert(lua_tostring(L, -1));
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
- }
- } else if(lua_isstring(L, -1)){
- required_neighbors.insert(lua_tostring(L, -1));
+ } else if (lua_isstring(L, -1)) {
+ trigger_contents.emplace_back(readParam<std::string>(L, -1));
+ }
+ lua_pop(L, 1);
+
+ std::vector<std::string> required_neighbors;
+ lua_getfield(L, current_abm, "neighbors");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ required_neighbors.emplace_back(readParam<std::string>(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
- lua_pop(L, 1);
+ } else if (lua_isstring(L, -1)) {
+ required_neighbors.emplace_back(readParam<std::string>(L, -1));
+ }
+ lua_pop(L, 1);
+
+ float trigger_interval = 10.0;
+ getfloatfield(L, current_abm, "interval", trigger_interval);
+
+ int trigger_chance = 50;
+ getintfield(L, current_abm, "chance", trigger_chance);
- float trigger_interval = 10.0;
- getfloatfield(L, current_abm, "interval", trigger_interval);
+ bool simple_catch_up = true;
+ getboolfield(L, current_abm, "catch_up", simple_catch_up);
- int trigger_chance = 50;
- getintfield(L, current_abm, "chance", trigger_chance);
+ s16 min_y = INT16_MIN;
+ getintfield(L, current_abm, "min_y", min_y);
- LuaABM *abm = new LuaABM(L, id, trigger_contents,
- required_neighbors, trigger_interval, trigger_chance);
+ s16 max_y = INT16_MAX;
+ getintfield(L, current_abm, "max_y", max_y);
- env->addActiveBlockModifier(abm);
+ lua_getfield(L, current_abm, "action");
+ luaL_checktype(L, current_abm + 1, LUA_TFUNCTION);
+ lua_pop(L, 1);
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
+ LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
+ trigger_interval, trigger_chance, simple_catch_up, min_y, max_y);
+
+ env->addActiveBlockModifier(abm);
+
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+
+ // Get core.registered_lbms
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_lbms");
+ int registered_lbms = lua_gettop(L);
+
+ if (!lua_istable(L, registered_lbms)) {
+ lua_pop(L, 1);
+ throw LuaError("core.registered_lbms was not a lua table, as expected.");
+ }
+
+ lua_pushnil(L);
+ while (lua_next(L, registered_lbms)) {
+ // key at index -2 and value at index -1
+ int id = lua_tonumber(L, -2);
+ int current_lbm = lua_gettop(L);
+
+ std::set<std::string> trigger_contents;
+ lua_getfield(L, current_lbm, "nodenames");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ trigger_contents.insert(readParam<std::string>(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if (lua_isstring(L, -1)) {
+ trigger_contents.insert(readParam<std::string>(L, -1));
}
+ lua_pop(L, 1);
+
+ std::string name;
+ getstringfield(L, current_lbm, "name", name);
+
+ bool run_at_every_load = getboolfield_default(L, current_lbm,
+ "run_at_every_load", false);
+
+ lua_getfield(L, current_lbm, "action");
+ luaL_checktype(L, current_lbm + 1, LUA_TFUNCTION);
+ lua_pop(L, 1);
+
+ LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name,
+ run_at_every_load);
+
+ env->addLoadingBlockModifierDef(lbm);
+
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
lua_pop(L, 1);
}
+
+void ScriptApiEnv::on_emerge_area_completion(
+ v3s16 blockpos, int action, ScriptCallbackState *state)
+{
+ Server *server = getServer();
+
+ // This function should be executed with envlock held.
+ // The caller (LuaEmergeAreaCallback in src/script/lua_api/l_env.cpp)
+ // should have obtained the lock.
+ // Note that the order of these locks is important! Envlock must *ALWAYS*
+ // be acquired before attempting to acquire scriptlock, or else ServerThread
+ // will try to acquire scriptlock after it already owns envlock, thus
+ // deadlocking EmergeThread and ServerThread
+
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref);
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+
+ push_v3s16(L, blockpos);
+ lua_pushinteger(L, action);
+ lua_pushinteger(L, state->refcount);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref);
+
+ setOriginDirect(state->origin.c_str());
+
+ try {
+ PCALL_RES(lua_pcall(L, 4, 0, error_handler));
+ } catch (LuaError &e) {
+ // Note: don't throw here, we still need to run the cleanup code below
+ server->setAsyncFatalError(e);
+ }
+
+ lua_pop(L, 1); // Pop error handler
+
+ if (state->refcount == 0) {
+ luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref);
+ luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
+ }
+}
+
+void ScriptApiEnv::check_for_falling(v3s16 p)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "check_for_falling");
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ push_v3s16(L, p);
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+}
+
+void ScriptApiEnv::on_liquid_transformed(
+ const std::vector<std::pair<v3s16, MapNode>> &list)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_liquid_transformed
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_liquid_transformed");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_remove(L, -2);
+
+ // Skip converting list and calling hook if there are
+ // no registered callbacks.
+ if(lua_objlen(L, -1) < 1) return;
+
+ // Convert the list to a pos array and a node array for lua
+ int index = 1;
+ lua_createtable(L, list.size(), 0);
+ lua_createtable(L, list.size(), 0);
+ for(std::pair<v3s16, MapNode> p : list) {
+ lua_pushnumber(L, index);
+ push_v3s16(L, p.first);
+ lua_rawset(L, -4);
+ lua_pushnumber(L, index++);
+ pushnode(L, p.second);
+ lua_rawset(L, -3);
+ }
+
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiEnv::on_mapblocks_changed(const std::unordered_set<v3s16> &set)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_mapblocks_changed
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_mapblocks_changed");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_remove(L, -2);
+
+ // Convert the set to a set of position hashes
+ lua_createtable(L, 0, set.size());
+ for(const v3s16 &p : set) {
+ lua_pushnumber(L, hash_node_position(p));
+ lua_pushboolean(L, true);
+ lua_rawset(L, -3);
+ }
+ lua_pushinteger(L, set.size());
+
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+}
+
+bool ScriptApiEnv::has_on_mapblocks_changed()
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_mapblocks_changed
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_mapblocks_changed");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ return lua_objlen(L, -1) > 0;
+}